1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: adrbklib.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
4 
5 /* ========================================================================
6  * Copyright 2006-2009 University of Washington
7  * Copyright 2013-2021 Eduardo Chappa
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * ========================================================================
16  */
17 
18 #include "../pith/headers.h"
19 #include "../pith/adrbklib.h"
20 #include "../pith/abdlc.h"
21 #include "../pith/addrbook.h"
22 #include "../pith/addrstring.h"
23 #include "../pith/state.h"
24 #include "../pith/conf.h"
25 #include "../pith/status.h"
26 #include "../pith/remote.h"
27 #include "../pith/tempfile.h"
28 #include "../pith/bldaddr.h"
29 #include "../pith/signal.h"
30 #include "../pith/busy.h"
31 #include "../pith/util.h"
32 
33 
34 AddrScrState as;
35 
36 void (*pith_opt_save_and_restore)(int, SAVE_STATE_S *);
37 
38 
39 /*
40  * We don't want any end of line fixups to occur, so include "b" in DOS modes.
41  */
42 #if defined(DOS) || defined(OS2)
43 #define	ADRBK_NAME		"addrbook"
44 #else
45 #define	ADRBK_NAME		".addressbook"
46 #endif
47 
48 #define	OPEN_WRITE_MODE		(O_TRUNC|O_WRONLY|O_CREAT|O_BINARY)
49 
50 
51 #ifndef MAXPATH
52 #define MAXPATH 1000    /* Longest file path we can deal with */
53 #endif
54 
55 #define TABWIDTH 8
56 #define INDENTSTR "   "
57 #define INDENTXTRA " : "
58 #define INDENT 3  /* length of INDENTSTR */
59 
60 
61 #define SLOP 3
62 
63 static int writing; /* so we can give understandable error message */
64 
65 AdrBk               *edited_abook;
66 
67 static char empty[]       = "";
68 
69 jmp_buf jump_over_qsort;
70 
71 
72 /* internal prototypes */
73 int            copy_abook_to_tempfile(AdrBk *, char *, size_t);
74 char          *dir_containing(char *);
75 char          *get_next_abook_entry(FILE *, int);
76 void           strip_addr_string(char *, char **, char **);
77 void           adrbk_check_local_validity(AdrBk *, long);
78 int            write_single_abook_entry(AdrBk_Entry *, FILE *, int *, int *, int *, int *);
79 char	      *backcompat_encoding_for_abook(char *, size_t, char *, size_t, char *);
80 int            percent_abook_saved(void);
81 void           exp_del_nth(EXPANDED_S *, a_c_arg_t);
82 void           exp_add_nth(EXPANDED_S *, a_c_arg_t);
83 int            cmp_ae_by_full_lists_last(const qsort_t *,const qsort_t *);
84 int            cmp_cntr_by_full_lists_last(const qsort_t *, const qsort_t *);
85 int            cmp_ae_by_full(const qsort_t *, const qsort_t *);
86 int            cmp_cntr_by_full(const qsort_t *, const qsort_t *);
87 int            cmp_ae_by_nick_lists_last(const qsort_t *,const qsort_t *);
88 int            cmp_cntr_by_nick_lists_last(const qsort_t *, const qsort_t *);
89 int            cmp_ae_by_nick(const qsort_t *, const qsort_t *);
90 int            cmp_cntr_by_nick(const qsort_t *, const qsort_t *);
91 int            cmp_addr(const qsort_t *, const qsort_t *);
92 void           sort_addr_list(char **);
93 int            build_abook_datastruct(AdrBk *, char *, size_t);
94 AdrBk_Entry   *init_ae(AdrBk *, AdrBk_Entry *, char *);
95 adrbk_cntr_t   count_abook_entries_on_disk(AdrBk *, a_c_arg_t *);
96 AdrBk_Entry   *adrbk_get_delae(AdrBk *, a_c_arg_t);
97 void           free_ae_parts(AdrBk_Entry *);
98 void           add_entry_to_trie(AdrBk_Trie **, char *, a_c_arg_t);
99 int            build_abook_tries(AdrBk *, char *);
100 void           repair_abook_tries(AdrBk *);
101 adrbk_cntr_t   lookup_nickname_in_trie(AdrBk *, char *);
102 adrbk_cntr_t   lookup_address_in_trie(AdrBk *, char *);
103 adrbk_cntr_t   lookup_in_abook_trie(AdrBk_Trie *, char *);
104 void           free_abook_trie(AdrBk_Trie **);
105 adrbk_cntr_t   re_sort_particular_entry(AdrBk *, a_c_arg_t);
106 void           move_ab_entry(AdrBk *, a_c_arg_t, a_c_arg_t);
107 void           insert_ab_entry(AdrBk *, a_c_arg_t, AdrBk_Entry *, int);
108 void           delete_ab_entry(AdrBk *, a_c_arg_t, int);
109 void           defvalue_ae(AdrBk_Entry *);
110 
111 
112 /*
113  *  Open, read, and parse an address book.
114  *
115  * Args: pab      -- the PerAddrBook structure
116  *       homedir  -- the user's home directory if specified
117  *       warning  -- put "why failed" message to user here
118  *                   (provide space of at least 201 chars)
119  *
120  * If filename is NULL, the default will be used in the homedir
121  * passed in.  If homedir is NULL, the current dir will be used.
122  * If filename is not NULL and is an absolute path, just the filename
123  * will be used.  Otherwise, it will be used relative to the homedir, or
124  * to the current dir depending on whether or not homedir is NULL.
125  *
126  * Expected addressbook file format is:
127  *  <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
128  *
129  * The last two fields (\t<fcc>\t<comment>) are optional.
130  *
131  * Lines that start with SPACE are continuation lines.  Ends of lines are
132  * treated as if they were spaces.  The address field is either a single
133  * address or a list of comma-separated addresses inside parentheses.
134  *
135  * Fields missing from the end of an entry are considered blank.
136  *
137  * Commas in the address field will cause problems, as will tabs in any
138  * field.
139  *
140  * There may be some deleted entries in the addressbook that don't get
141  * used.  They look like regular entries except their nicknames start
142  * with the string "#DELETED-YY/MM/DD#".
143  */
144 AdrBk *
adrbk_open(PerAddrBook * pab,char * homedir,char * warning,size_t warninglen,int sort_rule)145 adrbk_open(PerAddrBook *pab, char *homedir, char *warning, size_t warninglen, int sort_rule)
146 {
147     char   path[MAXPATH], *filename;
148     AdrBk *ab;
149     int    abook_validity_was_minusone = 0;
150 
151 
152     filename = pab ? pab->filename : NULL;
153 
154     dprint((2, "- adrbk_open(%s) -\n", filename ? filename : ""));
155 
156     ab = (AdrBk *)fs_get(sizeof(AdrBk));
157     memset(ab, 0, sizeof(*ab));
158 
159     ab->orig_filename = filename ? cpystr(filename) : NULL;
160 
161     if(pab->type & REMOTE_VIA_IMAP){
162 	int try_cache;
163 
164 	ab->type           = Imap;
165 
166 	if(!ab->orig_filename || *(ab->orig_filename) != '{'){
167 	    dprint((1, "adrbk_open: remote: filename=%s\n",
168 		   ab->orig_filename ? ab->orig_filename : "NULL"));
169 	    goto bail_out;
170 	}
171 
172 	if(!(ab->rd = rd_new_remdata(RemImap, ab->orig_filename, REMOTE_ABOOK_SUBTYPE))){
173 	    dprint((1,
174 		   "adrbk_open: remote: new_remdata failed: %s\n",
175 		   ab->orig_filename ? ab->orig_filename : "NULL"));
176 	    goto bail_out;
177 	}
178 
179 	/* Transfer responsibility for the storage object */
180 	ab->rd->so = pab->so;
181 	pab->so = NULL;
182 
183 	try_cache = rd_read_metadata(ab->rd);
184 
185 	if(ab->rd->lf)
186 	  ab->filename = cpystr(ab->rd->lf);
187 
188 	/* Transfer responsibility for removal of temp file */
189 	if(ab->rd->flags & DEL_FILE){
190 	    ab->flags |= DEL_FILE;
191 	    ab->rd->flags &= ~DEL_FILE;
192 	}
193 
194 	if(pab->access == MaybeRorW){
195 	    if(ab->rd->read_status == 'R')
196 	      pab->access = ab->rd->access = ReadOnly;
197 	    else
198 	      pab->access = ab->rd->access = ReadWrite;
199 	}
200 	else if(pab->access == ReadOnly){
201 	    /*
202 	     * Pass on readonly-ness from being a global addrbook.
203 	     * This should cause us to open the remote folder readonly,
204 	     * avoiding error messages about readonly-ness.
205 	     */
206 	    ab->rd->access = ReadOnly;
207 	}
208 
209 	/*
210 	 * The plan is to fetch addrbook data and copy into local file.
211 	 * Then we open the local copy for reading. We use the IMAP STATUS
212 	 * command to tell us if we need to update from the remote addrbook.
213 	 *
214 	 * If access is NoExists, that probably means we had trouble
215 	 * opening the remote folder in the adrbk_access routine.
216 	 * In that case we'll use a cached copy if we have one.
217 	 */
218 	if(pab->access != NoExists){
219 
220 bootstrap_nocheck_policy:
221 	    if(try_cache && ps_global->remote_abook_validity == -1 &&
222 	       !abook_validity_was_minusone)
223 	      abook_validity_was_minusone++;
224 	    else{
225 		rd_check_remvalid(ab->rd, 1L);
226 		abook_validity_was_minusone = 0;
227 	    }
228 
229 	    /*
230 	     * If the cached info on this addrbook says it is readonly but
231 	     * it looks like it's been fixed now, change it to readwrite.
232 	     */
233 	    if(!(pab->type & GLOBAL) && ab->rd->read_status == 'R'){
234 		/*
235 		 * We go to this trouble since readonly addrbooks
236 		 * are likely a mistake. They are usually supposed to be
237 		 * readwrite. So we open it and check if it's been fixed.
238 		 */
239 		rd_check_readonly_access(ab->rd);
240 		if(ab->rd->read_status == 'W'){
241 		    pab->access = ab->rd->access = ReadWrite;
242 		    ab->rd->flags |= REM_OUTOFDATE;
243 		}
244 		else
245 		  pab->access = ab->rd->access = ReadOnly;
246 	    }
247 
248 	    if(ab->rd->flags & REM_OUTOFDATE){
249 		if(rd_update_local(ab->rd) != 0){
250 		    dprint((1,
251 			   "adrbk_open: remote: rd_update_local failed\n"));
252 		    /*
253 		     * Don't give up altogether. We still may be
254 		     * able to use a cached copy.
255 		     */
256 		}
257 		else{
258 		    dprint((7,
259 			   "%s: copied remote to local (%ld)\n",
260 			   ab->rd->rn ? ab->rd->rn : "?",
261 			   (long)ab->rd->last_use));
262 		}
263 	    }
264 
265 	    if(pab->access == ReadWrite)
266 	      ab->rd->flags |= DO_REMTRIM;
267 	}
268 
269 	/* If we couldn't get to remote folder, try using the cached copy */
270 	if(pab->access == NoExists || ab->rd->flags & REM_OUTOFDATE){
271 	    if(try_cache){
272 		pab->access = ab->rd->access = ReadOnly;
273 		ab->rd->flags |= USE_OLD_CACHE;
274 		q_status_message(SM_ORDER, 3, 4,
275 		 _("Can't contact remote address book server, using cached copy"));
276 		dprint((2,
277 	"Can't open remote addrbook %s, using local cached copy %s readonly\n",
278 		       ab->rd->rn ? ab->rd->rn : "?",
279 		       ab->rd->lf ? ab->rd->lf : "?"));
280 	    }
281 	    else
282 	      goto bail_out;
283 	}
284     }
285     else{
286 	ab->type = Local;
287 
288 	/*------------ figure out and save name of file to open ---------*/
289 	if(filename == NULL){
290 	    if(homedir != NULL){
291 		build_path(path, homedir, ADRBK_NAME, sizeof(path));
292 		ab->filename = cpystr(path);
293 	    }
294 	    else
295 	      ab->filename = cpystr(ADRBK_NAME);
296 	}
297 	else{
298 	    if(is_absolute_path(filename)){
299 		ab->filename = cpystr(filename);
300 	    }
301 	    else{
302 		if(homedir != NULL){
303 		    build_path(path, homedir, filename, sizeof(path));
304 		    ab->filename = cpystr(path);
305 		}
306 		else
307 		  ab->filename = cpystr(filename);
308 	    }
309 	}
310     }
311 
312     if(ab->filename && ab->filename[0]){
313 	char buf[MAXPATH];
314 
315 	strncpy(buf, ab->filename, sizeof(buf)-4);
316 	buf[sizeof(buf)-4] = '\0';
317 
318 	/*
319 	 * Our_filecopy is used in _WINDOWS to allow
320 	 * multiple pines to update the address book. The problem is
321 	 * that if a file is open it can't be deleted, so we need to keep
322 	 * the main filename closed most of the time.
323 	 * In Unix, our_filecopy just points to filename.
324 	 */
325 
326 #ifdef	_WINDOWS
327 	/*
328 	 * If we can't write in the same directory as filename is in, put
329 	 * the copies in /tmp instead.
330 	 */
331 	if(!(ab->our_filecopy = tempfile_in_same_dir(ab->filename, "a3", NULL)))
332 	  ab->our_filecopy = temp_nam(NULL, "a3");
333 #endif	/* _WINDOWS */
334 
335 	/*
336 	 * We don't need the copies on Unix because we can rename/delete
337 	 * open files. Turn the feature off by making the copies point to
338 	 * the originals.
339 	 */
340 	if(!ab->our_filecopy)
341 	  ab->our_filecopy = ab->filename;
342     }
343     else{
344 	dprint((1, "adrbk_open: ab->filename is NULL???\n"));
345 	goto bail_out;
346     }
347 
348 
349     /*
350      * We're going to make our own copy of the address book file so that
351      * we won't conflict with other instances of pine trying to change it.
352      * In particular, on Windows the address book file cannot be deleted
353      * or renamed into if it is open in another process.
354      */
355     ab->flags |= FILE_OUTOFDATE;
356     if(copy_abook_to_tempfile(ab, warning, warninglen) < 0){
357 	dprint((1, "adrbk_open: copy_file failed\n"));
358 	if(abook_validity_was_minusone){
359 	    /*
360 	     * The file copy failed when it shouldn't have. If the user has
361 	     * remote_abook_validity == -1 then we'll go back and try to
362 	     * do the validity check in case that can get us the file we
363 	     * need to copy. Without the validity check first time we won't
364 	     * contact the imap server.
365 	     */
366 	    dprint((1, "adrbk_open: trying to bootstrap\n"));
367 
368 	    if(ab->our_filecopy){
369 		if(ab->our_filecopy != ab->filename){
370 		    our_unlink(ab->our_filecopy);
371 		    fs_give((void **)&ab->our_filecopy);
372 		}
373 
374 		ab->our_filecopy = NULL;
375 	    }
376 
377 	    goto bootstrap_nocheck_policy;
378 	}
379 
380 	goto bail_out;
381     }
382 
383     if(!ab->fp)
384       goto bail_out;
385 
386     ab->sort_rule = sort_rule;
387     if(pab->access == ReadOnly)
388       ab->sort_rule = AB_SORT_RULE_NONE;
389 
390     if(ab){
391 	/* allocate header for expanded lists list */
392 	ab->exp      = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
393 	/* first real element is NULL */
394 	ab->exp->next = (EXPANDED_S *)NULL;
395 
396 	/* allocate header for checked entries list */
397 	ab->checks       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
398 	/* first real element is NULL */
399 	ab->checks->next = (EXPANDED_S *)NULL;
400 
401 	/* allocate header for selected entries list */
402 	ab->selects       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
403 	/* first real element is NULL */
404 	ab->selects->next = (EXPANDED_S *)NULL;
405 
406 	return(ab);
407     }
408 
409 bail_out:
410     dprint((2, "adrbk_open: bailing: filenames=%s %s %s fp=%s\n",
411 	   ab->orig_filename ? ab->orig_filename : "NULL",
412 	   ab->filename ? ab->filename : "NULL",
413 	   ab->our_filecopy ? ab->our_filecopy : "NULL",
414 	   ab->fp ? "open" : "NULL"));
415 
416     if(ab->rd){
417 	ab->rd->flags &= ~DO_REMTRIM;
418 	rd_close_remdata(&ab->rd);
419     }
420 
421     if(ab->fp)
422       (void)fclose(ab->fp);
423 
424     if(ab->orig_filename)
425       fs_give((void **) &ab->orig_filename);
426 
427     if(ab->our_filecopy && ab->our_filecopy != ab->filename){
428 	our_unlink(ab->our_filecopy);
429 	fs_give((void **) &ab->our_filecopy);
430     }
431 
432     if(ab->filename)
433       fs_give((void **) &ab->filename);
434 
435     if(pab->so){
436 	so_give(&(pab->so));
437 	pab->so = NULL;
438     }
439 
440     fs_give((void **) &ab);
441 
442     return NULL;
443 }
444 
445 
446 /*
447  * Copy the address book file to the temporary session copy. Also copy
448  * the hashfile. Any of these files which don't exist will be created.
449  *
450  * Returns  0 success
451  *         -1 failure
452  */
453 int
copy_abook_to_tempfile(AdrBk * ab,char * warning,size_t warninglen)454 copy_abook_to_tempfile(AdrBk *ab, char *warning, size_t warninglen)
455 {
456     int    got_it, fd, c,
457 	   ret = -1,
458 	   we_cancel = 0;
459     FILE  *fp_read = (FILE *)NULL,
460 	  *fp_write = (FILE *)NULL;
461     char  *lc;
462     time_t mtime;
463 
464 
465     dprint((3, "copy_file(%s) -\n",
466 	       (ab && ab->filename) ? ab->filename : ""));
467 
468     if(!ab || !ab->filename || !ab->filename[0])
469       goto get_out;
470 
471     if(!(ab->flags & FILE_OUTOFDATE))
472       return(0);
473 
474     /* open filename for reading */
475     fp_read = our_fopen(ab->filename, "rb");
476     if(fp_read == NULL){
477 
478 	/*
479 	 * filename probably doesn't exist so we try to create it
480 	 */
481 
482 	/* don't want to create in these cases, should already be there */
483 	if(ab->type == Imap){
484 	    if(warning){
485 		if(ab->type == Imap){
486 		    snprintf(warning, warninglen,
487 		    /* TRANSLATORS: A temporary file for the address book can't
488 		       be opened. */
489 			        _("Temp addrbook file can't be opened: %s"),
490 				ab->filename);
491 		    warning[warninglen-1] = '\0';
492 		}
493 		else{
494 		    strncpy(warning, _("Address book doesn't exist"), warninglen);
495 		    warning[warninglen-1] = '\0';
496 		}
497 	    }
498 
499 	    goto get_out;
500 	}
501 
502 	q_status_message1(SM_INFO, 0, 3,
503 	                  _("Address book %.200s doesn't exist, creating"),
504 	                  (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
505 	dprint((2, "Address book %s doesn't exist, creating\n",
506 	       ab->filename ? ab->filename : "?"));
507 
508 	/*
509 	 * Just use user's umask for permissions. Mode is "w" so the create
510 	 * will happen. We close it right after creating and open it in
511 	 * read mode again later.
512 	 */
513         fp_read = our_fopen(ab->filename, "wb");  /* create */
514         if(fp_read == NULL        ||
515 	   fclose(fp_read) == EOF ||
516 	   (fp_read = our_fopen(ab->filename, "rb")) == NULL){
517             /*--- Create failed, bail out ---*/
518 	    if(warning){
519 		strncpy(warning, error_description(errno), warninglen);
520 		warning[warninglen-1] = '\0';
521 	    }
522 
523 	    dprint((2, "create failed: %s\n",
524 		error_description(errno)));
525 
526 	    goto get_out;
527         }
528     }
529 
530     /* record new change date of addrbook file */
531     ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
532 
533     ab->last_local_valid_chk = get_adj_time();
534 
535 
536     /* now there is an ab->filename and we have it open for reading */
537 
538     got_it = 0;
539 
540     /* copy ab->filename to ab->our_filecopy, preserving mtime */
541     if(ab->filename != ab->our_filecopy){
542 	struct stat sbuf;
543 	struct utimbuf times;
544 	int valid_stat = 0;
545 
546 	dprint((7, "Before abook copies\n"));
547 	if((fd = our_open(ab->our_filecopy, OPEN_WRITE_MODE, 0600)) < 0)
548 	  goto get_out;
549 
550 	fp_write = fdopen(fd, "wb");
551 	rewind(fp_read);
552 	if(fstat(fileno(fp_read), &sbuf)){
553 	    q_status_message1(SM_INFO, 0, 3,
554 		 "Error: can't stat addrbook \"%.200s\"",
555 		 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
556 	    dprint((2, "Error: can't stat addrbook \"%s\"\n",
557 		   ab->filename ? ab->filename : "?"));
558 	}
559 	else{
560 	    valid_stat++;
561 	    times.actime  = sbuf.st_atime;
562 	    times.modtime = sbuf.st_mtime;
563 	}
564 
565 	while((c = getc(fp_read)) != EOF)
566 	  if(putc(c, fp_write) == EOF)
567 	    goto get_out;
568 
569 	(void)fclose(fp_write);
570 	fp_write = (FILE *)NULL;
571 	if(valid_stat && our_utime(ab->our_filecopy, &times)){
572 	    q_status_message1(SM_INFO, 0, 3,
573 		 "Error: can't set mtime for \"%.200s\"",
574 		 (lc=last_cmpnt(ab->filename)) ? lc : ab->our_filecopy);
575 	    dprint((2, "Error: can't set mtime for \"%s\"\n",
576 		   ab->our_filecopy ? ab->our_filecopy : "?"));
577 	}
578 
579 	(void)fclose(fp_read);
580 	fp_read = (FILE *)NULL;
581 	if(!(ab->fp = our_fopen(ab->our_filecopy, "rb")))
582 	  goto get_out;
583 
584 	dprint((7, "After abook file copy\n"));
585     }
586     else{  /* already open to the right file */
587 	ab->fp = fp_read;
588 	fp_read = (FILE *)NULL;
589     }
590 
591     /*
592      * Now we've copied filename to our_filecopy.
593      * Operate on the copy now. Ab->fp is open readonly on
594      * our_filecopy.
595      */
596 
597     got_it = 0;
598     mtime = get_adj_name_file_mtime(ab->our_filecopy);
599     we_cancel = busy_cue(NULL, NULL, 1);
600     if(build_abook_datastruct(ab, (warning && !*warning) ? warning : NULL, warninglen)){
601 	if(we_cancel)
602 	  cancel_busy_cue(-1);
603 
604 	dprint((2, "failed in build_abook_datastruct\n"));
605 	goto get_out;
606     }
607 
608     if(ab->arr
609        && build_abook_tries(ab, (warning && !*warning) ? warning : NULL)){
610 	if(we_cancel)
611 	  cancel_busy_cue(-1);
612 
613 	dprint((2, "failed in build_abook_tries\n"));
614 	goto get_out;
615     }
616 
617     if(we_cancel)
618       cancel_busy_cue(-1);
619 
620     ab->flags &= ~FILE_OUTOFDATE;  /* turn off out of date flag */
621     ret = 0;
622 
623 get_out:
624     if(fp_read)
625       (void)fclose(fp_read);
626 
627     if(fp_write)
628       (void)fclose(fp_write);
629 
630     if(ret < 0 && ab){
631 	if(ab->our_filecopy && ab->our_filecopy != ab->filename)
632 	  our_unlink(ab->our_filecopy);
633     }
634 
635     return(ret);
636 }
637 
638 
639 /*
640  * Returns an allocated copy of the directory which contains filename.
641  */
642 char *
dir_containing(char * filename)643 dir_containing(char *filename)
644 {
645     char  dir[MAXPATH+1];
646     char *dirp = NULL;
647 
648     if(filename){
649 	char *lc;
650 
651 	if((lc = last_cmpnt(filename)) != NULL){
652 	    int to_copy;
653 
654 	    to_copy = (lc - filename > 1) ? (lc - filename - 1) : 1;
655 	    strncpy(dir, filename, MIN(to_copy, sizeof(dir)-1));
656 	    dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
657 	}
658 	else{
659 	    dir[0] = '.';
660 	    dir[1] = '\0';
661 	}
662 
663 	dirp = cpystr(dir);
664     }
665 
666     return(dirp);
667 }
668 
669 
670 /*
671  * Checks whether or not the addrbook is sorted correctly according to
672  * the SortType.  Returns 1 if is sorted correctly, 0 otherwise.
673  */
674 int
adrbk_is_in_sort_order(AdrBk * ab,int be_quiet)675 adrbk_is_in_sort_order(AdrBk *ab, int be_quiet)
676 {
677     adrbk_cntr_t entry;
678     AdrBk_Entry *ae, *ae_prev;
679     int (*cmp_func)();
680     int we_cancel = 0;
681 
682     dprint((9, "- adrbk_is_in_sort_order -\n"));
683 
684     if(!ab)
685       return 0;
686 
687     if(ab->sort_rule == AB_SORT_RULE_NONE || ab->count < 2)
688       return 1;
689 
690     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
691 					    cmp_ae_by_full_lists_last :
692                (ab->sort_rule == AB_SORT_RULE_FULL) ?
693 					    cmp_ae_by_full :
694                (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
695 					    cmp_ae_by_nick_lists_last :
696             /* (ab->sort_rule == AB_SORT_RULE_NICK) */
697 					    cmp_ae_by_nick;
698 
699     ae_prev = adrbk_get_ae(ab, (a_c_arg_t) 0);
700 
701     if(!be_quiet)
702       we_cancel = busy_cue(NULL, NULL, 1);
703 
704     for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t) entry);
705 	ae != (AdrBk_Entry *)NULL;
706 	ae = adrbk_get_ae(ab, (a_c_arg_t) (++entry))){
707 
708 	    if((*cmp_func)((qsort_t *)&ae_prev, (qsort_t *)&ae) > 0){
709 		if(we_cancel)
710 		  cancel_busy_cue(-1);
711 
712 		dprint((9, "- adrbk_is_in_sort_order : no (entry %ld) -\n", (long) entry));
713 		return 0;
714 	    }
715 
716 	    ae_prev = ae;
717     }
718 
719     if(we_cancel)
720       cancel_busy_cue(-1);
721 
722     dprint((9, "- adrbk_is_in_sort_order : yes -\n"));
723 
724     return 1;
725 }
726 
727 
728 /*
729  * Look through the ondisk address book and count the number of entries.
730  *
731  * Args  ab     -- address book pointer
732  *      deleted -- pointer to location to return number of deleted entries
733  *      warning -- place to put warning
734  *
735  * Returns number of non-deleted entries.
736  */
737 adrbk_cntr_t
count_abook_entries_on_disk(AdrBk * ab,a_c_arg_t * deleted)738 count_abook_entries_on_disk(AdrBk *ab, a_c_arg_t *deleted)
739 {
740     FILE          *fp_in;
741     char          *nickname;
742     adrbk_cntr_t   count = 0;
743     adrbk_cntr_t   deleted_count = 0;
744     int		   rew = 1;
745     char           nickbuf[50];
746 
747     if(!ab || !ab->fp)
748       return -1;
749 
750     fp_in = ab->fp;
751 
752     while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
753 	rew = 0;
754 	strncpy(nickbuf, nickname, sizeof(nickbuf));
755 	nickbuf[sizeof(nickbuf)-1] = '\0';
756 	fs_give((void **) &nickname);
757 	if(strncmp(nickbuf, DELETED, DELETED_LEN) == 0
758 	   && isdigit((unsigned char)nickbuf[DELETED_LEN])
759 	   && isdigit((unsigned char)nickbuf[DELETED_LEN+1])
760 	   && nickbuf[DELETED_LEN+2] == '/'
761 	   && isdigit((unsigned char)nickbuf[DELETED_LEN+3])
762 	   && isdigit((unsigned char)nickbuf[DELETED_LEN+4])
763 	   && nickbuf[DELETED_LEN+5] == '/'
764 	   && isdigit((unsigned char)nickbuf[DELETED_LEN+6])
765 	   && isdigit((unsigned char)nickbuf[DELETED_LEN+7])
766 	   && nickbuf[DELETED_LEN+8] == '#'){
767 	    deleted_count++;
768 	    continue;
769 	}
770 
771 	count++;
772     }
773 
774     if(deleted)
775       *deleted = (a_c_arg_t) deleted_count;
776 
777     return(count);
778 }
779 
780 
781 /*
782  * Builds the data structures used with the address book which is
783  * simply an array of entries.
784  */
785 int
build_abook_datastruct(AdrBk * ab,char * warning,size_t warninglen)786 build_abook_datastruct(AdrBk *ab, char *warning, size_t warninglen)
787 {
788     FILE          *fp_in;
789     char          *nickname;
790     char          *lc;
791     a_c_arg_t      count, deleted;
792     adrbk_cntr_t   used = 0, delused = 0;
793     int            max_nick = 0,
794 		   max_full = 0, full_two = 0, full_three = 0,
795 		   max_fcc = 0, fcc_two = 0, fcc_three = 0,
796 		   max_addr = 0, addr_two = 0, addr_three = 0,
797 		   this_nick_width, this_full_width, this_addr_width,
798 		   this_fcc_width;
799     WIDTH_INFO_S  *widths;
800     int		   rew = 1, is_deleted;
801     AdrBk_Entry   *ae;
802 
803     dprint((9, "- build_abook_datastruct -\n"));
804 
805     if(!ab || !ab->fp)
806       return -1;
807 
808     errno = 0;
809 
810     fp_in = ab->fp;
811 
812     /*
813      * If we used a list instead of an array to store the entries we
814      * could avoid this pass through the file to count the entries, which
815      * we use to allocate the array.
816      *
817      * Since we use entry_nums a lot to access address book entries it is
818      * convenient to have an array for quick access, so we'll probably
819      * leave this for now.
820      */
821     count = count_abook_entries_on_disk(ab, &deleted);
822 
823     if(count < 0)
824       return -1;
825 
826     ab->count       = (adrbk_cntr_t) count;
827     ab->del_count   = (adrbk_cntr_t) deleted;
828 
829     if(count > 0){
830 	ab->arr = (AdrBk_Entry *) fs_get(count * sizeof(AdrBk_Entry));
831 	memset(ab->arr, 0, count * sizeof(AdrBk_Entry));
832     }
833 
834     if(deleted > 0){
835 	ab->del = (AdrBk_Entry *) fs_get(deleted * sizeof(AdrBk_Entry));
836 	memset(ab->del, 0, deleted * sizeof(AdrBk_Entry));
837     }
838 
839     while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
840 
841 
842 	ae = NULL;
843 	rew = 0;
844 	is_deleted = 0;
845 
846 	if(strncmp(nickname, DELETED, DELETED_LEN) == 0
847 	   && isdigit((unsigned char)nickname[DELETED_LEN])
848 	   && isdigit((unsigned char)nickname[DELETED_LEN+1])
849 	   && nickname[DELETED_LEN+2] == '/'
850 	   && isdigit((unsigned char)nickname[DELETED_LEN+3])
851 	   && isdigit((unsigned char)nickname[DELETED_LEN+4])
852 	   && nickname[DELETED_LEN+5] == '/'
853 	   && isdigit((unsigned char)nickname[DELETED_LEN+6])
854 	   && isdigit((unsigned char)nickname[DELETED_LEN+7])
855 	   && nickname[DELETED_LEN+8] == '#'){
856 	    is_deleted++;
857 	}
858 
859 	ALARM_BLIP();
860 	if(!is_deleted && (long) used > MAX_ADRBK_SIZE){
861 	    q_status_message2(SM_ORDER | SM_DING, 4, 5,
862 		    "Max addrbook size is %.200s, %.200s too large, giving up",
863 			    long2string(MAX_ADRBK_SIZE),
864 			    (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
865 	    dprint((1, "build_ondisk: used=%ld > %s\n",
866 		   (long) used, long2string(MAX_ADRBK_SIZE)));
867 	    goto io_err;
868 	}
869 
870 	if(is_deleted){
871 	    if(delused < ab->del_count)
872 	      ae = &ab->del[delused++];
873 	}
874 	else{
875 	    if(used < ab->count)
876 	      ae = &ab->arr[used++];
877 	}
878 
879 	if(ae)
880 	  init_ae(ab, ae, nickname);
881 
882 	fs_give((void **) &nickname);
883 
884 	if(!ae || is_deleted)
885 	  continue;
886 
887 	/*
888 	 * We're calculating the widths as we read in the data.
889 	 * We could just go with some default widths to save time.
890 	 */
891         this_nick_width = 0;
892 	this_full_width = 0;
893 	this_addr_width = 0;
894 	this_fcc_width = 0;
895 
896 	if(ae->nickname)
897 	  this_nick_width = (int) utf8_width(ae->nickname);
898 
899 	if(ae->fullname)
900 	  this_full_width = (int) utf8_width(ae->fullname);
901 
902 	if(ae->tag == Single){
903 	    if(ae->addr.addr)
904 	      this_addr_width = (int) utf8_width(ae->addr.addr);
905 	}
906 	else{
907 	    char **a2;
908 
909 	    this_addr_width = 0;
910 	    for(a2 = ae->addr.list; *a2 != NULL; a2++)
911 	      this_addr_width = MAX(this_addr_width, (int) utf8_width(*a2));
912 	}
913 
914 	if(ae->fcc)
915 	  this_fcc_width = (int) utf8_width(ae->fcc);
916 
917 	max_nick = MAX(max_nick, this_nick_width);
918 
919 	if(this_full_width > max_full){
920 	    full_three = full_two;
921 	    full_two   = max_full;
922 	    max_full   = this_full_width;
923 	}
924 	else if(this_full_width > full_two){
925 	    full_three = full_two;
926 	    full_two   = this_full_width;
927 	}
928 	else if(this_full_width > full_three){
929 	    full_three = this_full_width;
930 	}
931 
932 	if(this_addr_width > max_addr){
933 	    addr_three = addr_two;
934 	    addr_two   = max_addr;
935 	    max_addr   = this_addr_width;
936 	}
937 	else if(this_addr_width > addr_two){
938 	    addr_three = addr_two;
939 	    addr_two   = this_addr_width;
940 	}
941 	else if(this_addr_width > addr_three){
942 	    addr_three = this_addr_width;
943 	}
944 
945 	if(this_fcc_width > max_fcc){
946 	    fcc_three = fcc_two;
947 	    fcc_two   = max_fcc;
948 	    max_fcc   = this_fcc_width;
949 	}
950 	else if(this_fcc_width > fcc_two){
951 	    fcc_three = fcc_two;
952 	    fcc_two   = this_fcc_width;
953 	}
954 	else if(this_fcc_width > fcc_three){
955 	    fcc_three = this_fcc_width;
956 	}
957     }
958 
959     widths = &ab->widths;
960     widths->max_nickname_width  = MIN(max_nick, 99);
961     widths->max_fullname_width  = MIN(max_full, 99);
962     widths->max_addrfield_width = MIN(max_addr, 99);
963     widths->max_fccfield_width  = MIN(max_fcc, 99);
964     widths->third_biggest_fullname_width  = MIN(full_three, 99);
965     widths->third_biggest_addrfield_width = MIN(addr_three, 99);
966     widths->third_biggest_fccfield_width  = MIN(fcc_three, 99);
967 
968     dprint((9, "- build_abook_datastruct done -\n"));
969     return 0;
970 
971 io_err:
972     if(warning && errno != 0){
973 	strncpy(warning, error_description(errno), warninglen);
974 	warning[warninglen-1] = '\0';
975     }
976 
977     dprint((1, "build_ondisk: io_err: %s\n",
978 	   error_description(errno)));
979 
980     return -1;
981 }
982 
983 
984 /*
985  * Builds the trees used for nickname and address lookups.
986  */
987 int
build_abook_tries(AdrBk * ab,char * warning)988 build_abook_tries(AdrBk *ab, char *warning)
989 {
990     adrbk_cntr_t entry_num;
991     AdrBk_Entry *ae;
992     int we_cancel = 0;
993 
994     if(!ab)
995       return -1;
996 
997     dprint((9, "- build_abook_tries(%s) -\n", ab->filename));
998 
999 
1000     if(ab->nick_trie)
1001       free_abook_trie(&ab->nick_trie);
1002 
1003     if(ab->addr_trie)
1004       free_abook_trie(&ab->addr_trie);
1005 
1006     if(ab->full_trie)
1007       free_abook_trie(&ab->full_trie);
1008 
1009     if(ab->revfull_trie)
1010       free_abook_trie(&ab->revfull_trie);
1011 
1012     /*
1013      * Go through addrbook entries and add each to the tries it
1014      * belongs in.
1015      */
1016     for(entry_num = 0; entry_num < ab->count; entry_num++){
1017 	ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1018 	if(ae){
1019 	    /* every nickname in the nick trie */
1020 	    if(ae->nickname && ae->nickname[0])
1021 	      add_entry_to_trie(&ab->nick_trie, ae->nickname, (a_c_arg_t) entry_num);
1022 
1023 	    if(ae->fullname && ae->fullname[0]){
1024 		char *reverse = NULL;
1025 		char *forward = NULL;
1026 		char *comma = NULL;
1027 
1028 		/*
1029 		 * We have some fullnames stored as Last, First. Put both in.
1030 		 */
1031 		if(ae->fullname[0] != '"'
1032 		   && (comma=strindex(ae->fullname, ',')) != NULL
1033 		   && comma - ae->fullname > 0){
1034 		    forward = adrbk_formatname(ae->fullname, NULL, NULL);
1035 		    if(forward && forward[0]){
1036 			*comma = '\0';
1037 			reverse = cpystr(ae->fullname);
1038 			*comma = ',';
1039 		    }
1040 		    else{
1041 			if(forward)
1042 			  fs_give((void **) &forward);
1043 
1044 			forward = ae->fullname;
1045 		    }
1046 		}
1047 		else
1048 		  forward = ae->fullname;
1049 
1050 		if(forward){
1051 		    char *addthis;
1052 
1053 		    /*
1054 		     * Make this add not only the full name as is (forward) but
1055 		     * also add the middle name and last name (names after spaces).
1056 		     * so that they'll be found.
1057 		     */
1058 		    for(addthis=forward;
1059 			addthis && (*addthis);
1060 			addthis = strindex(addthis, ' ')){
1061 			while(*addthis == ' ')
1062 			  addthis++;
1063 
1064 			if(*addthis)
1065 			  add_entry_to_trie(&ab->full_trie, addthis, (a_c_arg_t) entry_num);
1066 		    }
1067 		}
1068 
1069 		if(reverse){
1070 		  add_entry_to_trie(&ab->revfull_trie, reverse, (a_c_arg_t) entry_num);
1071 		  fs_give((void **) &reverse);
1072 		}
1073 
1074 		if(forward && forward != ae->fullname)
1075 		  fs_give((void **) &forward);
1076 	    }
1077 
1078 	    if(ae->tag == Single && ae->addr.addr && ae->addr.addr[0]){
1079 		char     buf[1000];
1080 		char    *tmp_a_string, *simple_addr = NULL;
1081 		ADDRESS *addr = NULL;
1082 		char    *fakedomain = "@";
1083 
1084 		/*
1085 		 * Isolate the actual address out of ae.
1086 		 */
1087 		tmp_a_string = cpystr(ae->addr.addr);
1088 		rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1089 		if(tmp_a_string)
1090 		  fs_give((void **) &tmp_a_string);
1091 
1092 		if(addr){
1093 		    if(addr->mailbox && addr->host
1094 		       && !(addr->host[0] == '@' && addr->host[1] == '\0'))
1095 		      simple_addr = simple_addr_string(addr, buf, sizeof(buf));
1096 
1097 		    /*
1098 		     * If the fullname wasn't set in the addrbook entry there
1099 		     * may still be one that is part of the address. We need
1100 		     * to be careful because we may be in the middle of opening
1101 		     * the address book right now. Don't call something like
1102 		     * our_build_address because it will probably re-open this
1103 		     * same addrbook infinitely.
1104 		     */
1105 		    if(!(ae->fullname && ae->fullname[0])
1106 		       && addr->personal && addr->personal[0])
1107 		      add_entry_to_trie(&ab->full_trie, addr->personal,
1108 					(a_c_arg_t) entry_num);
1109 
1110 		    mail_free_address(&addr);
1111 		}
1112 
1113 		if(simple_addr)
1114 		  add_entry_to_trie(&ab->addr_trie, simple_addr, (a_c_arg_t) entry_num);
1115 	    }
1116 	}
1117     }
1118 
1119     dprint((9, "- build_abook_tries done -\n"));
1120 
1121     if(we_cancel)
1122       cancel_busy_cue(-1);
1123 
1124     return 0;
1125 }
1126 
1127 
1128 void
add_entry_to_trie(AdrBk_Trie ** head,char * str,a_c_arg_t entry_num)1129 add_entry_to_trie(AdrBk_Trie **head, char *str, a_c_arg_t entry_num)
1130 {
1131     AdrBk_Trie *temp, *trail;
1132     char *addthis, *p, buf[1000];
1133 
1134     if(!head || !str || !*str)
1135       return;
1136 
1137     /* add as lower case */
1138 
1139     for(p = str; *p && ((*p & 0x80) || !isupper((unsigned char) *p)); p++)
1140       ;
1141 
1142     if(*p){
1143 	strncpy(buf, str, sizeof(buf));
1144 	buf[sizeof(buf)-1] = '\0';
1145 	for(p = buf; *p; p++)
1146 	  if(!(*p & 0x80) && isupper((unsigned char) *p))
1147 	    *p = tolower(*p);
1148 
1149 	addthis = buf;
1150     }
1151     else
1152       addthis = str;
1153 
1154     temp = trail = (*head);
1155 
1156     /*
1157      * Find way down the trie, adding missing nodes as we go.
1158      */
1159     for(p = addthis; *p;){
1160 	if(temp == NULL){
1161 	    temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1162 	    memset(temp, 0, sizeof(*temp));
1163 	    temp->value = *p;
1164 	    temp->entrynum = NO_NEXT;
1165 
1166 	    if(*head == NULL)
1167 	      *head = temp;
1168 	    else
1169 	      trail->down = temp;
1170 	}
1171 	else{
1172 	    while((temp != NULL) && (temp->value != *p)){
1173 		trail = temp;
1174 		temp = temp->right;
1175 	    }
1176 
1177 	    /* wasn't there, add new node */
1178 	    if(temp == NULL){
1179 		temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1180 		memset(temp, 0, sizeof(*temp));
1181 		temp->value = *p;
1182 		temp->entrynum = NO_NEXT;
1183 		trail->right = temp;
1184 	    }
1185 	}
1186 
1187 	if(*(++p)){
1188 	    trail = temp;
1189 	    temp = temp->down;
1190 	}
1191     }
1192 
1193     /*
1194      * If entrynum is already filled in there must be an entry with
1195      * the same nickname earlier in the abook. Use that earlier entry.
1196      */
1197     if(temp != NULL && temp->entrynum == NO_NEXT)
1198       temp->entrynum = (adrbk_cntr_t) entry_num;
1199 }
1200 
1201 
1202 /*
1203  * Returns entry_num of first entry with this nickname, else NO_NEXT.
1204  */
1205 adrbk_cntr_t
lookup_nickname_in_trie(AdrBk * ab,char * nickname)1206 lookup_nickname_in_trie(AdrBk *ab, char *nickname)
1207 {
1208     if(!ab || !nickname || !ab->nick_trie)
1209       return(-1L);
1210 
1211     return(lookup_in_abook_trie(ab->nick_trie, nickname));
1212 }
1213 
1214 
1215 /*
1216  * Returns entry_num of first entry with this address, else NO_NEXT.
1217  */
1218 adrbk_cntr_t
lookup_address_in_trie(AdrBk * ab,char * address)1219 lookup_address_in_trie(AdrBk *ab, char *address)
1220 {
1221     dprint((9, "lookup_address_in_trie: %s\n", ab ? (ab->addr_trie ? (address ? address : "?") : "null addr_trie") : "null ab"));
1222     if(!ab || !address || !ab->addr_trie)
1223       return(-1L);
1224 
1225     return(lookup_in_abook_trie(ab->addr_trie, address));
1226 }
1227 
1228 
1229 adrbk_cntr_t
lookup_in_abook_trie(AdrBk_Trie * t,char * str)1230 lookup_in_abook_trie(AdrBk_Trie *t, char *str)
1231 {
1232     char        *p, *lookthisup;
1233     char         buf[1000];
1234     adrbk_cntr_t ret = NO_NEXT;
1235 
1236     if(!t || !str)
1237       return(ret);
1238 
1239     /* make lookup case independent */
1240 
1241     for(p = str; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
1242       ;
1243 
1244     if(*p){
1245 	strncpy(buf, str, sizeof(buf));
1246 	buf[sizeof(buf)-1] = '\0';
1247 	for(p = buf; *p; p++)
1248 	  if(!(*p & 0x80) && isupper((unsigned char) *p))
1249 	    *p = tolower(*p);
1250 
1251 	lookthisup = buf;
1252     }
1253     else
1254       lookthisup = str;
1255 
1256     p = lookthisup;
1257 
1258     /*
1259      * We usually return out from inside the loop (unless str == "").
1260      */
1261     while(*p){
1262 	/* search for character at this level */
1263 	while(t->value != *p){
1264 	    if(t->right == NULL)
1265 	      return(ret);		/* no match */
1266 
1267 	    t = t->right;
1268 	}
1269 
1270 	if(*++p == '\0')		/* end of str, a match */
1271 	  return(t->entrynum);
1272 
1273 	/* need to go down to match next character */
1274 	if(t->down == NULL)		/* no match */
1275 	  return(ret);
1276 
1277 	t = t->down;
1278     }
1279 
1280     return(ret);
1281 }
1282 
1283 
1284 void
free_abook_trie(AdrBk_Trie ** trie)1285 free_abook_trie(AdrBk_Trie **trie)
1286 {
1287     if(trie){
1288 	if(*trie){
1289 	    free_abook_trie(&(*trie)->down);
1290 	    free_abook_trie(&(*trie)->right);
1291 	    fs_give((void **) trie);
1292 	}
1293     }
1294 }
1295 
1296 
1297 /*
1298  * Returns pointer to start of next address book entry from disk file.
1299  * The return will be in raw form from the file with newlines still
1300  * embedded. Or NULL at end of file.
1301  *
1302  * If rew is set, rewind the file and start over at beginning.
1303  */
1304 char *
get_next_abook_entry(FILE * fp,int rew)1305 get_next_abook_entry(FILE *fp, int rew)
1306 {
1307     char       *returned_lines = NULL, *p;
1308     char        line[1024];
1309     static int  will_be_done_next_time = 0;
1310     static long next_nickname_offset   = 0L;
1311     size_t      lsize;
1312     long        offset, saved_offset;
1313     long        len = 0L;
1314 
1315 #define CHUNKSIZE 500
1316 
1317     lsize = sizeof(line);
1318 
1319     if(rew){
1320 	will_be_done_next_time = 0;
1321 	rewind(fp);
1322 	/* skip leading (bogus) continuation lines */
1323 	do{
1324 	    offset  = ftell(fp);
1325 	    line[0] = '\0';
1326 	    line[lsize-2] = '\0';
1327 	    p = fgets(line, lsize, fp);
1328 
1329 	    if(p == NULL)
1330 	      return(NULL);
1331 
1332 	    /* line is too long to fit, read the rest and discard */
1333 	    while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1334 		  && p != NULL){
1335 
1336 		/* get next lsize-1 characters, leaving line[0] */
1337 		line[lsize-2] = '\0';
1338 		p = fgets(line+1, lsize-1, fp);
1339 	    }
1340 	}while(line[0] == SPACE);
1341 
1342 	/* offset is start of first good line now */
1343 	next_nickname_offset = offset;
1344     }
1345 
1346     if(will_be_done_next_time)
1347       return(NULL);
1348 
1349     /* we set this up in rew==1 case or on previous call to this routine */
1350     offset = next_nickname_offset;
1351 
1352     /*
1353      * The rest is working on finding the start of the next entry so
1354      * skip continuation lines
1355      */
1356     do{
1357 	next_nickname_offset = ftell(fp);
1358 	line[0] = '\0';
1359 	line[lsize-2] = '\0';
1360 	p = fgets(line, lsize, fp);
1361 
1362 	/* line is too long to fit, read the rest and discard */
1363 	while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1364 	      && p != NULL){
1365 
1366 	    /* get next lsize-1 characters, leaving line[0] */
1367 	    line[lsize-2] = '\0';
1368 	    p = fgets(line+1, lsize-1, fp);
1369 	}
1370     }while(line[0] == SPACE);
1371 
1372     /* next_nickname_offset is start of next entry now */
1373 
1374     if(!line[0])
1375       will_be_done_next_time = 1;
1376 
1377     len = next_nickname_offset - offset;
1378 
1379     returned_lines = (char *) fs_get((len + 1) * sizeof(char));
1380 
1381     saved_offset = ftell(fp);
1382     if(fseek(fp, offset, 0)){
1383 	dprint((2, "get_next_ab_entry: trouble fseeking\n"));
1384 	len = 0;
1385 	fs_give((void **) &returned_lines);
1386     }
1387     else{
1388 	if(fread(returned_lines, sizeof(char), (unsigned) len, fp) != len){
1389 	    dprint((2, "get_next_ab_entry: trouble freading\n"));
1390 	    len = 0;
1391 	    fs_give((void **) &returned_lines);
1392 	}
1393     }
1394 
1395     if(fseek(fp, saved_offset, 0)){
1396 	dprint((2, "get_next_ab_entry: trouble fseeking to saved_offset\n"));
1397 	len = 0;
1398 	fs_give((void **) &returned_lines);
1399     }
1400 
1401     if(returned_lines)
1402       returned_lines[len] = '\0';
1403 
1404     return(returned_lines);
1405 }
1406 
1407 
1408 /*
1409  * Returns a pointer to the start of the mailbox@host part of this
1410  * address string, and a pointer to the end + 1.  The caller can then
1411  * replace the end char with \0 and call the hash function, then put
1412  * back the end char.  Start_addr and end_addr are assumed to be non-null.
1413  */
1414 void
strip_addr_string(char * addrstr,char ** start_addr,char ** end_addr)1415 strip_addr_string(char *addrstr, char **start_addr, char **end_addr)
1416 {
1417     register char *q;
1418     int in_quotes  = 0,
1419         in_comment = 0;
1420     char prev_char = '\0';
1421 
1422     if(!addrstr || !*addrstr){
1423 	*start_addr = NULL;
1424 	*end_addr = NULL;
1425 	return;
1426     }
1427 
1428     *start_addr = addrstr;
1429 
1430     for(q = addrstr; *q; q++){
1431 	switch(*q){
1432 	  case '<':
1433 	    if(!in_quotes && !in_comment){
1434 		if(*++q){
1435 		    *start_addr = q;
1436 		    /* skip to > */
1437 		    while(*q && *q != '>')
1438 		      q++;
1439 
1440 		    /* found > */
1441 		    if(*q){
1442 			*end_addr = q;
1443 			return;
1444 		    }
1445 		    else
1446 		      q--;
1447 		}
1448 	    }
1449 
1450 	    break;
1451 
1452 	  case LPAREN:
1453 	    if(!in_quotes && !in_comment)
1454 	      in_comment = 1;
1455 	    break;
1456 
1457 	  case RPAREN:
1458 	    if(in_comment && prev_char != BSLASH)
1459 	      in_comment = 0;
1460 	    break;
1461 
1462 	  case QUOTE:
1463 	    if(in_quotes && prev_char != BSLASH)
1464 	      in_quotes = 0;
1465 	    else if(!in_quotes && !in_comment)
1466 	      in_quotes = 1;
1467 	    break;
1468 
1469 	  default:
1470 	    break;
1471 	}
1472 
1473 	prev_char = *q;
1474     }
1475 
1476     *end_addr = q;
1477 }
1478 
1479 
1480 /*
1481  * Fill in the passed in ae pointer by parsing the str that is passed.
1482  *
1483  * Args   ab --
1484  *        ae -- pointer we want to fill in. The individual members of
1485  *                the ae struct will be allocated here
1486  *       str -- the string from the on-disk address book file for this entry
1487  *
1488  * Returns a pointer to ae or NULL if there are problems.
1489  */
1490 AdrBk_Entry *
init_ae(AdrBk * ab,AdrBk_Entry * a,char * str)1491 init_ae(AdrBk *ab, AdrBk_Entry *a, char *str)
1492 {
1493     char *p;
1494     char *addrfield = (char *) NULL;
1495     char *addrfield_end;
1496     char *nickname, *fullname, *fcc, *extra;
1497 
1498     if(!ab){
1499 	dprint((2, "init_ae: found trouble: NULL ab\n"));
1500 	return((AdrBk_Entry *) NULL);
1501     }
1502 
1503     defvalue_ae(a);
1504 
1505     p = str;
1506 
1507     REPLACE_NEWLINES_WITH_SPACE(p);
1508 
1509     nickname = p;
1510     SKIP_TO_TAB(p);
1511     if(!*p){
1512 	RM_END_SPACE(nickname, p);
1513 	a->nickname = cpystr(nickname);
1514     }
1515     else{
1516 	*p = '\0';
1517 	RM_END_SPACE(nickname, p);
1518 	a->nickname = cpystr(nickname);
1519 	p++;
1520 	SKIP_SPACE(p);
1521 	fullname = p;
1522 	SKIP_TO_TAB(p);
1523 	if(!*p){
1524 	    RM_END_SPACE(fullname, p);
1525 	    a->fullname = cpystr(fullname);
1526 	}
1527 	else{
1528 	    *p = '\0';
1529 	    RM_END_SPACE(fullname, p);
1530 	    a->fullname = cpystr(fullname);
1531 	    p++;
1532 	    SKIP_SPACE(p);
1533 	    addrfield = p;
1534 	    SKIP_TO_TAB(p);
1535 	    if(!*p){
1536 		RM_END_SPACE(addrfield, p);
1537 	    }
1538 	    else{
1539 		*p = '\0';
1540 		RM_END_SPACE(addrfield, p);
1541 		p++;
1542 		SKIP_SPACE(p);
1543 		fcc = p;
1544 		SKIP_TO_TAB(p);
1545 		if(!*p){
1546 		    RM_END_SPACE(fcc, p);
1547 		    a->fcc = cpystr(fcc);
1548 		}
1549 		else{
1550 		    char *src, *dst;
1551 
1552 		    *p = '\0';
1553 		    RM_END_SPACE(fcc, p);
1554 		    a->fcc = cpystr(fcc);
1555 		    p++;
1556 		    SKIP_SPACE(p);
1557 		    extra = p;
1558 		    p = extra + strlen(extra);
1559 		    RM_END_SPACE(extra, p);
1560 
1561 		    /*
1562 		     * When we wrap long comments we insert an extra colon
1563 		     * in the wrap so we can spot it and take it back out.
1564 		     * Pretty much a hack since we thought of it a long
1565 		     * time after designing it, but it eliminates the limit
1566 		     * on length of comments. Here we are looking for
1567 		     * <SP> <SP> : <SP> or
1568 		     * <SP> <SP> <SP> : <SP> and replacing it with <SP>.
1569 		     * There could have been a single \n or \r\n, so that
1570 		     * is why we check for 2 or 3 spaces before the colon.
1571 		     * (This was another mistake.)
1572 		     */
1573 		    dst = src = extra;
1574 		    while(*src != '\0'){
1575 			if(*src == SPACE && *(src+1) == SPACE &&
1576 			   *(src+2) == ':' && *(src+3) == SPACE){
1577 
1578 			    /*
1579 			     * If there was an extra space because of the
1580 			     * CRLF (instead of LF) then we already put
1581 			     * a SP in dst last time through the loop
1582 			     * and don't need to add another.
1583 			     */
1584 			    if(src == extra || *(src-1) != SPACE)
1585 			      *dst++ = *src;
1586 
1587 			    src += 4;
1588 			}
1589 			else
1590 			  *dst++ = *src++;
1591 		    }
1592 
1593 		    *dst = '\0';
1594 		    a->extra = cpystr(extra);
1595 		}
1596 	    }
1597 	}
1598     }
1599 
1600     /* decode and convert to UTF-8 if we need to */
1601 
1602     convert_possibly_encoded_str_to_utf8(&a->nickname);
1603     convert_possibly_encoded_str_to_utf8(&a->fullname);
1604     convert_possibly_encoded_str_to_utf8(&a->fcc);
1605     convert_possibly_encoded_str_to_utf8(&a->extra);
1606 
1607     /* parse addrfield */
1608     if(addrfield){
1609 	if(*addrfield == '('){  /* it's a list */
1610 	    a->tag = List;
1611 	    p = addrfield;
1612 	    addrfield_end = p + strlen(p);
1613 
1614 	    /*
1615 	     * Get rid of the parens.
1616 	     * If this isn't true the input file is messed up.
1617 	     */
1618 	    if(p[strlen(p)-1] == ')'){
1619 		char **ll;
1620 
1621 		p[strlen(p)-1] = '\0';
1622 		p++;
1623 		a->addr.list = parse_addrlist(p);
1624 		for(ll = a->addr.list; ll && *ll; ll++)
1625 		  convert_possibly_encoded_str_to_utf8(ll);
1626 	    }
1627 	    else{
1628 		/* put back what was there to start with */
1629 		*addrfield_end = ')';
1630 		a->addr.list = (char **)fs_get(sizeof(char *) * 2);
1631 		a->addr.list[0] = cpystr(addrfield);
1632 		a->addr.list[1] = NULL;
1633 		dprint((2, "parsing error reading addressbook: missing right paren: %s\n",
1634 			addrfield ? addrfield : "?"));
1635 	    }
1636 	}
1637 	else{  /* A plain, single address */
1638 
1639 	    a->tag       = Single;
1640 	    a->addr.addr = cpystr(addrfield);
1641 	    convert_possibly_encoded_str_to_utf8(&a->addr.addr);
1642 	}
1643     }
1644     else{
1645 	/*
1646 	 * If no addrfield, assume an empty Single.
1647 	 */
1648 	a->addr.addr = cpystr("");
1649 	a->tag       = Single;
1650     }
1651 
1652     return(a);
1653 }
1654 
1655 
1656 /*
1657  * Return the size of the address book
1658  */
1659 adrbk_cntr_t
adrbk_count(AdrBk * ab)1660 adrbk_count(AdrBk *ab)
1661 {
1662     return(ab ? ab->count : (adrbk_cntr_t) 0);
1663 }
1664 
1665 
1666 /*
1667  * Return a pointer to the ae that has index number "entry_num".
1668  */
1669 AdrBk_Entry *
adrbk_get_ae(AdrBk * ab,a_c_arg_t entry_num)1670 adrbk_get_ae(AdrBk *ab, a_c_arg_t entry_num)
1671 {
1672     if(!ab || entry_num >= (a_c_arg_t) ab->count)
1673       return((AdrBk_Entry *) NULL);
1674 
1675     return(&ab->arr[entry_num]);
1676 }
1677 
1678 
1679 /*
1680  * Return a pointer to the deleted ae that has index number "entry_num".
1681  */
1682 AdrBk_Entry *
adrbk_get_delae(AdrBk * ab,a_c_arg_t entry_num)1683 adrbk_get_delae(AdrBk *ab, a_c_arg_t entry_num)
1684 {
1685     if(!ab || entry_num >= (a_c_arg_t) ab->del_count)
1686       return((AdrBk_Entry *) NULL);
1687 
1688     return(&ab->del[entry_num]);
1689 }
1690 
1691 
1692 /*
1693  * Look up an entry in the address book given a nickname
1694  *
1695  * Args: ab       -- the address book
1696  *       nickname -- nickname to match
1697  *      entry_num -- if matched, return entry_num of match here
1698  *
1699  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1700  *
1701  * Lookups usually need to be recursive in case the address
1702  * book references itself.  This is left to the next level up.
1703  * adrbk_clearrefs() is provided to clear all the reference tags in
1704  * the address book for loop detection.
1705  * When there are duplicates of the same nickname we return the first.
1706  * This can only happen if addrbook was edited externally.
1707  */
1708 AdrBk_Entry *
adrbk_lookup_by_nick(AdrBk * ab,char * nickname,adrbk_cntr_t * entry_num)1709 adrbk_lookup_by_nick(AdrBk *ab, char *nickname, adrbk_cntr_t *entry_num)
1710 {
1711     adrbk_cntr_t num;
1712     AdrBk_Entry *ae;
1713 
1714     dprint((5, "- adrbk_lookup_by_nick(%s) (in %s) -\n",
1715 	   nickname ? nickname : "?",
1716 	   (ab && ab->filename) ? ab->filename : "?"));
1717 
1718     if(!ab || !nickname || !nickname[0])
1719       return NULL;
1720 
1721 
1722     num = lookup_nickname_in_trie(ab, nickname);
1723 
1724     if(num != NO_NEXT){
1725 	ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1726 	if(entry_num && ae)
1727 	  *entry_num = num;
1728 
1729 	return(ae);
1730     }
1731     else
1732       return((AdrBk_Entry *) NULL);
1733 }
1734 
1735 
1736 /*
1737  * Look up an entry in the address book given an address
1738  *
1739  * Args: ab       -- the address book
1740  *       address  -- address to match
1741  *      entry_num -- if matched, return entry_num of match here
1742  *
1743  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1744  *
1745  * Note:  When there are multiple occurrences of an address in an addressbook,
1746  * which there will be if more than one nickname points to same address, then
1747  * we want this to match the first occurrence so that the fcc you get will
1748  * be predictable.
1749  */
1750 AdrBk_Entry *
adrbk_lookup_by_addr(AdrBk * ab,char * address,adrbk_cntr_t * entry_num)1751 adrbk_lookup_by_addr(AdrBk *ab, char *address, adrbk_cntr_t *entry_num)
1752 {
1753     adrbk_cntr_t num;
1754     AdrBk_Entry *ae;
1755 
1756     dprint((5, "- adrbk_lookup_by_addr(%s) (in %s) -\n",
1757 	   address ? address : "?",
1758 	   (ab && ab->filename) ? ab->filename : "?"));
1759 
1760     if(!ab || !address || !address[0])
1761       return((AdrBk_Entry *)NULL);
1762 
1763     num = lookup_address_in_trie(ab, address);
1764 
1765     if(num != NO_NEXT){
1766 	ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1767 	if(entry_num && ae)
1768 	  *entry_num = num;
1769 
1770 	return(ae);
1771     }
1772     else
1773       return((AdrBk_Entry *)NULL);
1774 }
1775 
1776 
1777 /*
1778  * Format a full name.
1779  *
1780  * Args: fullname -- full name out of address book for formatting
1781  *          first -- Return a pointer to first name here.
1782  *           last -- Return a pointer to last name here.
1783  *
1784  * Result:  Returns pointer to name formatted for a mail header. Space is
1785  *          allocated here and should be freed by caller.
1786  *
1787  * We need this because we store full names as Last, First.
1788  * If the name has no comma, then no change is made.
1789  * Otherwise the text before the first comma is moved to the end and
1790  * the comma is deleted.
1791  *
1792  * Last and first have to be freed by caller.
1793  */
1794 char *
adrbk_formatname(char * fullname,char ** first,char ** last)1795 adrbk_formatname(char *fullname, char **first, char **last)
1796 {
1797     char       *comma;
1798     char       *new_name;
1799 
1800     if(first)
1801       *first = NULL;
1802     if(last)
1803       *last = NULL;
1804 
1805     /*
1806      * There is an assumption that the fullname is a UTF-8 string.
1807      */
1808 
1809     if(fullname[0] != '"'  && (comma = strindex(fullname, ',')) != NULL){
1810 	size_t l;
1811         int last_name_len = comma - fullname;
1812 
1813         comma++;
1814         while(*comma && isspace((unsigned char)*comma))
1815 	  comma++;
1816 
1817 	if(first)
1818 	  *first = cpystr(comma);
1819 
1820 	if(last){
1821 	    *last = (char *)fs_get((last_name_len + 1) * sizeof(char));
1822 	    strncpy(*last, fullname, last_name_len);
1823 	    (*last)[last_name_len] = '\0';
1824 	}
1825 
1826 	l = strlen(comma) + 1 + last_name_len;
1827 	new_name = (char *) fs_get((l+1) * sizeof(char));
1828         strncpy(new_name, comma, l);
1829 	new_name[l] = '\0';
1830         strncat(new_name, " ", l+1-1-strlen(new_name));
1831 	new_name[l] = '\0';
1832         strncat(new_name, fullname, MIN(last_name_len,l+1-1-strlen(new_name)));
1833 	new_name[l] = '\0';
1834     }
1835     else
1836       new_name = cpystr(fullname);
1837 
1838     return(new_name);
1839 }
1840 
1841 
1842 /*
1843  * Clear reference flags in preparation for a recursive lookup.
1844  *
1845  * For loop detection during address book look up.  This clears all the
1846  * referenced flags, then as the lookup proceeds the referenced flags can
1847  * be checked and set.
1848  */
1849 void
adrbk_clearrefs(AdrBk * ab)1850 adrbk_clearrefs(AdrBk *ab)
1851 {
1852     adrbk_cntr_t entry_num;
1853     AdrBk_Entry *ae;
1854 
1855     dprint((9, "- adrbk_clearrefs -\n"));
1856 
1857     if(!ab)
1858       return;
1859 
1860     for(entry_num = 0; entry_num < ab->count; entry_num++){
1861 	ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1862 	ae->referenced = 0;
1863     }
1864 }
1865 
1866 
1867 /*
1868  *  Allocate a new AdrBk_Entry
1869  */
1870 AdrBk_Entry *
adrbk_newentry(void)1871 adrbk_newentry(void)
1872 {
1873     AdrBk_Entry *ae;
1874 
1875     ae = (AdrBk_Entry *) fs_get(sizeof(AdrBk_Entry));
1876     defvalue_ae(ae);
1877 
1878     return(ae);
1879 }
1880 
1881 
1882 /*
1883  *  Just sets ae values to default.
1884  *  Parts should be freed before calling this or they will leak.
1885  */
1886 void
defvalue_ae(AdrBk_Entry * ae)1887 defvalue_ae(AdrBk_Entry *ae)
1888 {
1889     ae->nickname    = empty;
1890     ae->fullname    = empty;
1891     ae->addr.addr   = empty;
1892     ae->fcc         = empty;
1893     ae->extra       = empty;
1894     ae->tag         = NotSet;
1895     ae->referenced  = 0;
1896 }
1897 
1898 
1899 AdrBk_Entry *
copy_ae(AdrBk_Entry * src)1900 copy_ae(AdrBk_Entry *src)
1901 {
1902     AdrBk_Entry *a;
1903 
1904     a = adrbk_newentry();
1905     a->tag = src->tag;
1906     a->nickname = cpystr(src->nickname ? src->nickname : "");
1907     a->fullname = cpystr(src->fullname ? src->fullname : "");
1908     a->fcc      = cpystr(src->fcc ? src->fcc : "");
1909     a->extra    = cpystr(src->extra ? src->extra : "");
1910     if(a->tag == Single)
1911       a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
1912     else if(a->tag == List){
1913 	char **p;
1914 	int    i, n;
1915 
1916 	/* count list */
1917 	for(p = src->addr.list; p && *p; p++)
1918 	  ;/* do nothing */
1919 
1920 	if(p == NULL)
1921 	  n = 0;
1922 	else
1923 	  n = p - src->addr.list;
1924 
1925 	a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
1926 	for(i = 0; i < n; i++)
1927 	  a->addr.list[i] = cpystr(src->addr.list[i]);
1928 
1929 	a->addr.list[n] = NULL;
1930     }
1931 
1932     return(a);
1933 }
1934 
1935 
1936 /*
1937  * Add an entry to the address book, or modify an existing entry
1938  *
1939  * Args: ab       -- address book to add to
1940  *  old_entry_num -- the entry we want to modify.  If this is NO_NEXT, then
1941  *                    we look up the nickname passed in to see if that's the
1942  *                    entry to modify, else it is a new entry.
1943  *       nickname -- the nickname for new entry
1944  *       fullname -- the fullname for new entry
1945  *       address  -- the address for new entry
1946  *       fcc      -- the fcc for new entry
1947  *       extra    -- the extra field for new entry
1948  *       tag      -- the type of new entry
1949  *  new_entry_num -- return entry_num of new or modified entry here
1950  * resort_happened -- means that more than just the current entry changed,
1951  *                     either something was added or order was changed
1952  *     enable_intr -- tell adrbk_write to enable interrupt handling
1953  *        be_quiet -- tell adrbk_write to not do percent done messages
1954  *        write_it -- only do adrbk_write if this is set
1955  *
1956  * Result: return code:  0 all went well
1957  *                      -2 error writing address book, check errno
1958  *		        -3 no modification, the tag given didn't match
1959  *                         existing tag
1960  *                      -4 tabs are in one of the fields passed in
1961  *
1962  * If the nickname exists in the address book already, the operation is
1963  * considered a modification even if the case does not match exactly,
1964  * otherwise it is an add.  The entry the operation occurs on is returned
1965  * in new.  All fields are set to those passed in; that is, passing in NULL
1966  * even on a modification will set those fields to NULL as opposed to leaving
1967  * them unchanged.  It is acceptable to pass in the current strings
1968  * in the entry in the case of modification.  For address lists, the
1969  * structure passed in is what is used, so the storage has to all have
1970  * come from fs_get().  If the pointer passed in is the same as
1971  * the current field, no change is made.
1972  */
1973 int
adrbk_add(AdrBk * ab,a_c_arg_t old_entry_num,char * nickname,char * fullname,char * address,char * fcc,char * extra,Tag tag,adrbk_cntr_t * new_entry_num,int * resort_happened,int enable_intr,int be_quiet,int write_it)1974 adrbk_add(AdrBk *ab, a_c_arg_t old_entry_num, char *nickname, char *fullname,
1975 	  char *address, char *fcc, char *extra, Tag tag, adrbk_cntr_t *new_entry_num,
1976 	  int *resort_happened, int enable_intr, int be_quiet, int write_it)
1977 {
1978     AdrBk_Entry *a;
1979     AdrBk_Entry *ae;
1980     adrbk_cntr_t old_enum;
1981     adrbk_cntr_t new_enum;
1982     int (*cmp_func)();
1983     int retval = 0;
1984     int need_write = 0;
1985     int set_mangled = 0;
1986 
1987     dprint((3, "- adrbk_add(%s) -\n", nickname ? nickname : ""));
1988 
1989     if(!ab)
1990       return -2;
1991 
1992     /* ---- Make sure there are no tabs in the stuff to add ------*/
1993     if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
1994        (fullname != NULL && strindex(fullname, TAB) != NULL) ||
1995        (fcc != NULL && strindex(fcc, TAB) != NULL) ||
1996        (tag == Single && address != NULL && strindex(address, TAB) != NULL))
1997         return -4;
1998 
1999     /*
2000      * Are we adding or updating ?
2001      *
2002      * If old_entry_num was passed in, we're updating that.  If nickname
2003      * already exists, we're updating that entry.  Otherwise, this is an add.
2004      */
2005     if((adrbk_cntr_t)old_entry_num != NO_NEXT){
2006 	ae = adrbk_get_ae(ab, old_entry_num);
2007 	if(ae)
2008 	  old_enum = (adrbk_cntr_t)old_entry_num;
2009     }
2010     else
2011       ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
2012 
2013     if(ae == NULL){  /*----- adding a new entry ----*/
2014 
2015 	ae            = adrbk_newentry();
2016 	ae->tag       = tag;
2017 	if(nickname)
2018 	  ae->nickname  = cpystr(nickname);
2019 	if(fullname)
2020 	  ae->fullname  = cpystr(fullname);
2021 	if(fcc)
2022 	  ae->fcc  = cpystr(fcc);
2023 	if(extra)
2024 	  ae->extra  = cpystr(extra);
2025 
2026 	if(tag == Single)
2027           ae->addr.addr = cpystr(address);
2028 	else
2029 	  ae->addr.list = (char **)NULL;
2030 
2031 	cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2032 						cmp_ae_by_full_lists_last :
2033 		   (ab->sort_rule == AB_SORT_RULE_FULL) ?
2034 						cmp_ae_by_full :
2035 		   (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2036 						cmp_ae_by_nick_lists_last :
2037 		/* (ab->sort_rule == AB_SORT_RULE_NICK) */
2038 						cmp_ae_by_nick;
2039 
2040 	if(ab->sort_rule == AB_SORT_RULE_NONE)  /* put it last */
2041 	  new_enum = ab->count;
2042 	else  /* Find slot for it */
2043 	  for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t) new_enum);
2044 	      a != (AdrBk_Entry *)NULL;
2045 	      a = adrbk_get_ae(ab, (a_c_arg_t) (++new_enum))){
2046 		    if((*cmp_func)((qsort_t *)&a, (qsort_t *)&ae) >= 0)
2047 			break;
2048 	  }
2049 
2050 	/* Insert ae before entry new_enum. */
2051 	insert_ab_entry(ab, (a_c_arg_t) new_enum, ae, 0);
2052 
2053 	if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2054 	  exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
2055 
2056 	exp_add_nth(ab->selects, (a_c_arg_t)new_enum);
2057 
2058 	/*
2059 	 * insert_ab_entry copies the pointers of ae so the things
2060 	 * being pointed to (nickname, ...) are still in use.
2061 	 * Don't free them but free the ae struct itself.
2062 	 */
2063 	fs_give((void **) &ae);
2064 
2065         /*---- return in pointer if requested -----*/
2066         if(new_entry_num)
2067 	  *new_entry_num = new_enum;
2068 
2069         if(resort_happened)
2070 	  *resort_happened = 1;
2071 
2072 	if(write_it)
2073 	  retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num, &set_mangled,
2074 			       enable_intr, be_quiet);
2075     }
2076     else{
2077         /*----- Updating an existing entry ----*/
2078 
2079 	int fix_tries = 0;
2080 
2081 	if(ae->tag != tag)
2082 	  return -3;
2083 
2084         /*
2085 	 * Instead of just freeing and reallocating here we attempt to re-use
2086 	 * the space that was already allocated if possible.
2087 	 */
2088 	if(ae->nickname != nickname
2089 	   && ae->nickname != NULL
2090 	   && nickname != NULL
2091 	   && strcmp(nickname, ae->nickname) != 0){
2092 	    need_write++;
2093 	    fix_tries++;
2094 	    /* can use already alloc'd space */
2095             if(ae->nickname != NULL && nickname != NULL &&
2096 	       strlen(nickname) <= strlen(ae->nickname)){
2097 
2098                 strncpy(ae->nickname, nickname, strlen(ae->nickname)+1);
2099             }
2100 	    else{
2101                 if(ae->nickname != NULL && ae->nickname != empty)
2102                   fs_give((void **)&ae->nickname);
2103 
2104                 ae->nickname = nickname ? cpystr(nickname) : nickname;
2105 	    }
2106         }
2107 
2108 	if(ae->fullname != fullname
2109 	   && ae->fullname != NULL
2110 	   && fullname != NULL
2111 	   && strcmp(fullname, ae->fullname) != 0){
2112 	    need_write++;
2113             if(ae->fullname != NULL && fullname != NULL &&
2114 	       strlen(fullname) <= strlen(ae->fullname)){
2115 
2116                 strncpy(ae->fullname, fullname, strlen(ae->fullname)+1);
2117             }
2118 	    else{
2119                 if(ae->fullname != NULL && ae->fullname != empty)
2120                   fs_give((void **)&ae->fullname);
2121 
2122                 ae->fullname = fullname ? cpystr(fullname) : fullname;
2123 	    }
2124         }
2125 
2126 	if(ae->fcc != fcc
2127 	   && ae->fcc != NULL
2128 	   && fcc != NULL
2129 	   && strcmp(fcc, ae->fcc) != 0){
2130 	    need_write++;
2131             if(ae->fcc != NULL && fcc != NULL &&
2132 	       strlen(fcc) <= strlen(ae->fcc)){
2133 
2134                 strncpy(ae->fcc, fcc, strlen(ae->fcc)+1);
2135             }
2136 	    else{
2137                 if(ae->fcc != NULL && ae->fcc != empty)
2138                   fs_give((void **)&ae->fcc);
2139 
2140                 ae->fcc = fcc ? cpystr(fcc) : fcc;
2141 	    }
2142         }
2143 
2144 	if(ae->extra != extra
2145 	   && ae->extra != NULL
2146 	   && extra != NULL
2147 	   && strcmp(extra, ae->extra) != 0){
2148 	    need_write++;
2149             if(ae->extra != NULL && extra != NULL &&
2150 	       strlen(extra) <= strlen(ae->extra)){
2151 
2152                 strncpy(ae->extra, extra, strlen(ae->extra)+1);
2153             }
2154 	    else{
2155                 if(ae->extra != NULL && ae->extra != empty)
2156                   fs_give((void **)&ae->extra);
2157 
2158                 ae->extra = extra ? cpystr(extra) : extra;
2159 	    }
2160         }
2161 
2162 	if(tag == Single){
2163             /*---- Single ----*/
2164 	    if(ae->addr.addr != address
2165 	       && ae->addr.addr != NULL
2166 	       && address != NULL
2167 	       && strcmp(address, ae->addr.addr) != 0){
2168 		need_write++;
2169 		fix_tries++;
2170 		if(ae->addr.addr != NULL && address != NULL &&
2171 		   strlen(address) <= strlen(ae->addr.addr)){
2172 
2173 		    strncpy(ae->addr.addr, address, strlen(ae->addr.addr)+1);
2174 		}
2175 		else{
2176 		    if(ae->addr.addr != NULL && ae->addr.addr != empty)
2177 		      fs_give((void **)&ae->addr.addr);
2178 
2179 		    ae->addr.addr = address ? cpystr(address) : address;
2180 		}
2181 	    }
2182 	}
2183 	else{
2184             /*---- List -----*/
2185             /*
2186 	     * We don't mess with lists here.
2187 	     * The caller has to do it with adrbk_listadd().
2188 	     */
2189 	    ;/* do nothing */
2190 	}
2191 
2192 
2193         /*---------- Make sure it's still in order ---------*/
2194 
2195 	/*
2196 	 * old_enum is where ae is currently located
2197 	 * put it where it belongs
2198 	 */
2199 	if(need_write){
2200 	    new_enum = re_sort_particular_entry(ab, (a_c_arg_t) old_enum);
2201 	    if(old_enum != new_enum)
2202 	      fix_tries++;
2203 	}
2204 	else
2205 	  new_enum = old_enum;
2206 
2207         /*---- return in pointer if requested -----*/
2208         if(new_entry_num)
2209 	  *new_entry_num = new_enum;
2210 
2211         if(resort_happened)
2212 	  *resort_happened = (old_enum != new_enum);
2213 
2214 	if(fix_tries)
2215 	  repair_abook_tries(ab);
2216 
2217 	if(write_it && need_write){
2218 	    int sort_happened = 0;
2219 
2220 	    retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num,
2221 			         &sort_happened, enable_intr, be_quiet);
2222 
2223 	    set_mangled = sort_happened;
2224 
2225 	    if(new_entry_num)
2226 	      new_enum = (*new_entry_num);
2227 
2228 	    if(resort_happened && (sort_happened || (old_enum != new_enum)))
2229 	      *resort_happened = 1;
2230 	}
2231 	else
2232 	  retval = 0;
2233     }
2234 
2235     if(set_mangled)
2236       ps_global->mangled_screen = 1;
2237 
2238     return(retval);
2239 }
2240 
2241 
2242 /*
2243  * Similar to adrbk_add, but lower cost. No sorting is done, the new entry
2244  * goes on the end. This won't work if it is an edit instead of an append.
2245  * The address book is not committed to disk.
2246  *
2247  * Args: ab       -- address book to add to
2248  *       nickname -- the nickname for new entry
2249  *       fullname -- the fullname for new entry
2250  *       address  -- the address for new entry
2251  *       fcc      -- the fcc for new entry
2252  *       extra    -- the extra field for new entry
2253  *       tag      -- the type of new entry
2254  *
2255  * Result: return code:  0 all went well
2256  *                      -2 error writing address book, check errno
2257  *		        -3 no modification, the tag given didn't match
2258  *                         existing tag
2259  *                      -4 tabs are in one of the fields passed in
2260  */
2261 int
adrbk_append(AdrBk * ab,char * nickname,char * fullname,char * address,char * fcc,char * extra,Tag tag,adrbk_cntr_t * new_entry_num)2262 adrbk_append(AdrBk *ab, char *nickname, char *fullname, char *address, char *fcc,
2263 	     char *extra, Tag tag, adrbk_cntr_t *new_entry_num)
2264 {
2265     AdrBk_Entry *ae;
2266 
2267     dprint((3, "- adrbk_append(%s) -\n", nickname ? nickname : ""));
2268 
2269     if(!ab)
2270       return -2;
2271 
2272     /* ---- Make sure there are no tabs in the stuff to add ------*/
2273     if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
2274        (fullname != NULL && strindex(fullname, TAB) != NULL) ||
2275        (fcc != NULL && strindex(fcc, TAB) != NULL) ||
2276        (tag == Single && address != NULL && strindex(address, TAB) != NULL))
2277         return -4;
2278 
2279     ae            = adrbk_newentry();
2280     ae->tag       = tag;
2281     if(nickname)
2282       ae->nickname  = cpystr(nickname);
2283     if(fullname)
2284       ae->fullname  = cpystr(fullname);
2285     if(fcc)
2286       ae->fcc  = cpystr(fcc);
2287     if(extra)
2288       ae->extra  = cpystr(extra);
2289 
2290     if(tag == Single)
2291       ae->addr.addr = cpystr(address);
2292     else
2293       ae->addr.list = (char **)NULL;
2294 
2295     if(new_entry_num)
2296       *new_entry_num = ab->count;
2297 
2298     insert_ab_entry(ab, (a_c_arg_t) ab->count, ae, 0);
2299 
2300     /*
2301      * insert_ab_entry copies the pointers of ae so the things
2302      * being pointed to (nickname, ...) are still in use.
2303      * Don't free them but free the ae struct itself.
2304      */
2305     fs_give((void **) &ae);
2306 
2307     return(0);
2308 }
2309 
2310 
2311 /*
2312  * The entire address book is assumed sorted correctly except perhaps for
2313  * entry number cur.  Put it in the correct place.  Return the new entry
2314  * number for cur.
2315  */
2316 adrbk_cntr_t
re_sort_particular_entry(AdrBk * ab,a_c_arg_t cur)2317 re_sort_particular_entry(AdrBk *ab, a_c_arg_t cur)
2318 {
2319     AdrBk_Entry  *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
2320     long small_enough;
2321     adrbk_cntr_t big_enough;
2322     adrbk_cntr_t new_entry_num;
2323     int (*cmp_func)(const qsort_t *, const qsort_t *);
2324 
2325     dprint((9, "- re_sort -\n"));
2326 
2327     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2328 					    cmp_ae_by_full_lists_last :
2329 	       (ab->sort_rule == AB_SORT_RULE_FULL) ?
2330 					    cmp_ae_by_full :
2331 	       (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2332 					    cmp_ae_by_nick_lists_last :
2333 	    /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2334 					    cmp_ae_by_nick;
2335 
2336     new_entry_num = (adrbk_cntr_t) cur;
2337 
2338     if(ab->sort_rule == AB_SORT_RULE_NONE)
2339       return(new_entry_num);
2340 
2341     ae_cur = adrbk_get_ae(ab, cur);
2342 
2343     if(cur > 0)
2344       ae_prev  = adrbk_get_ae(ab, cur - 1);
2345 
2346     if(cur < ab->count -1)
2347       ae_next  = adrbk_get_ae(ab, cur + 1);
2348 
2349     /*
2350      * A possible optimization here would be to implement some sort of
2351      * binary search to find where it goes instead of stepping through the
2352      * entries one at a time.
2353      */
2354     if(cur > 0 &&
2355        (*cmp_func)((qsort_t *)&ae_cur,(qsort_t *)&ae_prev) < 0){
2356 	/*--- Out of order, needs to be moved up ----*/
2357 	for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
2358 	  ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t) small_enough);
2359 	  if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_small_enough) >= 0)
2360 	    break;
2361 	}
2362 	new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
2363 	move_ab_entry(ab, cur, (a_c_arg_t) new_entry_num);
2364     }
2365     else if(cur < ab->count - 1 &&
2366 	(*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_next) > 0){
2367 	/*---- Out of order needs, to be moved towards end of list ----*/
2368 	for(big_enough = (adrbk_cntr_t)(cur + 2);
2369 	    big_enough < ab->count;
2370 	    big_enough++){
2371 	  ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t) big_enough);
2372 	  if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_big_enough) <= 0)
2373 	    break;
2374 	}
2375 	new_entry_num = big_enough - 1;
2376 	move_ab_entry(ab, cur, (a_c_arg_t) big_enough);
2377     }
2378 
2379     dprint((9, "- re_sort done -\n"));
2380 
2381     return(new_entry_num);
2382 }
2383 
2384 
2385 /*
2386  * Delete an entry from the address book
2387  *
2388  * Args: ab        -- the address book
2389  *       entry_num -- entry to delete
2390  *    save_deleted -- save deleted as a #DELETED- entry
2391  *     enable_intr -- tell adrbk_write to enable interrupt handling
2392  *        be_quiet -- tell adrbk_write to not do percent done messages
2393  *        write_it -- only do adrbk_write if this is set
2394  *
2395  * Result: returns:  0 if all went well
2396  *                  -1 if there is no such entry
2397  *                  -2 error writing address book, check errno
2398  */
2399 int
adrbk_delete(AdrBk * ab,a_c_arg_t entry_num,int save_deleted,int enable_intr,int be_quiet,int write_it)2400 adrbk_delete(AdrBk *ab, a_c_arg_t entry_num, int save_deleted, int enable_intr,
2401 	     int be_quiet, int write_it)
2402 {
2403     int retval = 0;
2404     int set_mangled = 0;
2405 
2406     dprint((3, "- adrbk_delete(%ld) -\n", (long)entry_num));
2407 
2408     if(!ab)
2409       return -2;
2410 
2411     delete_ab_entry(ab, entry_num, save_deleted);
2412     if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2413       exp_del_nth(ab->exp, entry_num);
2414 
2415     exp_del_nth(ab->selects, entry_num);
2416 
2417     if(write_it)
2418       retval = adrbk_write(ab, 0, NULL, &set_mangled, enable_intr, be_quiet);
2419 
2420     if(set_mangled)
2421       ps_global->mangled_screen = 1;
2422 
2423     return(retval);
2424 }
2425 
2426 
2427 /*
2428  * Delete an address out of an address list
2429  *
2430  * Args: ab    -- the address book
2431  *   entry_num -- the address list we are deleting from
2432  *       addr  -- address in above list to be deleted
2433  *
2434  * Result: 0: Deletion complete, address book written
2435  *        -1: Address for deletion not found
2436  *        -2: Error writing address book. Check errno.
2437  *
2438  * The address to be deleted is located by matching the string.
2439  */
2440 int
adrbk_listdel(AdrBk * ab,a_c_arg_t entry_num,char * addr)2441 adrbk_listdel(AdrBk *ab, a_c_arg_t entry_num, char *addr)
2442 {
2443     char **p, *to_free;
2444     AdrBk_Entry *ae;
2445     int ret;
2446     int set_mangled = 0;
2447 
2448     dprint((3, "- adrbk_listdel(%ld) -\n", (long) entry_num));
2449 
2450     if(!ab || entry_num >= ab->count)
2451       return -2;
2452 
2453     if(!addr)
2454       return -1;
2455 
2456     ae = adrbk_get_ae(ab, entry_num);
2457 
2458     if(ae->tag != List)
2459       return -1;
2460 
2461     for(p = ae->addr.list; *p; p++)
2462       if(strcmp(*p, addr) == 0)
2463         break;
2464 
2465     if(*p == NULL)
2466       return -1;
2467 
2468     /* note storage to be freed */
2469     if(*p != empty)
2470       to_free = *p;
2471     else
2472       to_free = NULL;
2473 
2474     /* slide all the entries below up (including NULL) */
2475     for(; *p; p++)
2476       *p = *(p+1);
2477 
2478     if(to_free)
2479       fs_give((void **) &to_free);
2480 
2481     ret = adrbk_write(ab, 0, NULL, &set_mangled, 1, 0);
2482 
2483     if(set_mangled)
2484       ps_global->mangled_screen = 1;
2485 
2486     return(ret);
2487 }
2488 
2489 
2490 /*
2491  * Delete all addresses out of an address list
2492  *
2493  * Args: ab    -- the address book
2494  *   entry_num -- the address list we are deleting from
2495  *
2496  * Result: 0: Deletion complete, address book written
2497  *        -1: Address for deletion not found
2498  *        -2: Error writing address book. Check errno.
2499  */
2500 int
adrbk_listdel_all(AdrBk * ab,a_c_arg_t entry_num)2501 adrbk_listdel_all(AdrBk *ab, a_c_arg_t entry_num)
2502 {
2503     char **p;
2504     AdrBk_Entry *ae;
2505 
2506     dprint((3, "- adrbk_listdel_all(%ld) -\n", (long) entry_num));
2507 
2508     if(!ab || entry_num >= ab->count)
2509       return -2;
2510 
2511     ae = adrbk_get_ae(ab, entry_num);
2512 
2513     if(ae->tag != List)
2514       return -1;
2515 
2516     /* free old list */
2517     for(p = ae->addr.list; p && *p; p++)
2518       if(*p != empty)
2519         fs_give((void **)p);
2520 
2521     if(ae->addr.list)
2522       fs_give((void **) &ae->addr.list);
2523 
2524     ae->addr.list = NULL;
2525 
2526     return 0;
2527 }
2528 
2529 
2530 /*
2531  * Add a list of addresses to an already existing address list
2532  *
2533  * Args: ab        -- the address book
2534  *       entry_num -- the address list we are adding to
2535  *       addrs     -- address list to be added
2536  *     enable_intr -- tell adrbk_write to enable interrupt handling
2537  *        be_quiet -- tell adrbk_write to not do percent done messages
2538  *        write_it -- only do adrbk_write if this is set
2539  *
2540  * Result: returns 0 : addition made, address book written
2541  *                -1 : addition to non-list attempted
2542  *                -2 : error writing address book -- check errno
2543  */
2544 int
adrbk_nlistadd(AdrBk * ab,a_c_arg_t entry_num,adrbk_cntr_t * new_entry_num,int * resort_happened,char ** addrs,int enable_intr,int be_quiet,int write_it)2545 adrbk_nlistadd(AdrBk *ab, a_c_arg_t entry_num, adrbk_cntr_t *new_entry_num,
2546 	       int *resort_happened, char **addrs,
2547 	       int enable_intr, int be_quiet, int write_it)
2548 {
2549     char **p;
2550     int    cur_size, size_of_additional_list, new_size;
2551     int    i, rc = 0;
2552     int    set_mangled = 0;
2553     AdrBk_Entry *ae;
2554 
2555     dprint((3, "- adrbk_nlistadd(%ld) -\n", (long) entry_num));
2556 
2557     if(!ab || entry_num >= ab->count)
2558       return -2;
2559 
2560     ae = adrbk_get_ae(ab, entry_num);
2561 
2562     if(ae->tag != List)
2563       return -1;
2564 
2565     /* count up size of existing list */
2566     for(p = ae->addr.list; p != NULL && *p != NULL; p++)
2567       ;/* do nothing */
2568 
2569     cur_size = p - ae->addr.list;
2570 
2571     /* count up size of new list */
2572     for(p = addrs; p != NULL && *p != NULL; p++)
2573       ;/* do nothing */
2574 
2575     size_of_additional_list = p - addrs;
2576     new_size = cur_size + size_of_additional_list;
2577 
2578     /* make room at end of list for it */
2579     if(cur_size == 0)
2580       ae->addr.list = (char **) fs_get(sizeof(char *) * (new_size + 1));
2581     else
2582       fs_resize((void **) &ae->addr.list, sizeof(char *) * (new_size + 1));
2583 
2584     /* Put new list at the end */
2585     for(i = cur_size; i < new_size; i++)
2586       (ae->addr.list)[i] = cpystr(addrs[i - cur_size]);
2587 
2588     (ae->addr.list)[new_size] = NULL;
2589 
2590     /*---- sort it into the correct place ------*/
2591     if(ab->sort_rule != AB_SORT_RULE_NONE)
2592       sort_addr_list(ae->addr.list);
2593 
2594     if(write_it)
2595       rc = adrbk_write(ab, entry_num, new_entry_num, &set_mangled, enable_intr, be_quiet);
2596 
2597     if(set_mangled){
2598 	ps_global->mangled_screen = 1;
2599 	if(resort_happened)
2600 	  *resort_happened = 1;
2601     }
2602 
2603     return(rc);
2604 }
2605 
2606 
2607 /*
2608  * Set the valid variable if we determine that the address book has
2609  * been changed by something other than us. This means we should update
2610  * our view of the address book when next possible.
2611  *
2612  * Args    ab -- AdrBk handle
2613  *  do_it_now -- If > 0, check now regardless
2614  *               If = 0, check if time since last chk more than default
2615  *               If < 0, check if time since last chk more than -do_it_now
2616  */
2617 void
adrbk_check_validity(AdrBk * ab,long int do_it_now)2618 adrbk_check_validity(AdrBk *ab, long int do_it_now)
2619 {
2620     dprint((9, "- adrbk_check_validity(%s) -\n",
2621 	   (ab && ab->filename) ? ab->filename : ""));
2622 
2623     if(!ab || ab->flags & FILE_OUTOFDATE)
2624       return;
2625 
2626     adrbk_check_local_validity(ab, do_it_now);
2627 
2628     if(ab->type == Imap && !(ab->flags & FILE_OUTOFDATE ||
2629 			     ab->rd->flags & REM_OUTOFDATE))
2630       rd_check_remvalid(ab->rd, do_it_now);
2631 }
2632 
2633 
2634 /*
2635  * Set the valid variable if we determine that the address book has
2636  * been changed by something other than us. This means we should update
2637  * our view of the address book when next possible.
2638  *
2639  * Args    ab -- AdrBk handle
2640  *  do_it_now -- If > 0, check now regardless
2641  *               If = 0, check if time since last chk more than default
2642  *               If < 0, check if time since last chk more than -do_it_now
2643  */
2644 void
adrbk_check_local_validity(AdrBk * ab,long int do_it_now)2645 adrbk_check_local_validity(AdrBk *ab, long int do_it_now)
2646 {
2647     time_t mtime, chk_interval;
2648 
2649     dprint((9, "- adrbk_check_local_validity(%s) -\n",
2650 	   (ab && ab->filename) ? ab->filename : ""));
2651 
2652     if(!ab)
2653       return;
2654 
2655     if(do_it_now < 0L){
2656 	chk_interval = -1L * do_it_now;
2657 	do_it_now = 0L;
2658     }
2659     else
2660       chk_interval = FILE_VALID_CHK_INTERVAL;
2661 
2662     if(!do_it_now &&
2663        get_adj_time() <= ab->last_local_valid_chk + chk_interval)
2664       return;
2665 
2666     ab->last_local_valid_chk = get_adj_time();
2667 
2668     /*
2669      * Check local file for a modification time change.
2670      * If this is out of date, don't even bother checking for the remote
2671      * folder being out of date. That will get fixed when we reopen.
2672      */
2673     if(!(ab->flags & FILE_OUTOFDATE) &&
2674        ab->last_change_we_know_about != (time_t)(-1) &&
2675        (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
2676        ab->last_change_we_know_about != mtime){
2677 
2678 	dprint((2, "adrbk_check_local_validity: addrbook %s has changed\n",
2679 		ab->filename ? ab->filename : "?"));
2680 	ab->flags |= FILE_OUTOFDATE;
2681     }
2682 }
2683 
2684 
2685 /*
2686  * See if we can re-use an existing stream.
2687  *
2688  * [ We don't believe we need this anymore now that the stuff in pine.c ]
2689  * [ is recycling streams for us, but we haven't thought it through all ]
2690  * [ the way enough to get rid of this. Hubert 2003-07-09               ]
2691  *
2692  * Args  name     -- Name of folder we want a stream for.
2693  *
2694  * Returns -- A mail stream suitable for status cmd or append cmd.
2695  *            NULL if none were available.
2696  */
2697 MAILSTREAM *
adrbk_handy_stream(char * name)2698 adrbk_handy_stream(char *name)
2699 {
2700     MAILSTREAM *stat_stream = NULL;
2701     int         i;
2702 
2703     dprint((9, "- adrbk_handy_stream(%s) -\n", name ? name : "?"));
2704 
2705     stat_stream = sp_stream_get(name, SP_SAME);
2706 
2707     /*
2708      * Look through our imap streams to see if there is one we can use.
2709      */
2710     for(i = 0; !stat_stream && i < as.n_addrbk; i++){
2711 	PerAddrBook *pab;
2712 
2713 	pab = &as.adrbks[i];
2714 
2715 	if(pab->address_book &&
2716 	   pab->address_book->type == Imap &&
2717 	   pab->address_book->rd &&
2718 	   pab->address_book->rd->type == RemImap &&
2719 	   same_stream(name, pab->address_book->rd->t.i.stream)){
2720 	    stat_stream = pab->address_book->rd->t.i.stream;
2721 	    pab->address_book->rd->last_use = get_adj_time();
2722 	    dprint((7,
2723 		   "%s: used other abook stream for status (%ld)\n",
2724 		   pab->address_book->orig_filename
2725 		     ? pab->address_book->orig_filename : "?",
2726 		   (long)pab->address_book->rd->last_use));
2727 	}
2728     }
2729 
2730     dprint((9, "adrbk_handy_stream: returning %s\n",
2731 	   stat_stream ? "good stream" : "NULL"));
2732 
2733     return(stat_stream);
2734 }
2735 
2736 
2737 /*
2738  * Close address book
2739  *
2740  * All that is done here is to free the storage, since the address book is
2741  * rewritten on every change.
2742  */
2743 void
adrbk_close(AdrBk * ab)2744 adrbk_close(AdrBk *ab)
2745 {
2746     int we_cancel = 0;
2747 
2748     dprint((4, "- adrbk_close(%s) -\n",
2749 	   (ab && ab->filename) ? ab->filename : ""));
2750 
2751     if(!ab)
2752       return;
2753 
2754     if(ab->rd)
2755       rd_close_remdata(&ab->rd);
2756 
2757     if(ab->fp)
2758       (void)fclose(ab->fp);
2759 
2760     if(ab->our_filecopy && ab->filename != ab->our_filecopy){
2761 	our_unlink(ab->our_filecopy);
2762 	fs_give((void**) &ab->our_filecopy);
2763     }
2764 
2765     if(ab->exp){
2766 	exp_free(ab->exp);
2767 	fs_give((void **)&ab->exp);  /* free head of list, too */
2768     }
2769 
2770     if(ab->checks){
2771 	exp_free(ab->checks);
2772 	fs_give((void **)&ab->checks);  /* free head of list, too */
2773     }
2774 
2775     if(ab->selects){
2776 	exp_free(ab->selects);
2777 	fs_give((void **)&ab->selects);  /* free head of list, too */
2778     }
2779 
2780     if(ab->arr){
2781 	adrbk_cntr_t entry_num;
2782 
2783 	/*
2784 	 * ab->arr is an allocated array. Each element of the array contains
2785 	 * several pointers that point to other allocated stuff, so we
2786 	 * free_ae_parts to get those and then free the array after the loop.
2787 	 */
2788 	for(entry_num = 0; entry_num < ab->count; entry_num++)
2789 	  free_ae_parts(&ab->arr[entry_num]);
2790 
2791 	fs_give((void **) &ab->arr);
2792     }
2793 
2794     if(ab->del){
2795 	adrbk_cntr_t entry_num;
2796 
2797 	for(entry_num = 0; entry_num < ab->del_count; entry_num++)
2798 	  free_ae_parts(&ab->del[entry_num]);
2799 
2800 	fs_give((void **) &ab->del);
2801     }
2802 
2803     if(ab->nick_trie)
2804       free_abook_trie(&ab->nick_trie);
2805 
2806     if(ab->addr_trie)
2807       free_abook_trie(&ab->addr_trie);
2808 
2809     if(ab->full_trie)
2810       free_abook_trie(&ab->full_trie);
2811 
2812     if(ab->revfull_trie)
2813       free_abook_trie(&ab->revfull_trie);
2814 
2815     if(we_cancel)
2816       cancel_busy_cue(0);
2817 
2818     if(ab->filename){
2819 	if(ab->flags & DEL_FILE)
2820 	  our_unlink(ab->filename);
2821 
2822 	fs_give((void**)&ab->filename);
2823     }
2824 
2825     if(ab->orig_filename)
2826       fs_give((void**)&ab->orig_filename);
2827 
2828     fs_give((void **) &ab);
2829 }
2830 
2831 
2832 void
adrbk_partial_close(AdrBk * ab)2833 adrbk_partial_close(AdrBk *ab)
2834 {
2835     dprint((4, "- adrbk_partial_close(%s) -\n",
2836 	   (ab && ab->filename) ? ab->filename : ""));
2837 
2838     exp_free(ab->exp);		/* leaves head of list */
2839     exp_free(ab->checks);	/* leaves head of list */
2840 }
2841 
2842 
2843 /*
2844  * It has been noticed that this stream is dead and it is about to
2845  * be removed from the stream pool. We may have a pointer to it for one
2846  * of the remote address books. We have to note that the pointer is stale.
2847  */
2848 void
note_closed_adrbk_stream(MAILSTREAM * stream)2849 note_closed_adrbk_stream(MAILSTREAM *stream)
2850 {
2851     PerAddrBook *pab;
2852     int i;
2853 
2854     if(!stream)
2855       return;
2856 
2857     for(i = 0; i < as.n_addrbk; i++){
2858 	pab = &as.adrbks[i];
2859 	if(pab->address_book
2860 	   && pab->address_book->type == Imap
2861 	   && pab->address_book->rd
2862 	   && pab->address_book->rd->type == RemImap
2863 	   && (stream == pab->address_book->rd->t.i.stream)){
2864 	    dprint((4, "- note_closed_adrbk_stream(%s) -\n",
2865 		   (pab->address_book && pab->address_book->orig_filename)
2866 		       ? pab->address_book->orig_filename : ""));
2867 	    pab->address_book->rd->t.i.stream = NULL;
2868 	}
2869     }
2870 }
2871 
2872 
2873 static adrbk_cntr_t tot_for_percent;
2874 static adrbk_cntr_t entry_num_for_percent;
2875 
2876 /*
2877  * Write out the address book.
2878  *
2879  * If be_quiet is set, don't turn on busy_cue.
2880  *
2881  * If enable_intr_handling is set, turn on and off interrupt handling.
2882  *
2883  * Format is as in comment in the adrbk_open routine.  Lines are wrapped
2884  * to be under 80 characters.  This is called on every change to the
2885  * address book.  Write is first to a temporary file,
2886  * which is then renamed to be the real address book so that we won't
2887  * destroy the real address book in case of something like a full file
2888  * system.
2889  *
2890  * Writing a temp file and then renaming has the bad side affect of
2891  * destroying links.  It also overrides any read only permissions on
2892  * the mail file since rename ignores such permissions.  However, we
2893  * handle readonly-ness in addrbook.c before we call this.
2894  * We retain the permissions by doing a stat on the old file and a
2895  * chmod on the new one (with file_attrib_copy).
2896  *
2897  * In pre-alpine pine the address book entries were encoded on disk.
2898  * We would be happy with raw UTF-8 now but in order to preserve some
2899  * backwards compatibility we encode the entries before writing.
2900  * We first try to translate to the user's character set and encode
2901  * in that, else we use UTF-8 but with 1522 encoding.
2902  *
2903  * Returns:   0 write was successful
2904  *           -2 write failed
2905  *           -5 interrupted
2906  */
2907 int
adrbk_write(AdrBk * ab,a_c_arg_t current_entry_num,adrbk_cntr_t * new_entry_num,int * sort_happened,int enable_intr_handling,int be_quiet)2908 adrbk_write(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num,
2909 	    int *sort_happened, int enable_intr_handling, int be_quiet)
2910 {
2911     FILE                  *ab_stream = NULL;
2912     AdrBk_Entry           *ae = NULL;
2913     adrbk_cntr_t           entry_num;
2914 #ifndef	DOS
2915     void                  (*save_sighup)();
2916 #endif
2917     int                   max_nick = 0,
2918 			  max_full = 0, full_two = 0, full_three = 0,
2919 			  max_fcc = 0, fcc_two = 0, fcc_three = 0,
2920 			  max_addr = 0, addr_two = 0, addr_three = 0,
2921 			  this_nick_width, this_full_width, this_addr_width,
2922 			  this_fcc_width, fd, i;
2923     int                   interrupt_happened = 0, we_cancel = 0, we_turned_on = 0;
2924     char                 *temp_filename = NULL;
2925     WIDTH_INFO_S         *widths;
2926 
2927     if(!ab)
2928       return -2;
2929 
2930     dprint((2, "- adrbk_write(\"%s\") - writing %lu entries\n",
2931 	ab->filename ? ab->filename : "", (unsigned long) ab->count));
2932 
2933     errno = 0;
2934 
2935     adrbk_check_local_validity(ab, 1L);
2936     /* verify that file has not been changed by something else */
2937     if(ab->flags & FILE_OUTOFDATE){
2938 	/* It has changed! */
2939 	q_status_message(SM_ORDER | SM_DING, 5, 15,
2940 	    /* TRANSLATORS: The address book was changed by something else so alpine
2941 	       is not making the change the user wanted to make to avoid damaging
2942 	       the address book. */
2943 	    _("Addrbook changed by another process, aborting our change to avoid damage..."));
2944 	dprint((1, "adrbk_write: addrbook %s changed while we had it open, aborting write\n",
2945 		ab->filename ? ab->filename : "?"));
2946 	longjmp(addrbook_changed_unexpectedly, 1);
2947 	/*NOTREACHED*/
2948     }
2949 
2950     /*
2951      * Verify that remote folder has not been
2952      * changed by something else (not if checked in last 5 seconds).
2953      *
2954      * The -5 is so we won't check every time on a series of quick writes.
2955      */
2956     rd_check_remvalid(ab->rd, -5L);
2957     if(ab->type == Imap){
2958 	int  ro;
2959 
2960 	/*
2961 	 * We'll eventually need this open to write to remote, so see if
2962 	 * we can open it now.
2963 	 */
2964 	rd_open_remote(ab->rd);
2965 
2966 	/*
2967 	 * Did someone else change the remote copy?
2968 	 */
2969 	if((ro=rd_remote_is_readonly(ab->rd)) || ab->rd->flags & REM_OUTOFDATE){
2970 	    if(ro == 1){
2971 		q_status_message(SM_ORDER | SM_DING, 5, 15,
2972 			    _("Can't access remote addrbook, aborting change..."));
2973 		dprint((1,
2974 			"adrbk_write: Can't write to remote addrbook %s, aborting write: open failed\n",
2975 			ab->rd->rn ? ab->rd->rn : "?"));
2976 	    }
2977 	    else if(ro == 2){
2978 		if(!(ab->rd->flags & NO_META_UPDATE)){
2979 		    unsigned long save_chk_nmsgs;
2980 
2981 		    /*
2982 		     * Should have some non-type-specific method of doing this.
2983 		     */
2984 		    switch(ab->rd->type){
2985 		      case RemImap:
2986 			save_chk_nmsgs = ab->rd->t.i.chk_nmsgs;
2987 			ab->rd->t.i.chk_nmsgs = 0;/* cause it to be OUTOFDATE */
2988 			rd_write_metadata(ab->rd, 0);
2989 			ab->rd->t.i.chk_nmsgs = save_chk_nmsgs;
2990 			break;
2991 
2992 		      default:
2993 			q_status_message(SM_ORDER | SM_DING, 3, 5,
2994 					 "Adrbk_write: Type not supported");
2995 			break;
2996 		    }
2997 		}
2998 
2999 		q_status_message(SM_ORDER | SM_DING, 5, 15,
3000 	     _("No write permission for remote addrbook, aborting change..."));
3001 	    }
3002 	    else{
3003 		q_status_message(SM_ORDER | SM_DING, 5, 15,
3004 	     _("Remote addrbook changed, aborting our change to avoid damage..."));
3005 		dprint((1,
3006 			"adrbk_write: remote addrbook %s changed while we had it open, aborting write\n",
3007 			ab->orig_filename ? ab->orig_filename : "?"));
3008 	    }
3009 
3010 	    rd_close_remote(ab->rd);
3011 
3012 	    longjmp(addrbook_changed_unexpectedly, 1);
3013 	    /*NOTREACHED*/
3014 	}
3015     }
3016 
3017 #ifndef	DOS
3018     save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
3019 #endif
3020 
3021     /*
3022      * If we want to be able to modify the address book, we will
3023      * need a temp_filename in the same directory as our_filecopy.
3024      */
3025     if(!(ab->our_filecopy && ab->our_filecopy[0])
3026        || !(temp_filename = tempfile_in_same_dir(ab->our_filecopy,"a1",NULL))
3027        || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3028 	dprint((1, "adrbk_write(%s): failed opening temp file (%s)\n",
3029 		ab->filename ? ab->filename : "?",
3030 		temp_filename ? temp_filename : "NULL"));
3031         goto io_error;
3032     }
3033 
3034     ab_stream = fdopen(fd, "wb");
3035     if(ab_stream == NULL){
3036 	dprint((1, "adrbk_write(%s): fdopen failed\n", temp_filename ? temp_filename : "?"));
3037         goto io_error;
3038     }
3039 
3040     if(adrbk_is_in_sort_order(ab, be_quiet)){
3041 	if(sort_happened)
3042 	  *sort_happened = 0;
3043 
3044 	if(new_entry_num)
3045 	  *new_entry_num = current_entry_num;
3046     }
3047     else{
3048 	if(sort_happened)
3049 	  *sort_happened = 1;
3050 
3051 	(void) adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet);
3052     }
3053 
3054     /* accept keyboard interrupts */
3055     if(enable_intr_handling)
3056       we_turned_on = intr_handling_on();
3057 
3058     if(!be_quiet){
3059 	tot_for_percent = MAX(ab->count, 1);
3060 	entry_num_for_percent = 0;
3061 	we_cancel = busy_cue(_("Saving address book"), percent_abook_saved, 0);
3062     }
3063 
3064     writing = 1;
3065 
3066     /*
3067      * If there are any old deleted entries, copy them to new file.
3068      */
3069     if(ab->del_count > 0){
3070 	char *nickname;
3071 	long  delindex;
3072 
3073 	/*
3074 	 * If there are deleted entries old enough that we no longer want
3075 	 * to save them, remove them from the list.
3076 	 */
3077 	for(delindex = (long) ab->del_count-1;
3078 	    delindex >= 0L && ab->del_count > 0; delindex--){
3079 
3080 	    ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3081 	    nickname = ae->nickname;
3082 
3083 	    if(strncmp(nickname, DELETED, DELETED_LEN) == 0
3084 	       && isdigit((unsigned char)nickname[DELETED_LEN])
3085 	       && isdigit((unsigned char)nickname[DELETED_LEN+1])
3086 	       && nickname[DELETED_LEN+2] == '/'
3087 	       && isdigit((unsigned char)nickname[DELETED_LEN+3])
3088 	       && isdigit((unsigned char)nickname[DELETED_LEN+4])
3089 	       && nickname[DELETED_LEN+5] == '/'
3090 	       && isdigit((unsigned char)nickname[DELETED_LEN+6])
3091 	       && isdigit((unsigned char)nickname[DELETED_LEN+7])
3092 	       && nickname[DELETED_LEN+8] == '#'){
3093 		int year, month, day;
3094 		struct tm *tm_before;
3095 		time_t     now, before;
3096 
3097 		now = time((time_t *)0);
3098 		before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
3099 		tm_before = localtime(&before);
3100 		tm_before->tm_mon++;
3101 
3102 		/*
3103 		 * Check to see if it is older than 100 days.
3104 		 */
3105 		year  = atoi(&nickname[DELETED_LEN]);
3106 		month = atoi(&nickname[DELETED_LEN+3]);
3107 		day   = atoi(&nickname[DELETED_LEN+6]);
3108 		if(year < 95)
3109 		  year += 100;  /* this breaks in year 2095 */
3110 
3111 		/*
3112 		 * remove it if it is more than 100 days old
3113 		 */
3114 		if(!(year > tm_before->tm_year
3115 		     || (year == tm_before->tm_year
3116 		         && month > tm_before->tm_mon)
3117 		     || (year == tm_before->tm_year
3118 		         && month == tm_before->tm_mon
3119 		         && day >= tm_before->tm_mday))){
3120 
3121 		    /* it's old, dump it */
3122 		    free_ae_parts(ae);
3123 		    ab->del_count--;
3124 		    /* patch the array by moving everything below up */
3125 		    if(delindex < ab->del_count){
3126 			memmove(&ab->del[delindex],
3127 			        &ab->del[delindex+1],
3128 				(ab->del_count - delindex) *
3129 							sizeof(AdrBk_Entry));
3130 		    }
3131 		}
3132 	    }
3133 	}
3134 
3135 	/* write out what remains */
3136 	if(ab->del_count){
3137 	    for(delindex = 0L; delindex < ab->del_count; delindex++){
3138 		ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3139 		if(write_single_abook_entry(ae, ab_stream, NULL, NULL,
3140 					    NULL, NULL) == EOF){
3141 		    dprint((1, "adrbk_write(%s): failed writing deleted entry\n",
3142 			    temp_filename ? temp_filename : "?"));
3143 		    goto io_error;
3144 		}
3145 	    }
3146 	}
3147 	else{
3148 	    /* nothing left, get rid of del array */
3149 	    fs_give((void **) &ab->del);
3150 	}
3151     }
3152 
3153     if(ab->del_count)
3154       dprint((4, "  adrbk_write: saving %ld deleted entries\n",
3155 			     (long) ab->del_count));
3156 
3157     for(entry_num = 0; entry_num < ab->count; entry_num++){
3158 	entry_num_for_percent++;
3159 
3160 	ALARM_BLIP();
3161 
3162 	ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
3163 
3164 	if(ae == (AdrBk_Entry *) NULL){
3165 	    dprint((1, "adrbk_write(%s): can't find ae while writing addrbook, entry_num = %ld\n",
3166 		    ab->filename ? ab->filename : "?",
3167 		    (long) entry_num));
3168 	    goto io_error;
3169 	}
3170 
3171 	/* write to temp file */
3172 	if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
3173 		&this_full_width, &this_addr_width, &this_fcc_width) == EOF){
3174 	    dprint((1, "adrbk_write(%s): failed writing for entry %ld\n",
3175 		    temp_filename ? temp_filename : "?",
3176 		    (long) entry_num));
3177 	    goto io_error;
3178 	}
3179 
3180 	/* keep track of widths */
3181 	max_nick = MAX(max_nick, this_nick_width);
3182 	if(this_full_width > max_full){
3183 	    full_three = full_two;
3184 	    full_two   = max_full;
3185 	    max_full   = this_full_width;
3186 	}
3187 	else if(this_full_width > full_two){
3188 	    full_three = full_two;
3189 	    full_two   = this_full_width;
3190 	}
3191 	else if(this_full_width > full_three){
3192 	    full_three = this_full_width;
3193 	}
3194 
3195 	if(this_addr_width > max_addr){
3196 	    addr_three = addr_two;
3197 	    addr_two   = max_addr;
3198 	    max_addr   = this_addr_width;
3199 	}
3200 	else if(this_addr_width > addr_two){
3201 	    addr_three = addr_two;
3202 	    addr_two   = this_addr_width;
3203 	}
3204 	else if(this_addr_width > addr_three){
3205 	    addr_three = this_addr_width;
3206 	}
3207 
3208 	if(this_fcc_width > max_fcc){
3209 	    fcc_three = fcc_two;
3210 	    fcc_two   = max_fcc;
3211 	    max_fcc   = this_fcc_width;
3212 	}
3213 	else if(this_fcc_width > fcc_two){
3214 	    fcc_three = fcc_two;
3215 	    fcc_two   = this_fcc_width;
3216 	}
3217 	else if(this_fcc_width > fcc_three){
3218 	    fcc_three = this_fcc_width;
3219 	}
3220 
3221 	/*
3222 	 * Check to see if we've been interrupted.  We check at the bottom
3223 	 * of the loop so that we can handle an interrupt in the last
3224 	 * iteration.
3225 	 */
3226 	if(enable_intr_handling && ps_global->intr_pending){
3227 	    interrupt_happened++;
3228 	    goto io_error;
3229 	}
3230     }
3231 
3232     if(we_cancel){
3233 	cancel_busy_cue(-1);
3234 	we_cancel = 0;
3235     }
3236 
3237     if(enable_intr_handling && we_turned_on){
3238 	intr_handling_off();
3239 	we_turned_on = 0;
3240     }
3241 
3242     if(fclose(ab_stream) == EOF){
3243 	dprint((1, "adrbk_write: fclose for %s failed\n",
3244 	       temp_filename ? temp_filename : "?"));
3245 	goto io_error;
3246     }
3247 
3248     ab_stream = (FILE *) NULL;
3249 
3250     file_attrib_copy(temp_filename, ab->our_filecopy);
3251     if(ab->fp){
3252 	(void) fclose(ab->fp);
3253 	ab->fp = NULL;			/* in case of problems */
3254     }
3255 
3256     if((i=rename_file(temp_filename, ab->our_filecopy)) < 0){
3257 	dprint((1, "adrbk_write: rename(%s, %s) failed: %s\n",
3258 		   temp_filename ? temp_filename : "?",
3259 		   ab->our_filecopy ? ab->our_filecopy : "?",
3260 		   error_description(errno)));
3261 #ifdef	_WINDOWS
3262 	if(i == -5){
3263 	    q_status_message2(SM_ORDER | SM_DING, 5, 7,
3264 			  _("Can't replace address book %.200sfile \"%.200s\""),
3265 			      (ab->type == Imap) ? "cache " : "",
3266 			      ab->filename);
3267 	    q_status_message(SM_ORDER | SM_DING, 5, 7,
3268     _("If another Alpine is running, quit that Alpine before updating address book."));
3269 	}
3270 #endif	/* _WINDOWS */
3271 	goto io_error;
3272     }
3273 
3274     /* reopen fp to new file */
3275     if(!(ab->fp = our_fopen(ab->our_filecopy, "rb"))){
3276 	dprint((1, "adrbk_write: can't reopen %s\n",
3277 	       ab->our_filecopy ? ab->our_filecopy : "?"));
3278 	goto io_error;
3279     }
3280 
3281     if(temp_filename){
3282 	our_unlink(temp_filename);
3283 	fs_give((void **) &temp_filename);
3284     }
3285 
3286     /*
3287      * Now copy our_filecopy back to filename.
3288      */
3289     if(ab->filename != ab->our_filecopy){
3290 	int   c, err = 0;
3291 	char *lc;
3292 
3293 	if(!(ab->filename && ab->filename[0])
3294 	   || !(temp_filename = tempfile_in_same_dir(ab->filename,"a1",NULL))
3295 	   || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3296 	    dprint((1,
3297 		    "adrbk_write(%s): failed opening temp file (%s)\n",
3298 		    ab->filename ? ab->filename : "?",
3299 		    temp_filename ? temp_filename : "NULL"));
3300 	    err++;
3301 	}
3302 
3303 	if(!err)
3304 	  ab_stream = fdopen(fd, "wb");
3305 
3306 	if(ab_stream == NULL){
3307 	    dprint((1, "adrbk_write(%s): fdopen failed\n",
3308 		    temp_filename ? temp_filename : "?"));
3309 	    err++;
3310 	}
3311 
3312 	if(!err){
3313 	    rewind(ab->fp);
3314 	    while((c = getc(ab->fp)) != EOF)
3315 	      if(putc(c, ab_stream) == EOF){
3316 		  err++;
3317 		  break;
3318 	      }
3319 	}
3320 
3321 	if(!err && fclose(ab_stream) == EOF)
3322 	  err++;
3323 
3324 	if(!err){
3325 #ifdef	_WINDOWS
3326 	    int tries = 3;
3327 #endif
3328 
3329 	    file_attrib_copy(temp_filename, ab->filename);
3330 
3331 	    /*
3332 	     * This could fail if somebody else has it open, but they should
3333 	     * only have it open for a short time.
3334 	     */
3335 	    while(!err && rename_file(temp_filename, ab->filename) < 0){
3336 #ifdef	_WINDOWS
3337 		if(i == -5){
3338 		    if(--tries <= 0)
3339 		      err++;
3340 
3341 		    if(!err){
3342 			q_status_message2(SM_ORDER, 0, 3,
3343 			      _("Replace of \"%.200s\" failed, trying %.200s"),
3344 			      (lc=last_cmpnt(ab->filename)) ? lc : ab->filename,
3345 			      (tries > 1) ? "again" : "one more time");
3346 			display_message('x');
3347 			sleep(3);
3348 		    }
3349 		}
3350 #else	/* UNIX */
3351 		err++;
3352 #endif	/* UNIX */
3353 	    }
3354 	}
3355 
3356 	if(err){
3357 	    q_status_message1(SM_ORDER | SM_DING, 5, 5,
3358 	       _("Copy of addrbook to \"%.200s\" failed, changes NOT saved!"),
3359 	       (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
3360 	    dprint((2, "adrbk_write: failed copying our_filecopy (%s)back to filename (%s): %s, continuing without a net\n",
3361 		   ab->our_filecopy ? ab->our_filecopy : "?",
3362 		   ab->filename ? ab->filename : "?",
3363 		   error_description(errno)));
3364 	}
3365     }
3366 
3367     widths = &ab->widths;
3368     widths->max_nickname_width  = MIN(max_nick, 99);
3369     widths->max_fullname_width  = MIN(max_full, 99);
3370     widths->max_addrfield_width = MIN(max_addr, 99);
3371     widths->max_fccfield_width  = MIN(max_fcc, 99);
3372     widths->third_biggest_fullname_width  = MIN(full_three, 99);
3373     widths->third_biggest_addrfield_width = MIN(addr_three, 99);
3374     widths->third_biggest_fccfield_width  = MIN(fcc_three, 99);
3375 
3376     /* record new change date of addrbook file */
3377     ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
3378 
3379 #ifndef	DOS
3380     (void)signal(SIGHUP, save_sighup);
3381 #endif
3382 
3383     /*
3384      * If this is a remote addressbook, copy the file over.
3385      * If it fails we warn but continue to operate on the changed,
3386      * locally cached addressbook file.
3387      */
3388     if(ab->type == Imap){
3389 	int   e;
3390 	char datebuf[200];
3391 
3392 	datebuf[0] = '\0';
3393 	if(we_cancel)
3394 	  cancel_busy_cue(-1);
3395 
3396 	we_cancel = busy_cue(_("Copying to remote addressbook"), NULL, 1);
3397 	/*
3398 	 * We don't want a cookie upgrade to blast our data in rd->lf by
3399 	 * copying back the remote data before doing the upgrade, and then
3400 	 * proceeding to finish with that data. So we tell rd_upgrade_cookie
3401 	 * about that here.
3402 	 */
3403 	ab->rd->flags |= BELIEVE_CACHE;
3404 	if((e = rd_update_remote(ab->rd, datebuf)) != 0){
3405 	    if(e == -1){
3406 		q_status_message2(SM_ORDER | SM_DING, 3, 5,
3407 			_("Error opening temporary addrbook file %.200s: %.200s"),
3408 				ab->rd->lf, error_description(errno));
3409 		dprint((1,
3410 		       "adrbk_write: error opening temp file %s\n",
3411 		       ab->rd->lf ? ab->rd->lf : "?"));
3412 	    }
3413 	    else{
3414 		q_status_message2(SM_ORDER | SM_DING, 3, 5,
3415 				_("Error copying to %.200s: %.200s"),
3416 				ab->rd->rn, error_description(errno));
3417 		dprint((1,
3418 		       "adrbk_write: error copying from %s to %s\n",
3419 		       ab->rd->lf ? ab->rd->lf : "?",
3420 		       ab->rd->rn ? ab->rd->rn : "?"));
3421 	    }
3422 
3423 	    q_status_message(SM_ORDER | SM_DING, 5, 5,
3424        _("Copy of addrbook to remote folder failed, changes NOT saved remotely"));
3425 	}
3426 	else{
3427 	    rd_update_metadata(ab->rd, datebuf);
3428 	    ab->rd->read_status = 'W';
3429 	    dprint((7,
3430 		   "%s: copied local to remote in adrbk_write (%ld)\n",
3431 		   ab->rd->rn ? ab->rd->rn : "?",
3432 		   (long)ab->rd->last_use));
3433 	}
3434 
3435 	ab->rd->flags &= ~BELIEVE_CACHE;
3436     }
3437 
3438     writing = 0;
3439 
3440     if(we_cancel)
3441       cancel_busy_cue(0);
3442 
3443     if(temp_filename){
3444 	our_unlink(temp_filename);
3445 	fs_give((void **) &temp_filename);
3446     }
3447 
3448     return 0;
3449 
3450 
3451 io_error:
3452     if(we_cancel)
3453       cancel_busy_cue(-1);
3454 
3455     if(enable_intr_handling && we_turned_on)
3456       intr_handling_off();
3457 
3458 #ifndef	DOS
3459     (void)signal(SIGHUP, save_sighup);
3460 #endif
3461     if(interrupt_happened){
3462 	q_status_message(0, 1, 2, _("Interrupt!  Reverting to previous version"));
3463 	display_message('x');
3464 	dprint((1, "adrbk_write(%s): Interrupt\n",
3465 		ab->filename ? ab->filename : "?"));
3466     }
3467     else
3468       dprint((1, "adrbk_write(%s) (%s): some sort of io_error\n",
3469 	      ab->filename ? ab->filename : "?",
3470 	      ab->our_filecopy ? ab->our_filecopy : "?"));
3471 
3472     writing = 0;
3473 
3474     if(ab_stream != NULL){
3475 	fclose(ab_stream);
3476 	ab_stream = (FILE *)NULL;
3477     }
3478 
3479     if(temp_filename){
3480 	our_unlink(temp_filename);
3481 	fs_give((void **) &temp_filename);
3482     }
3483 
3484     adrbk_partial_close(ab);
3485 
3486     if(interrupt_happened){
3487 	errno = EINTR;  /* for nicer error message */
3488 	return -5;
3489     }
3490     else
3491       return -2;
3492 }
3493 
3494 
3495 /*
3496  * Writes one addrbook entry with wrapping.  Fills in widths
3497  * for display purposes. Returns 0, or EOF on error.
3498  *
3499  * Continuation lines always start with spaces.  Tabs are treated as
3500  * separators, never as whitespace.  When we output tab separators we
3501  * always put them on the ends of lines, never on the start of a line
3502  * after a continuation.  That is, there is always something printable
3503  * after continuation spaces.
3504  */
3505 int
write_single_abook_entry(AdrBk_Entry * ae,FILE * fp,int * ret_nick_width,int * ret_full_width,int * ret_addr_width,int * ret_fcc_width)3506 write_single_abook_entry(AdrBk_Entry *ae, FILE *fp, int *ret_nick_width,
3507 			 int *ret_full_width, int *ret_addr_width, int *ret_fcc_width)
3508 {
3509     int len = 0;
3510     int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
3511     int tmplen, this_len;
3512     char *write_this = NULL;
3513 
3514     if(fp == (FILE *) NULL){
3515 	dprint((1, "write_single_abook_entry: fp is NULL\n"));
3516 	return(EOF);
3517     }
3518 
3519     if(ae->nickname){
3520 	nick_width = utf8_width(ae->nickname);
3521 
3522 	write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3523 			    tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->nickname);
3524 
3525 	this_len = strlen(write_this ? write_this : "");
3526 
3527 	/*
3528 	 * We aren't too concerned with where it wraps.
3529 	 * Long lines are ok as long as they aren't super long.
3530 	 */
3531 	if(len > 100 || (len+this_len > 150 && len > 20)){
3532 	    if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3533 		dprint((1,
3534 		    "write_single_abook_entry: fputs ind1 failed\n"));
3535 		return(EOF);
3536 	    }
3537 
3538 	    len = this_len + INDENT;
3539 	}
3540 
3541 	len += this_len;
3542 
3543 	if(fputs(write_this, fp) == EOF){
3544 	    dprint((1,
3545 		"write_single_abook_entry: fputs nick failed\n"));
3546 	    return(EOF);
3547 	}
3548     }
3549     else
3550       nick_width = 0;
3551 
3552     putc(TAB, fp);
3553     len++;
3554 
3555     if(ae->fullname){
3556 	full_width = utf8_width(ae->fullname);
3557 
3558 	write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3559 			    tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fullname);
3560 
3561 	this_len = strlen(write_this ? write_this : "");
3562 
3563 
3564 	if(len > 100 || (len+this_len > 150 && len > 20)){
3565 	    if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3566 		dprint((1,
3567 		    "write_single_abook_entry: fputs ind2 failed\n"));
3568 		return(EOF);
3569 	    }
3570 
3571 	    len = this_len + INDENT;
3572 	}
3573 
3574 	len += this_len;
3575 
3576 	if(fputs(write_this, fp) == EOF){
3577 	    dprint((1,
3578 		"write_single_abook_entry: fputs full failed\n"));
3579 	    return(EOF);
3580 	}
3581     }
3582     else
3583       full_width = 0;
3584 
3585     putc(TAB, fp);
3586     len++;
3587 
3588     /* special case, make sure empty list has () */
3589     if(ae->tag == List && ae->addr.list == NULL){
3590 	addr_width = 0;
3591 	putc('(', fp);
3592 	putc(')', fp);
3593 	len += 2;
3594     }
3595     else if(ae->addr.addr != NULL || ae->addr.list != NULL){
3596 	if(ae->tag == Single){
3597 	    /*----- Single: just one address ----*/
3598 	    if(ae->addr.addr){
3599 		addr_width = utf8_width(ae->addr.addr);
3600 
3601 		write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3602 				    tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->addr.addr);
3603 
3604 		this_len = strlen(write_this ? write_this : "");
3605 
3606 		if(len > 100 || (len+this_len > 150 && len > 20)){
3607 		    if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3608 			dprint((1,
3609 			    "write_single_abook_entry: fputs ind3 failed\n"));
3610 			return(EOF);
3611 		    }
3612 
3613 		    len = this_len + INDENT;
3614 		}
3615 
3616 		len += this_len;
3617 
3618 		if(fputs(write_this, fp) == EOF){
3619 		    dprint((1,
3620 			"write_single_abook_entry: fputs addr failed\n"));
3621 		    return(EOF);
3622 		}
3623 	    }
3624 	    else
3625 	      addr_width = 0;
3626 	}
3627 	else if(ae->tag == List){
3628 	    register char **a2;
3629 
3630 	    /*----- List: a distribution list ------*/
3631 	    putc('(', fp);
3632 	    len++;
3633 	    addr_width = 0;
3634 	    for(a2 = ae->addr.list; *a2 != NULL; a2++){
3635 		if(*a2){
3636 		    if(a2 != ae->addr.list){
3637 			putc(',', fp);
3638 			len++;
3639 		    }
3640 
3641 		    addr_width = MAX(addr_width, utf8_width(*a2));
3642 
3643 		    write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3644 					tmp_20k_buf+10000, SIZEOF_20KBUF-10000, *a2);
3645 
3646 		    this_len = strlen(write_this ? write_this : "");
3647 
3648 		    if(len > 100 || (len+this_len > 150 && len > 20)){
3649 			if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3650 			    dprint((1,
3651 				"write_single_abook_entry: fputs ind3 failed\n"));
3652 			    return(EOF);
3653 			}
3654 
3655 			len = this_len + INDENT;
3656 		    }
3657 
3658 		    len += this_len;
3659 
3660 		    if(fputs(write_this, fp) == EOF){
3661 			dprint((1,
3662 			    "write_single_abook_entry: fputs addrl failed\n"));
3663 			return(EOF);
3664 		    }
3665 		}
3666 	    }
3667 
3668 	    putc(')', fp);
3669 	    len++;
3670 	}
3671     }
3672 
3673     /* If either fcc or extra exists, output both, otherwise, neither */
3674     if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
3675 	putc(TAB, fp);
3676 	len++;
3677 
3678 	if(ae->fcc && ae->fcc[0]){
3679 	    fcc_width = utf8_width(ae->fcc);
3680 
3681 	    write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3682 				tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fcc);
3683 
3684 	    this_len = strlen(write_this ? write_this : "");
3685 
3686 	    if(len > 100 || (len+this_len > 150 && len > 20)){
3687 		if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3688 		    dprint((1,
3689 			"write_single_abook_entry: fputs ind4 failed\n"));
3690 		    return(EOF);
3691 		}
3692 
3693 		len = this_len + INDENT;
3694 	    }
3695 
3696 	    len += this_len;
3697 
3698 	    if(fputs(write_this, fp) == EOF){
3699 		dprint((1,
3700 		    "write_single_abook_entry: fputs fcc failed\n"));
3701 		return(EOF);
3702 	    }
3703 	}
3704 	else
3705 	  fcc_width = 0;
3706 
3707 	putc(TAB, fp);
3708 	len++;
3709 
3710 	if(ae->extra && ae->extra[0]){
3711 	    int space;
3712 	    char *cur, *end;
3713 	    char *extra_copy;
3714 
3715 	    write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3716 				tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->extra);
3717 
3718 	    /*
3719 	     * Copy ae->extra and replace newlines with spaces.
3720 	     * The reason we do this is because the continuation lines
3721 	     * produced by rfc1522_encode may interact with the
3722 	     * continuation lines produced below in a bad way.
3723 	     * In particular, what can happen is that the 1522 continuation
3724 	     * newline can be followed immediately by a newline produced
3725 	     * below. That breaks the continuation since that is a
3726 	     * line with nothing on it. Just turn 1522 continuations into
3727 	     * spaces, which work fine with 1522_decode.
3728 	     */
3729 	    extra_copy = cpystr(write_this);
3730 	    REPLACE_NEWLINES_WITH_SPACE(extra_copy);
3731 
3732 	    tmplen = strlen(extra_copy);
3733 
3734 	    if(len > 100 || (len+tmplen > 150 && len > 20)){
3735 		if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3736 		    dprint((1,
3737 			"write_single_abook_entry: fprintf indent failed\n"));
3738 		    return(EOF);
3739 		}
3740 
3741 		len = INDENT;
3742 	    }
3743 
3744 	    space = MAX(70 - len, 5);
3745 	    cur = extra_copy;
3746 	    end = cur + tmplen;
3747 	    while(cur < end){
3748 		if(end-cur > space){
3749 		    int i;
3750 
3751 		    /* find first space after spot we want to break */
3752 		    for(i = space; cur+i < end && cur[i] != SPACE; i++)
3753 		      ;
3754 
3755 		    cur[i] = '\0';
3756 		    if(fputs(cur, fp) == EOF){
3757 			dprint((1,
3758 			    "write_single_abook_entry: fputs extra failed\n"));
3759 			return(EOF);
3760 		    }
3761 
3762 		    cur += (i+1);
3763 
3764 		    if(cur < end){
3765 			if(fprintf(fp, "%s%s", NEWLINE, INDENTXTRA) == EOF){
3766 			    dprint((1,
3767 			"write_single_abook_entry: fprintf indent failed\n"));
3768 			    return(EOF);
3769 			}
3770 
3771 			space = 70 - INDENT;
3772 		    }
3773 		}
3774 		else{
3775 		    if(fputs(cur, fp) == EOF){
3776 			dprint((1,
3777 			    "write_single_abook_entry: fputs extra failed\n"));
3778 			return(EOF);
3779 		    }
3780 
3781 		    cur = end;
3782 		}
3783 	    }
3784 
3785 	    if(extra_copy)
3786 	      fs_give((void **)&extra_copy);
3787 	}
3788     }
3789     else
3790       fcc_width = 0;
3791 
3792     fprintf(fp, "%s", NEWLINE);
3793 
3794     if(ret_nick_width)
3795       *ret_nick_width = nick_width;
3796     if(ret_full_width)
3797       *ret_full_width = full_width;
3798     if(ret_addr_width)
3799       *ret_addr_width = addr_width;
3800     if(ret_fcc_width)
3801       *ret_fcc_width = fcc_width;
3802 
3803     return(0);
3804 }
3805 
3806 
3807 char *
backcompat_encoding_for_abook(char * buf1,size_t buf1len,char * buf2,size_t buf2len,char * srcstr)3808 backcompat_encoding_for_abook(char *buf1, size_t buf1len, char *buf2,
3809 			      size_t buf2len, char *srcstr)
3810 {
3811     char *encoded = NULL;
3812     char *p;
3813     int its_ascii = 1;
3814 
3815 
3816     for(p = srcstr; *p && its_ascii; p++)
3817       if(*p & 0x80)
3818 	its_ascii = 0;
3819 
3820     /* if it is ascii, go with that */
3821     if(its_ascii)
3822       encoded = srcstr;
3823     else{
3824 	char *trythischarset = NULL;
3825 
3826 	/*
3827 	 * If it is possible to translate the UTF-8
3828 	 * string into the user's character set then
3829 	 * do that. For backwards compatibility with
3830 	 * old pines.
3831 	 */
3832 	if(ps_global->keyboard_charmap && ps_global->keyboard_charmap[0])
3833 	  trythischarset = ps_global->keyboard_charmap;
3834 	else if(ps_global->display_charmap && ps_global->display_charmap[0])
3835 	  trythischarset = ps_global->display_charmap;
3836 
3837 	if(trythischarset){
3838 	    SIZEDTEXT src, dst;
3839 
3840 	    src.data = (unsigned char *) srcstr;
3841 	    src.size = strlen(srcstr);
3842 	    memset(&dst, 0, sizeof(dst));
3843 	    if(utf8_cstext(&src, trythischarset, &dst, 0)){
3844 		if(dst.data){
3845 		    strncpy(buf1, (char *) dst.data, buf1len);
3846 		    buf1[buf1len-1] = '\0';
3847 		    fs_give((void **) &dst.data);
3848 		    encoded = rfc1522_encode(buf2, buf2len,
3849 					     (unsigned char *) buf1, trythischarset);
3850 		    if(encoded)
3851 		      REPLACE_NEWLINES_WITH_SPACE(encoded);
3852 		}
3853 	    }
3854 	}
3855 
3856 	if(!encoded){
3857 	    encoded = rfc1522_encode(buf1, buf1len, (unsigned char *) srcstr, "UTF-8");
3858 	    if(encoded)
3859 	      REPLACE_NEWLINES_WITH_SPACE(encoded);
3860 	}
3861     }
3862 
3863     return(encoded);
3864 }
3865 
3866 
3867 int
percent_abook_saved(void)3868 percent_abook_saved(void)
3869 {
3870     return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
3871 	(unsigned long)tot_for_percent));
3872 }
3873 
3874 
3875 /*
3876  * Free memory associated with entry ae.
3877  *
3878  * Args:  ae  -- Address book entry to be freed.
3879  */
3880 void
free_ae(AdrBk_Entry ** ae)3881 free_ae(AdrBk_Entry **ae)
3882 {
3883     if(!ae || !(*ae))
3884       return;
3885 
3886     free_ae_parts(*ae);
3887     fs_give((void **) ae);
3888 }
3889 
3890 
3891 /*
3892  * Free memory associated with entry ae but not ae itself.
3893  */
3894 void
free_ae_parts(AdrBk_Entry * ae)3895 free_ae_parts(AdrBk_Entry *ae)
3896 {
3897     char **p;
3898 
3899     if(!ae)
3900       return;
3901 
3902     if(ae->nickname && ae->nickname != empty)
3903       fs_give((void **) &ae->nickname);
3904 
3905     if(ae->fullname && ae->fullname != empty)
3906       fs_give((void **) &ae->fullname);
3907 
3908     if(ae->tag == Single){
3909         if(ae->addr.addr && ae->addr.addr != empty)
3910           fs_give((void **) &ae->addr.addr);
3911     }
3912     else if(ae->tag == List){
3913         if(ae->addr.list){
3914             for(p = ae->addr.list; *p; p++)
3915               if(*p != empty)
3916 	        fs_give((void **) p);
3917 
3918             fs_give((void **) &ae->addr.list);
3919         }
3920     }
3921 
3922     if(ae->fcc && ae->fcc != empty)
3923       fs_give((void **) &ae->fcc);
3924 
3925     if(ae->extra && ae->extra != empty)
3926       fs_give((void **) &ae->extra);
3927 
3928     defvalue_ae(ae);
3929 }
3930 
3931 
3932 /*
3933  * Inserts element new_ae before element put_it_before_this.
3934  */
3935 void
insert_ab_entry(AdrBk * ab,a_c_arg_t put_it_before_this,AdrBk_Entry * new_ae,int use_deleted_list)3936 insert_ab_entry(AdrBk *ab, a_c_arg_t put_it_before_this, AdrBk_Entry *new_ae,
3937 		int use_deleted_list)
3938 {
3939     adrbk_cntr_t before, old_count, new_count;
3940     AdrBk_Entry *ae_before, *ae_before_next;
3941 
3942     dprint((7, "- insert_ab_entry(before_this=%ld) -\n", (long) put_it_before_this));
3943 
3944     before = (adrbk_cntr_t) put_it_before_this;
3945 
3946     if(!ab || before == NO_NEXT || (!use_deleted_list && before > ab->count)
3947        || (use_deleted_list && before > ab->del_count)){
3948 	;
3949     }
3950     else{
3951 	/*
3952 	 * add space for new entry to array
3953 	 * slide entries [before ... old_count-1] down one
3954 	 * put new_ae into opened up slot
3955 	 */
3956 	old_count = use_deleted_list ? ab->del_count : ab->count;
3957 	new_count = old_count + 1;
3958 	if(old_count == 0){
3959 	    if(use_deleted_list){
3960 		if(ab->del)		/* shouldn't happen */
3961 		  fs_give((void **) &ab->del);
3962 
3963 		/* first entry in new array */
3964 		ab->del = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3965 		defvalue_ae(ab->del);
3966 	    }
3967 	    else{
3968 		if(ab->arr)			/* shouldn't happen */
3969 		  fs_give((void **) &ab->arr);
3970 
3971 		/* first entry in new array */
3972 		ab->arr = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3973 		defvalue_ae(ab->arr);
3974 	    }
3975 	}
3976 	else{
3977 	    if(use_deleted_list){
3978 		fs_resize((void **) &ab->del, new_count * sizeof(AdrBk_Entry));
3979 		defvalue_ae(&ab->del[new_count-1]);
3980 	    }
3981 	    else{
3982 		fs_resize((void **) &ab->arr, new_count * sizeof(AdrBk_Entry));
3983 		defvalue_ae(&ab->arr[new_count-1]);
3984 	    }
3985 	}
3986 
3987 	if(use_deleted_list){
3988 	    ab->del_count = new_count;
3989 
3990 	    ae_before = adrbk_get_delae(ab, (a_c_arg_t) before);
3991 	    if(ae_before){
3992 		if(before < old_count){
3993 		    ae_before_next = adrbk_get_delae(ab, (a_c_arg_t) (before+1));
3994 		    memmove(ae_before_next, ae_before,
3995 			    (old_count-before) * sizeof(AdrBk_Entry));
3996 		}
3997 
3998 		memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
3999 	    }
4000 	}
4001 	else{
4002 	    ab->count = new_count;
4003 
4004 	    ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4005 	    if(ae_before){
4006 		if(before < old_count){
4007 		    ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4008 		    memmove(ae_before_next, ae_before,
4009 			    (old_count-before) * sizeof(AdrBk_Entry));
4010 		}
4011 
4012 		memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
4013 	    }
4014 	}
4015 
4016     }
4017 
4018     if(!use_deleted_list)
4019       repair_abook_tries(ab);
4020 }
4021 
4022 
4023 /*
4024  * Moves element move_this_one before element put_it_before_this.
4025  */
4026 void
move_ab_entry(AdrBk * ab,a_c_arg_t move_this_one,a_c_arg_t put_it_before_this)4027 move_ab_entry(AdrBk *ab, a_c_arg_t move_this_one, a_c_arg_t put_it_before_this)
4028 {
4029     adrbk_cntr_t m, before;
4030     AdrBk_Entry  ae_tmp;
4031     AdrBk_Entry *ae_m, *ae_m_next, *ae_before, *ae_before_prev, *ae_before_next;
4032 
4033     dprint((7, "- move_ab_entry(move_this=%ld,before_this=%ld) -\n", (long) move_this_one, (long) put_it_before_this));
4034 
4035     m      = (adrbk_cntr_t) move_this_one;
4036     before = (adrbk_cntr_t) put_it_before_this;
4037 
4038     if(!ab || m == NO_NEXT || before == NO_NEXT
4039        || m == before || m+1 == before || before > ab->count){
4040 	;
4041     }
4042     else if(m+1 < before){
4043 	/*
4044 	 * copy m
4045 	 * slide entries [m+1 ... before-1] up one ("up" means smaller indices)
4046 	 * put m into opened up slot
4047 	 */
4048 	ae_m  = adrbk_get_ae(ab, (a_c_arg_t) m);
4049 	ae_m_next  = adrbk_get_ae(ab, (a_c_arg_t) (m+1));
4050 	ae_before_prev  = adrbk_get_ae(ab, (a_c_arg_t) (before-1));
4051 	if(ae_m && ae_m_next && ae_before_prev){
4052 	    memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4053 	    memmove(ae_m, ae_m_next, (before-m-1) * sizeof(ae_tmp));
4054 	    memcpy(ae_before_prev, &ae_tmp, sizeof(ae_tmp));
4055 	}
4056     }
4057     else if(m > before){
4058 	/*
4059 	 * copy m
4060 	 * slide entries [before ... m-1] down one
4061 	 * put m into opened up slot
4062 	 */
4063 	ae_m  = adrbk_get_ae(ab, (a_c_arg_t) m);
4064 	ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4065 	ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4066 	if(ae_m && ae_before && ae_before_next){
4067 	    memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4068 	    memmove(ae_before_next, ae_before, (m-before) * sizeof(ae_tmp));
4069 	    memcpy(ae_before, &ae_tmp, sizeof(ae_tmp));
4070 	}
4071     }
4072 
4073     dprint((9, "- move_ab_entry: done -\n"));
4074 
4075     repair_abook_tries(ab);
4076 }
4077 
4078 
4079 /*
4080  * Deletes element delete_this_one from in-core data structure.
4081  * If save_it is set the deleted entry is moved to the deleted_list.
4082  */
4083 void
delete_ab_entry(AdrBk * ab,a_c_arg_t delete_this_one,int save_it)4084 delete_ab_entry(AdrBk *ab, a_c_arg_t delete_this_one, int save_it)
4085 {
4086     adrbk_cntr_t d;
4087     AdrBk_Entry *ae_deleted, *ae_deleted_next;
4088 
4089     dprint((7, "- delete_ab_entry(delete_this=%ld,save_it=%d) -\n", (long) delete_this_one, save_it));
4090 
4091     d = (adrbk_cntr_t) delete_this_one;
4092 
4093     if(!ab || d == NO_NEXT || d >= ab->count){
4094 	;
4095     }
4096     else{
4097 	/*
4098 	 * Move the entry to the deleted_list if asked to.
4099 	 */
4100 	ae_deleted  = adrbk_get_ae(ab, (a_c_arg_t) d);
4101 	if(ae_deleted){
4102 	  if(save_it){
4103 	    char *oldnick, *newnick;
4104 	    size_t len;
4105 	    struct tm *tm_now;
4106 	    time_t now;
4107 
4108 	    /*
4109 	     * First prepend the prefix
4110 	     * #DELETED-YY/MM/DD#
4111 	     * to the nickname.
4112 	     */
4113 	    now = time((time_t *) NULL);
4114 	    tm_now = localtime(&now);
4115 
4116 	    oldnick = ae_deleted->nickname;
4117 	    len = strlen(oldnick) + DELETED_LEN + strlen("YY/MM/DD#");
4118 
4119 	    newnick = (char *) fs_get((len+1) * sizeof(char));
4120 	    snprintf(newnick, len+1, "%s%02d/%02d/%02d#%s",
4121 		     DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1,
4122 		     tm_now->tm_mday, oldnick ? oldnick : "");
4123 	    newnick[len] = '\0';
4124 
4125 	    if(ae_deleted->nickname && ae_deleted->nickname != empty)
4126 	      fs_give((void **) &ae_deleted->nickname);
4127 
4128 	    ae_deleted->nickname = newnick;
4129 
4130 	    /*
4131 	     * Now insert this entry in the deleted_list.
4132 	     */
4133 	    insert_ab_entry(ab, (a_c_arg_t) ab->del_count, ae_deleted, 1);
4134 	  }
4135 	  else
4136 	    free_ae_parts(ae_deleted);
4137 	}
4138 
4139 	/*
4140 	 * slide entries [deleted+1 ... count-1] up one
4141 	 */
4142 	if(d+1 < ab->count){
4143 	    ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
4144 	    ae_deleted_next = adrbk_get_ae(ab, (a_c_arg_t) (d+1));
4145 	    if(ae_deleted && ae_deleted_next)
4146 	      memmove(ae_deleted, ae_deleted_next, (ab->count-d-1) * sizeof(AdrBk_Entry));
4147 	}
4148 
4149 	ab->count--;
4150 
4151 	if(ab->count > 0)
4152 	  fs_resize((void **) &ab->arr, ab->count * sizeof(AdrBk_Entry));
4153 	else
4154 	  fs_give((void **) &ab->arr);
4155     }
4156 
4157     repair_abook_tries(ab);
4158 }
4159 
4160 
4161 /*
4162  * We may want to be smarter about this and repair instead of
4163  * rebuild these.
4164  */
4165 void
repair_abook_tries(AdrBk * ab)4166 repair_abook_tries(AdrBk *ab)
4167 {
4168     if(ab->arr){
4169 	AdrBk_Trie *save_nick_trie, *save_addr_trie,
4170 		   *save_full_trie, *save_revfull_trie;
4171 
4172 	save_nick_trie = ab->nick_trie;
4173 	ab->nick_trie = NULL;
4174 	save_addr_trie = ab->addr_trie;
4175 	ab->addr_trie = NULL;
4176 	save_full_trie = ab->full_trie;
4177 	ab->full_trie = NULL;
4178 	save_revfull_trie = ab->revfull_trie;
4179 	ab->revfull_trie = NULL;
4180 	if(build_abook_tries(ab, NULL)){
4181 	    dprint((2, "trouble rebuilding tries, restoring\n"));
4182 	    if(ab->nick_trie)
4183 	      free_abook_trie(&ab->nick_trie);
4184 
4185 	    /* better than nothing */
4186 	    ab->nick_trie = save_nick_trie;
4187 
4188 	    if(ab->addr_trie)
4189 	      free_abook_trie(&ab->addr_trie);
4190 
4191 	    ab->addr_trie = save_addr_trie;
4192 
4193 	    if(ab->full_trie)
4194 	      free_abook_trie(&ab->full_trie);
4195 
4196 	    ab->full_trie = save_full_trie;
4197 
4198 	    if(ab->revfull_trie)
4199 	      free_abook_trie(&ab->revfull_trie);
4200 
4201 	    ab->revfull_trie = save_revfull_trie;
4202 	}
4203 	else{
4204 	    if(save_nick_trie)
4205 	      free_abook_trie(&save_nick_trie);
4206 
4207 	    if(save_addr_trie)
4208 	      free_abook_trie(&save_addr_trie);
4209 
4210 	    if(save_full_trie)
4211 	      free_abook_trie(&save_full_trie);
4212 
4213 	    if(save_revfull_trie)
4214 	      free_abook_trie(&save_revfull_trie);
4215 	}
4216     }
4217 }
4218 
4219 
4220 /*
4221  * Free the list of distribution lists which have been expanded.
4222  * Leaves the head of the list alone.
4223  *
4224  * Args:  exp_head -- Head of the expanded list.
4225  */
4226 void
exp_free(EXPANDED_S * exp_head)4227 exp_free(EXPANDED_S *exp_head)
4228 {
4229     EXPANDED_S *e, *the_next_one;
4230 
4231     e = exp_head ? exp_head->next : NULL;
4232 
4233     if(!e)
4234       return;
4235 
4236     while(e){
4237 	the_next_one = e->next;
4238 	fs_give((void **)&e);
4239 	e = the_next_one;
4240     }
4241 
4242     exp_head->next = (EXPANDED_S *)NULL;
4243 }
4244 
4245 
4246 /*
4247  * Is entry n expanded?
4248  *
4249  * Args:  exp_head -- Head of the expanded list.
4250  *        n        -- The entry num to check
4251  */
4252 int
exp_is_expanded(EXPANDED_S * exp_head,a_c_arg_t n)4253 exp_is_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4254 {
4255     register EXPANDED_S *e;
4256     adrbk_cntr_t nn;
4257 
4258     nn = (adrbk_cntr_t)n;
4259 
4260     e = exp_head ? exp_head->next : NULL;
4261 
4262     /*
4263      * The list is kept ordered, so we search until we find it or are
4264      * past it.
4265      */
4266     while(e){
4267 	if(e->ent >= nn)
4268 	  break;
4269 
4270 	e = e->next;
4271     }
4272 
4273     return(e && e->ent == nn);
4274 }
4275 
4276 
4277 /*
4278  * How many entries expanded in this addrbook.
4279  *
4280  * Args:  exp_head -- Head of the expanded list.
4281  */
4282 int
exp_howmany_expanded(EXPANDED_S * exp_head)4283 exp_howmany_expanded(EXPANDED_S *exp_head)
4284 {
4285     register EXPANDED_S *e;
4286     int                  cnt = 0;
4287 
4288     e = exp_head ? exp_head->next : NULL;
4289 
4290     while(e){
4291 	cnt++;
4292 	e = e->next;
4293     }
4294 
4295     return(cnt);
4296 }
4297 
4298 
4299 /*
4300  * Are any entries expanded?
4301  *
4302  * Args:  exp_head -- Head of the expanded list.
4303  */
4304 int
exp_any_expanded(EXPANDED_S * exp_head)4305 exp_any_expanded(EXPANDED_S *exp_head)
4306 {
4307     return(exp_head && exp_head->next != NULL);
4308 }
4309 
4310 
4311 /*
4312  * Return next entry num in list.
4313  *
4314  * Args:  cur -- Current position in the list.
4315  *
4316  * Result: Returns the number of the next entry, or NO_NEXT if there is
4317  *	   no next entry.  As a side effect, the cur pointer is incremented.
4318  */
4319 adrbk_cntr_t
exp_get_next(EXPANDED_S ** cur)4320 exp_get_next(EXPANDED_S **cur)
4321 {
4322     adrbk_cntr_t ret = NO_NEXT;
4323 
4324     if(cur && *cur && (*cur)->next){
4325 	ret  = (*cur)->next->ent;
4326 	*cur = (*cur)->next;
4327     }
4328 
4329     return(ret);
4330 }
4331 
4332 
4333 /*
4334  * Mark entry n as being expanded.
4335  *
4336  * Args:  exp_head -- Head of the expanded list.
4337  *        n        -- The entry num to mark
4338  */
4339 void
exp_set_expanded(EXPANDED_S * exp_head,a_c_arg_t n)4340 exp_set_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4341 {
4342     register EXPANDED_S *e;
4343     EXPANDED_S *new;
4344     adrbk_cntr_t nn;
4345 
4346     nn = (adrbk_cntr_t)n;
4347     if(!exp_head)
4348       alpine_panic("exp_head not set in exp_set_expanded");
4349 
4350     for(e = exp_head; e->next; e = e->next)
4351       if(e->next->ent >= nn)
4352 	break;
4353 
4354     if(e->next && e->next->ent == nn) /* already there */
4355       return;
4356 
4357     /* add new after e */
4358     new       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
4359     new->ent  = nn;
4360     new->next = e->next;
4361     e->next   = new;
4362 }
4363 
4364 
4365 /*
4366  * Mark entry n as being *not* expanded.
4367  *
4368  * Args:  exp_head -- Head of the expanded list.
4369  *        n        -- The entry num to mark
4370  */
4371 void
exp_unset_expanded(EXPANDED_S * exp_head,a_c_arg_t n)4372 exp_unset_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4373 {
4374     register EXPANDED_S *e;
4375     EXPANDED_S *delete_this_one = NULL;
4376     adrbk_cntr_t nn;
4377 
4378     nn = (adrbk_cntr_t)n;
4379     if(!exp_head)
4380       alpine_panic("exp_head not set in exp_unset_expanded");
4381 
4382     for(e = exp_head; e->next; e = e->next)
4383       if(e->next->ent >= nn)
4384 	break;
4385 
4386     if(e->next && e->next->ent == nn){
4387 	delete_this_one = e->next;
4388 	e->next = e->next->next;
4389     }
4390 
4391     if(delete_this_one)
4392       fs_give((void **)&delete_this_one);
4393 }
4394 
4395 
4396 /*
4397  * Adjust the "expanded" list to correspond to addrbook entry n being
4398  * deleted.
4399  *
4400  * Args:  exp_head -- Head of the expanded list.
4401  *        n        -- The entry num being deleted
4402  */
4403 void
exp_del_nth(EXPANDED_S * exp_head,a_c_arg_t n)4404 exp_del_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4405 {
4406     register EXPANDED_S *e;
4407     int delete_when_done = 0;
4408     adrbk_cntr_t nn;
4409 
4410     nn = (adrbk_cntr_t)n;
4411     if(!exp_head)
4412       alpine_panic("exp_head not set in exp_del_nth");
4413 
4414     e = exp_head->next;
4415     while(e && e->ent < nn)
4416       e = e->next;
4417 
4418     if(e){
4419 	if(e->ent == nn){
4420 	    delete_when_done++;
4421 	    e = e->next;
4422 	}
4423 
4424 	while(e){
4425 	    e->ent--; /* adjust entry nums */
4426 	    e = e->next;
4427 	}
4428 
4429 	if(delete_when_done)
4430 	  exp_unset_expanded(exp_head, n);
4431     }
4432 }
4433 
4434 
4435 /*
4436  * Adjust the "expanded" list to correspond to a new addrbook entry being
4437  * added between current entries n-1 and n.
4438  *
4439  * Args:  exp_head -- Head of the expanded list.
4440  *        n        -- The entry num being added
4441  *
4442  * The new entry is not marked expanded.
4443  */
4444 void
exp_add_nth(EXPANDED_S * exp_head,a_c_arg_t n)4445 exp_add_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4446 {
4447     register EXPANDED_S *e;
4448     adrbk_cntr_t nn;
4449 
4450     nn = (adrbk_cntr_t)n;
4451     if(!exp_head)
4452       alpine_panic("exp_head not set in exp_add_nth");
4453 
4454     e = exp_head->next;
4455     while(e && e->ent < nn)
4456       e = e->next;
4457 
4458     while(e){
4459 	e->ent++; /* adjust entry nums */
4460 	e = e->next;
4461     }
4462 }
4463 
4464 
4465 static AdrBk *ab_for_sort;
4466 
4467 /*
4468  * Compare two address book entries.  Args are AdrBk_Entry **'s.
4469  * Sorts lists after simple addresses and then sorts on Fullname field.
4470  */
4471 int
cmp_ae_by_full_lists_last(const qsort_t * a,const qsort_t * b)4472 cmp_ae_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4473 {
4474     AdrBk_Entry **x = (AdrBk_Entry **)a,
4475                 **y = (AdrBk_Entry **)b;
4476     int result;
4477 
4478     if((*x)->tag == List && (*y)->tag == Single)
4479       result = 1;
4480     else if((*x)->tag == Single && (*y)->tag == List)
4481       result = -1;
4482     else{
4483 	register char *p, *q, *r, *s;
4484 
4485 	p = (*x)->fullname;
4486 	if(*p == '"' && *(p+1))
4487 	  p++;
4488 
4489 	q = (*y)->fullname;
4490 	if(*q == '"' && *(q+1))
4491 	  q++;
4492 
4493 	r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4494 					   SIZEOF_20KBUF, p);
4495 
4496 	s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4497 					   SIZEOF_20KBUF-10000, q);
4498 
4499 	result = (*pcollator)(r, s);
4500 	if(result == 0)
4501 	  result = (*pcollator)((*x)->nickname, (*y)->nickname);
4502     }
4503 
4504     return(result);
4505 }
4506 
4507 
4508 /*
4509  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
4510  * Sorts lists after simple addresses and then sorts on Fullname field.
4511  */
4512 int
cmp_cntr_by_full_lists_last(const qsort_t * a,const qsort_t * b)4513 cmp_cntr_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4514 {
4515     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
4516                  *y = (adrbk_cntr_t *)b;
4517     AdrBk_Entry  *x_ae,
4518 		 *y_ae;
4519 
4520     if(ps_global->intr_pending)
4521       longjmp(jump_over_qsort, 1);
4522 
4523     ALARM_BLIP();
4524 
4525     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4526     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4527 
4528     return(cmp_ae_by_full_lists_last((const qsort_t *) &x_ae,
4529 				     (const qsort_t *) &y_ae));
4530 }
4531 
4532 
4533 /*
4534  * Compare two address book entries.  Args are AdrBk_Entry **'s.
4535  * Sorts on Fullname field.
4536  */
4537 int
cmp_ae_by_full(const qsort_t * a,const qsort_t * b)4538 cmp_ae_by_full(const qsort_t *a, const qsort_t *b)
4539 {
4540     AdrBk_Entry **x = (AdrBk_Entry **)a,
4541                 **y = (AdrBk_Entry **)b;
4542     int result;
4543     register char *p, *q, *r, *s;
4544 
4545     p = (*x)->fullname;
4546     if(*p == '"' && *(p+1))
4547       p++;
4548 
4549     q = (*y)->fullname;
4550     if(*q == '"' && *(q+1))
4551       q++;
4552 
4553     r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4554 				       SIZEOF_20KBUF, p);
4555     s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4556 				       SIZEOF_20KBUF-10000, q);
4557     result = (*pcollator)(r, s);
4558     if(result == 0)
4559       result = (*pcollator)((*x)->nickname, (*y)->nickname);
4560 
4561     return(result);
4562 }
4563 
4564 
4565 /*
4566  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
4567  * Sorts on Fullname field.
4568  */
4569 int
cmp_cntr_by_full(const qsort_t * a,const qsort_t * b)4570 cmp_cntr_by_full(const qsort_t *a, const qsort_t *b)
4571 {
4572     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
4573                  *y = (adrbk_cntr_t *)b;
4574     AdrBk_Entry  *x_ae,
4575 		 *y_ae;
4576 
4577     if(ps_global->intr_pending)
4578       longjmp(jump_over_qsort, 1);
4579 
4580     ALARM_BLIP();
4581 
4582     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4583     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4584 
4585     return(cmp_ae_by_full((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4586 }
4587 
4588 
4589 /*
4590  * Compare two address book entries.  Args are AdrBk_Entry **'s.
4591  * Sorts lists after simple addresses and then sorts on Nickname field.
4592  */
4593 int
cmp_ae_by_nick_lists_last(const qsort_t * a,const qsort_t * b)4594 cmp_ae_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4595 {
4596     AdrBk_Entry **x = (AdrBk_Entry **)a,
4597                 **y = (AdrBk_Entry **)b;
4598     int result;
4599 
4600     if((*x)->tag == List && (*y)->tag == Single)
4601       result = 1;
4602     else if((*x)->tag == Single && (*y)->tag == List)
4603       result = -1;
4604     else
4605       result = (*pcollator)((*x)->nickname, (*y)->nickname);
4606 
4607     return(result);
4608 }
4609 
4610 
4611 /*
4612  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
4613  * Sorts lists after simple addresses and then sorts on Nickname field.
4614  */
4615 int
cmp_cntr_by_nick_lists_last(const qsort_t * a,const qsort_t * b)4616 cmp_cntr_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4617 {
4618     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
4619                  *y = (adrbk_cntr_t *)b;
4620     AdrBk_Entry  *x_ae,
4621 		 *y_ae;
4622 
4623     if(ps_global->intr_pending)
4624       longjmp(jump_over_qsort, 1);
4625 
4626     ALARM_BLIP();
4627 
4628     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4629     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4630 
4631     return(cmp_ae_by_nick_lists_last((const qsort_t *) &x_ae,
4632 				     (const qsort_t *) &y_ae));
4633 }
4634 
4635 
4636 /*
4637  * Compare two address book entries.  Args are AdrBk_Entry **'s.
4638  * Sorts on Nickname field.
4639  */
4640 int
cmp_ae_by_nick(const qsort_t * a,const qsort_t * b)4641 cmp_ae_by_nick(const qsort_t *a, const qsort_t *b)
4642 {
4643     AdrBk_Entry **x = (AdrBk_Entry **)a,
4644                 **y = (AdrBk_Entry **)b;
4645 
4646     return((*pcollator)((*x)->nickname, (*y)->nickname));
4647 }
4648 
4649 
4650 /*
4651  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
4652  * Sorts on Nickname field.
4653  */
4654 int
cmp_cntr_by_nick(const qsort_t * a,const qsort_t * b)4655 cmp_cntr_by_nick(const qsort_t *a, const qsort_t *b)
4656 {
4657     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
4658                  *y = (adrbk_cntr_t *)b;
4659     AdrBk_Entry  *x_ae,
4660 		 *y_ae;
4661 
4662     if(ps_global->intr_pending)
4663       longjmp(jump_over_qsort, 1);
4664 
4665     ALARM_BLIP();
4666 
4667     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4668     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4669 
4670     return(cmp_ae_by_nick((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4671 }
4672 
4673 
4674 /*
4675  * For sorting a simple list of pointers to addresses (skip initial quotes)
4676  */
4677 int
cmp_addr(const qsort_t * a1,const qsort_t * a2)4678 cmp_addr(const qsort_t *a1, const qsort_t *a2)
4679 {
4680     char *x = *(char **)a1, *y = *(char **)a2;
4681     char *r, *s;
4682 
4683     if(x && *x == '"')
4684       x++;
4685 
4686     if(y && *y == '"')
4687       y++;
4688 
4689     r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4690 				       SIZEOF_20KBUF, x);
4691     s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4692 				       SIZEOF_20KBUF-10000, y);
4693     return((*pcollator)(r, s));
4694 }
4695 
4696 
4697 /*
4698  * Sort an array of strings, except skip initial quotes.
4699  */
4700 void
sort_addr_list(char ** list)4701 sort_addr_list(char **list)
4702 {
4703     register char **p;
4704 
4705     /* find size of list */
4706     for(p = list; *p != NULL; p++)
4707       ;/* do nothing */
4708 
4709     qsort((qsort_t *)list, (size_t)(p - list), sizeof(char *), cmp_addr);
4710 }
4711 
4712 
4713 /*
4714  * Sort this address book.
4715  *
4716  * Args: ab            -- address book to sort
4717  *   current_entry_num -- see next description
4718  *   new_entry_num     -- return new entry_num of current_entry_num here
4719  *
4720  * Result: return code:  0 all went well
4721  *                      -2 error writing address book, check errno
4722  *
4723  * The sorting strategy is to allocate an array of length ab->count which
4724  * contains the element numbers 0, 1, ..., ab->count - 1, representing the
4725  * entries in the addrbook, of course.  Sort the array, then use that
4726  * result to swap ae's in the in-core addrbook array before writing it out.
4727  */
4728 int
adrbk_sort(AdrBk * ab,a_c_arg_t current_entry_num,adrbk_cntr_t * new_entry_num,int be_quiet)4729 adrbk_sort(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num, int be_quiet)
4730 {
4731     adrbk_cntr_t *sort_array, *inv, tmp;
4732     long i, j, hi, count;
4733     int skip_the_sort = 0, we_cancel = 0, we_turned_on = 0;
4734     AdrBk_Entry ae_tmp, *ae_i, *ae_hi;
4735     EXPANDED_S *e, *e2, *smallest;
4736 
4737     dprint((5, "- adrbk_sort -\n"));
4738 
4739     count = (long) (ab->count);
4740 
4741     if(!ab)
4742       return -2;
4743 
4744     if(ab->sort_rule == AB_SORT_RULE_NONE)
4745       return 0;
4746 
4747     if(count < 2)
4748       return 0;
4749 
4750     sort_array = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4751     inv        = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4752 
4753     for(i = 0L; i < count; i++)
4754       sort_array[i] = (adrbk_cntr_t) i;
4755 
4756     ab_for_sort = ab;
4757 
4758     if(setjmp(jump_over_qsort))
4759       skip_the_sort = 1;
4760 
4761     if(!skip_the_sort){
4762 	we_turned_on = intr_handling_on();
4763 	if(!be_quiet)
4764 	  we_cancel = busy_cue(_("Sorting address book"), NULL, 0);
4765 
4766 	qsort((qsort_t *)sort_array,
4767 	    (size_t)count,
4768 	    sizeof(adrbk_cntr_t),
4769 	    (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
4770 						cmp_cntr_by_full_lists_last :
4771 	    (ab->sort_rule == AB_SORT_RULE_FULL) ?
4772 						cmp_cntr_by_full :
4773 	    (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
4774 						cmp_cntr_by_nick_lists_last :
4775 	    /* (ab->sort_rule == AB_SORT_RULE_NICK) */
4776 						cmp_cntr_by_nick);
4777     }
4778 
4779     dprint((9, "- adrbk_sort: done with first sort -\n"));
4780 
4781     if(we_turned_on)
4782       intr_handling_off();
4783 
4784     if(skip_the_sort){
4785 	q_status_message(SM_ORDER, 3, 3,
4786 	    _("Address book sort cancelled, using old order for now"));
4787 	goto skip_the_write_too;
4788     }
4789 
4790     dprint((5, "- adrbk_sort (%s)\n",
4791 	    ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
4792 	     ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
4793 	      ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
4794 	       ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
4795 
4796     /*
4797      * Rearrange the in-core array of ae's to be in the new order.
4798      * We can do that by sorting the inverse into the correct order in parallel.
4799      */
4800     for(i = 0L; i < count; i++)
4801       inv[sort_array[i]] = i;
4802 
4803     if(new_entry_num && (adrbk_cntr_t) current_entry_num >= 0
4804        && (adrbk_cntr_t) current_entry_num < count){
4805 	*new_entry_num = inv[(adrbk_cntr_t) current_entry_num];
4806     }
4807 
4808     /*
4809      * The expanded and selected lists will be wrong now. Correct them.
4810      * First the expanded list.
4811      */
4812     e = ab->exp ? ab->exp->next : NULL;
4813     while(e){
4814 	if(e->ent >= 0 && e->ent < count)
4815 	  e->ent = inv[e->ent];
4816 
4817 	e = e->next;
4818     }
4819 
4820     /*
4821      * And sort into ascending order as expected by the exp_ routines.
4822      */
4823     e = ab->exp ? ab->exp->next : NULL;
4824     while(e){
4825 	/* move smallest to e */
4826 	e2 = e;
4827 	smallest = e;
4828 	while(e2){
4829 	    if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4830 	      smallest = e2;
4831 
4832 	    e2 = e2->next;
4833 	}
4834 
4835 	/* swap values in e and smallest */
4836 	if(e != smallest){
4837 	    tmp = e->ent;
4838 	    e->ent = smallest->ent;
4839 	    smallest->ent = tmp;
4840 	}
4841 
4842 	e = e->next;
4843     }
4844 
4845     /*
4846      * Same thing for the selected list.
4847      */
4848     e = ab->selects ? ab->selects->next : NULL;
4849     while(e){
4850 	if(e->ent >= 0 && e->ent < count)
4851 	  e->ent = inv[e->ent];
4852 
4853 	e = e->next;
4854     }
4855 
4856     e = ab->selects ? ab->selects->next : NULL;
4857     while(e){
4858 	/* move smallest to e */
4859 	e2 = e;
4860 	smallest = e;
4861 	while(e2){
4862 	    if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4863 	      smallest = e2;
4864 
4865 	    e2 = e2->next;
4866 	}
4867 
4868 	/* swap values in e and smallest */
4869 	if(e != smallest){
4870 	    tmp = e->ent;
4871 	    e->ent = smallest->ent;
4872 	    smallest->ent = tmp;
4873 	}
4874 
4875 	e = e->next;
4876     }
4877 
4878     for(i = 0L; i < count; i++){
4879 	if(i != inv[i]){
4880 	    /* find inv[j] which = i */
4881 	    for(j = i+1; j < count; j++){
4882 		if(i == inv[j]){
4883 		    hi = j;
4884 		    break;
4885 		}
4886 	    }
4887 
4888 	    /* swap i and hi */
4889 	    ae_i  = adrbk_get_ae(ab, (a_c_arg_t) i);
4890 	    ae_hi = adrbk_get_ae(ab, (a_c_arg_t) hi);
4891 	    if(ae_i && ae_hi){
4892 		memcpy(&ae_tmp, ae_i, sizeof(ae_tmp));
4893 		memcpy(ae_i, ae_hi, sizeof(ae_tmp));
4894 		memcpy(ae_hi, &ae_tmp, sizeof(ae_tmp));
4895 	    }
4896 	    /* else can't happen */
4897 
4898 	    inv[hi] = inv[i];
4899 	}
4900     }
4901 
4902     if(we_cancel)
4903       cancel_busy_cue(0);
4904 
4905     repair_abook_tries(ab);
4906 
4907     dprint((9, "- adrbk_sort: done with rearranging -\n"));
4908 
4909 skip_the_write_too:
4910     if(we_cancel)
4911       cancel_busy_cue(0);
4912 
4913     if(sort_array)
4914       fs_give((void **) &sort_array);
4915 
4916     if(inv)
4917       fs_give((void **) &inv);
4918 
4919     return 0;
4920 }
4921 
4922 
4923 /*
4924  * Returns 1 if any addrbooks are in Open state, 0 otherwise.
4925  *
4926  * This is a test for ostatus == Open, not for whether or not the address book
4927  * FILE is opened.
4928  */
4929 int
any_ab_open(void)4930 any_ab_open(void)
4931 {
4932     int i, ret = 0;
4933 
4934     if(as.initialized)
4935       for(i = 0; ret == 0 && i < as.n_addrbk; i++)
4936 	if(as.adrbks[i].ostatus == Open)
4937 	  ret++;
4938 
4939     return(ret);
4940 }
4941 
4942 
4943 /*
4944  * Make sure addrbooks are minimally initialized.
4945  */
4946 void
init_ab_if_needed(void)4947 init_ab_if_needed(void)
4948 {
4949     dprint((9, "- init_ab_if_needed -\n"));
4950 
4951     if(!as.initialized)
4952       (void)init_addrbooks(Closed, 0, 0, 1);
4953 }
4954 
4955 
4956 /*
4957  * Sets everything up to get started.
4958  *
4959  * Args: want_status      -- The desired OpenStatus for all addrbooks.
4960  *       reset_to_top     -- Forget about the old location and put cursor
4961  *                           at top.
4962  *       open_if_only_one -- If want_status is HalfOpen and there is only
4963  *                           section to look at, then promote want_status
4964  *                           to Open.
4965  *       ro_warning       -- Set ReadOnly warning global
4966  *
4967  * Return: 1 if ok, 0 if problem
4968  */
4969 int
init_addrbooks(OpenStatus want_status,int reset_to_top,int open_if_only_one,int ro_warning)4970 init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, int ro_warning)
4971 {
4972     register PerAddrBook *pab;
4973     char *q, **t;
4974     long line;
4975 
4976     dprint((4, "-- init_addrbooks(%s, %d, %d, %d) --\n",
4977 		    want_status==Open ?
4978 				"Open" :
4979 				want_status==HalfOpen ?
4980 					"HalfOpen" :
4981 				   want_status==ThreeQuartOpen ?
4982 					"ThreeQuartOpen" :
4983 					want_status==NoDisplay ?
4984 						"NoDisplay" :
4985 						"Closed",
4986 		    reset_to_top, open_if_only_one, ro_warning));
4987 
4988     as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
4989 					       - HEADER_ROWS(ps_global);
4990     if(as.l_p_page <= 0)
4991       as.no_op_possbl++;
4992     else
4993       as.no_op_possbl = 0;
4994 
4995     as.ro_warning = ro_warning;
4996 
4997     /* already been initialized */
4998     if(as.initialized){
4999 	int i;
5000 
5001 	/*
5002 	 * Special case. If there is only one addressbook we start the
5003 	 * user out with that open.
5004 	 */
5005 	if(want_status == HalfOpen &&
5006 	   ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5007 	    (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5008 	     F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5009 	  want_status = Open;
5010 
5011 	/* open to correct state */
5012 	for(i = 0; i < as.n_addrbk; i++)
5013 	  init_abook(&as.adrbks[i], want_status);
5014 
5015 	if(reset_to_top){
5016 	    warp_to_beginning();
5017 	    as.top_ent     = 0L;
5018 	    line           = first_selectable_line(0L);
5019 	    if(line == NO_LINE)
5020 	      as.cur_row = 0L;
5021 	    else
5022 	      as.cur_row = line;
5023 
5024 	    if(as.cur_row >= as.l_p_page)
5025 	      as.top_ent += (as.cur_row - as.l_p_page + 1);
5026 
5027 	    as.old_cur_row = as.cur_row;
5028 	}
5029 
5030 	dprint((4, "init_addrbooks: already initialized: %d books\n",
5031 				    as.n_addrbk));
5032         return((as.n_addrbk + as.n_serv) ? 1 : 0);
5033     }
5034 
5035     as.initialized = 1;
5036 
5037     /* count directory servers */
5038     as.n_serv = 0;
5039     as.n_impl = 0;
5040 #ifdef	ENABLE_LDAP
5041     if(ps_global->VAR_LDAP_SERVERS &&
5042        ps_global->VAR_LDAP_SERVERS[0] &&
5043        ps_global->VAR_LDAP_SERVERS[0][0])
5044 	for(t = ps_global->VAR_LDAP_SERVERS; t[0] && t[0][0]; t++){
5045 	    LDAP_SERV_S *info;
5046 
5047 	    as.n_serv++;
5048 	    info = break_up_ldap_server(*t);
5049 	    as.n_impl += (info && info->impl) ? 1 : 0;
5050 	    if(info)
5051 	      free_ldap_server_info(&info);
5052 	}
5053 #endif	/* ENABLE_LDAP */
5054 
5055     /* count addressbooks */
5056     as.how_many_personals = 0;
5057     if(ps_global->VAR_ADDRESSBOOK &&
5058        ps_global->VAR_ADDRESSBOOK[0] &&
5059        ps_global->VAR_ADDRESSBOOK[0][0])
5060 	for(t = ps_global->VAR_ADDRESSBOOK; t[0] && t[0][0]; t++)
5061 	  as.how_many_personals++;
5062 
5063     as.n_addrbk = as.how_many_personals;
5064     if(ps_global->VAR_GLOB_ADDRBOOK &&
5065        ps_global->VAR_GLOB_ADDRBOOK[0] &&
5066        ps_global->VAR_GLOB_ADDRBOOK[0][0])
5067 	for(t = ps_global->VAR_GLOB_ADDRBOOK; t[0] && t[0][0]; t++)
5068 	  as.n_addrbk++;
5069 
5070     if(want_status == HalfOpen &&
5071        ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5072 	(F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5073 	 F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5074       want_status = Open;
5075 
5076 
5077     /*
5078      * allocate array of PerAddrBooks
5079      * (we don't give this up until we exit Pine, but it's small)
5080      */
5081     if(as.n_addrbk){
5082 	as.adrbks = (PerAddrBook *)fs_get(as.n_addrbk * sizeof(PerAddrBook));
5083 	memset((void *)as.adrbks, 0, as.n_addrbk * sizeof(PerAddrBook));
5084 
5085 	/* init PerAddrBook data */
5086 	for(as.cur = 0; as.cur < as.n_addrbk; as.cur++){
5087 	    char *nickname = NULL,
5088 		 *filename = NULL;
5089 
5090 	    if(as.cur < as.how_many_personals)
5091 	      q = ps_global->VAR_ADDRESSBOOK[as.cur];
5092 	    else
5093 	      q = ps_global->VAR_GLOB_ADDRBOOK[as.cur - as.how_many_personals];
5094 
5095 	    pab = &as.adrbks[as.cur];
5096 
5097 	    /* Parse entry for optional nickname and filename */
5098 	    get_pair(q, &nickname, &filename, 0, 0);
5099 
5100 	    if(nickname && !*nickname)
5101 	      fs_give((void **)&nickname);
5102 
5103 	    strncpy(tmp_20k_buf, filename, SIZEOF_20KBUF);
5104 	    fs_give((void **)&filename);
5105 
5106 	    filename = tmp_20k_buf;
5107 	    if(nickname == NULL)
5108 	      pab->abnick = cpystr(filename);
5109 	    else
5110 	      pab->abnick = nickname;
5111 
5112 	    if(*filename == '~')
5113 	      fnexpand(filename, SIZEOF_20KBUF);
5114 
5115 	    if(*filename == '{' || is_absolute_path(filename)){
5116 		pab->filename = cpystr(filename); /* fully qualified */
5117 	    }
5118 	    else{
5119 		char book_path[MAXPATH+1];
5120 		char *lc = last_cmpnt(ps_global->pinerc);
5121 
5122 		book_path[0] = '\0';
5123 		if(lc != NULL){
5124 		    strncpy(book_path, ps_global->pinerc,
5125 			    MIN(lc - ps_global->pinerc, sizeof(book_path)-1));
5126 		    book_path[MIN(lc - ps_global->pinerc,
5127 				  sizeof(book_path)-1)] = '\0';
5128 		}
5129 
5130 		strncat(book_path, filename,
5131 			sizeof(book_path)-1-strlen(book_path));
5132 		pab->filename = cpystr(book_path);
5133 	    }
5134 
5135 	    if(*pab->filename == '{')
5136 	      pab->type |= REMOTE_VIA_IMAP;
5137 
5138 	    if(as.cur >= as.how_many_personals)
5139 	      pab->type |= GLOBAL;
5140 
5141 	    pab->access = adrbk_access(pab);
5142 
5143 	    /* global address books are forced readonly */
5144 	    if(pab->type & GLOBAL && pab->access != NoAccess)
5145 	      pab->access = ReadOnly;
5146 
5147 	    pab->ostatus  = TotallyClosed;
5148 
5149 	    /*
5150 	     * and remember that the memset above initializes everything
5151 	     * else to 0
5152 	     */
5153 
5154 	    init_abook(pab, want_status);
5155 	}
5156     }
5157 
5158     /*
5159      * Have to reset_to_top in this case since this is the first open,
5160      * regardless of the value of the argument, since these values haven't been
5161      * set before here.
5162      */
5163     as.cur         = 0;
5164     as.top_ent     = 0L;
5165     warp_to_beginning();
5166     line           = first_selectable_line(0L);
5167 
5168     if(line == NO_LINE)
5169       as.cur_row = 0L;
5170     else
5171       as.cur_row = line;
5172 
5173     if(as.cur_row >= as.l_p_page){
5174 	as.top_ent += (as.cur_row - as.l_p_page + 1);
5175 	as.cur_row = as.l_p_page - 1;
5176     }
5177 
5178     as.old_cur_row = as.cur_row;
5179 
5180     return((as.n_addrbk + as.n_serv) ? 1 : 0);
5181 }
5182 
5183 
5184 /*
5185  * Something was changed in options screen, so need to start over.
5186  */
5187 void
addrbook_reset(void)5188 addrbook_reset(void)
5189 {
5190     dprint((4, "- addrbook_reset -\n"));
5191     completely_done_with_adrbks();
5192 }
5193 
5194 
5195 /*
5196  * Sort was changed in options screen. Since we only sort normally
5197  * when we actually make a change to the address book, we need to
5198  * go out of our way to sort here.
5199  */
5200 void
addrbook_redo_sorts(void)5201 addrbook_redo_sorts(void)
5202 {
5203     int i;
5204     PerAddrBook *pab;
5205     AdrBk *ab;
5206 
5207     dprint((4, "- addrbook_redo_sorts -\n"));
5208 
5209     addrbook_reset();
5210     init_ab_if_needed();
5211 
5212     for(i = 0; i < as.n_addrbk; i++){
5213 	pab = &as.adrbks[i];
5214 	init_abook(pab, NoDisplay);
5215 	ab = pab->address_book;
5216 
5217 	if(!adrbk_is_in_sort_order(ab, 0))
5218 	  adrbk_write(ab, 0, NULL, NULL, 1, 0);
5219     }
5220 
5221     addrbook_reset();
5222 }
5223 
5224 
5225 /*
5226  * Returns type of access allowed on this addrbook.
5227  */
5228 AccessType
adrbk_access(PerAddrBook * pab)5229 adrbk_access(PerAddrBook *pab)
5230 {
5231     char       fbuf[MAXPATH+1];
5232     AccessType access = NoExists;
5233     CONTEXT_S *dummy_cntxt = NULL;
5234 
5235     dprint((9, "- addrbook_access -\n"));
5236 
5237     if(pab && pab->type & REMOTE_VIA_IMAP){
5238 	/*
5239 	 * Open_fcc creates the folder if it didn't already exist.
5240 	 */
5241 	if((pab->so = open_fcc(pab->filename, &dummy_cntxt, 1,
5242 			       "Error: ",
5243 			       " Can't fetch remote addrbook.")) != NULL){
5244 	    /*
5245 	     * We know the folder is there but don't know what access
5246 	     * rights we have until we try to select it, which we don't
5247 	     * want to do unless we have to. So delay evaluating.
5248 	     */
5249 	    access = MaybeRorW;
5250 	}
5251     }
5252     else if(pab){	/* local file */
5253 #if defined(NO_LOCAL_ADDRBOOKS)
5254 	/* don't allow any access to local addrbooks */
5255 	access = NoAccess;
5256 #else /* !NO_LOCAL_ADDRBOOKS) */
5257 	build_path(fbuf, is_absolute_path(pab->filename) ? NULL
5258 							 : ps_global->home_dir,
5259 		   pab->filename, sizeof(fbuf));
5260 
5261 #if defined(DOS) || defined(OS2)
5262 	/*
5263 	 * Microsoft networking causes some access calls to do a DNS query (!!)
5264 	 * when it is turned on. In particular, if there is a / in the filename
5265 	 * this seems to happen. So, just don't allow it.
5266 	 */
5267 	if(strindex(fbuf, '/') != NULL){
5268 	    dprint((2, "\"/\" not allowed in addrbook name\n"));
5269 	    return NoAccess;
5270 	}
5271 #else /* !DOS */
5272 	/* also prevent backslash in non-DOS addrbook names */
5273 	if(strindex(fbuf, '\\') != NULL){
5274 	    dprint((2, "\"\\\" not allowed in addrbook name\n"));
5275 	    return NoAccess;
5276 	}
5277 #endif /* !DOS */
5278 
5279 	if(can_access(fbuf, ACCESS_EXISTS) == 0){
5280 	    if(can_access(fbuf, EDIT_ACCESS) == 0){
5281 		char *dir, *p;
5282 
5283 		dir = ".";
5284 		if((p = last_cmpnt(fbuf)) != NULL){
5285 		    *--p = '\0';
5286 		    dir  = *fbuf ? fbuf : "/";
5287 		}
5288 
5289 #if	defined(DOS) || defined(OS2)
5290 		/*
5291 		 * If the dir has become a drive letter and : (e.g. "c:")
5292 		 * then append a "\".  The library function access() in the
5293 		 * win 16 version of MSC seems to require this.
5294 		 */
5295 		if(isalpha((unsigned char) *dir)
5296 		   && *(dir+1) == ':' && *(dir+2) == '\0'){
5297 		    *(dir+2) = '\\';
5298 		    *(dir+3) = '\0';
5299 		}
5300 #endif	/* DOS || OS2 */
5301 
5302 		/*
5303 		 * Even if we can edit the address book file itself, we aren't
5304 		 * going to be able to change it unless we can also write in
5305 		 * the directory that contains it (because we write into a
5306 		 * temp file and then rename).
5307 		 */
5308 		if(can_access(dir, EDIT_ACCESS) == 0)
5309 		  access = ReadWrite;
5310 		else{
5311 		    access = ReadOnly;
5312 		    q_status_message1(SM_ORDER, 2, 2,
5313 				      "Address book directory (%.200s) is ReadOnly",
5314 				      dir);
5315 		}
5316 	    }
5317 	    else if(can_access(fbuf, READ_ACCESS) == 0)
5318 	      access = ReadOnly;
5319 	    else
5320 	      access = NoAccess;
5321 	}
5322 #endif /* !NO_LOCAL_ADDRBOOKS) */
5323     }
5324 
5325     return(access);
5326 }
5327 
5328 
5329 /*
5330  * Trim back remote address books if necessary.
5331  */
5332 void
trim_remote_adrbks(void)5333 trim_remote_adrbks(void)
5334 {
5335     register PerAddrBook *pab;
5336     int i;
5337 
5338     dprint((2, "- trim_remote_adrbks -\n"));
5339 
5340     if(!as.initialized)
5341       return;
5342 
5343     for(i = 0; i < as.n_addrbk; i++){
5344 	pab = &as.adrbks[i];
5345 	if(pab->ostatus != TotallyClosed && pab->address_book
5346 	   && pab->address_book->rd)
5347 	  rd_trim_remdata(&pab->address_book->rd);
5348     }
5349 }
5350 
5351 
5352 /*
5353  * Free and close everything.
5354  */
5355 void
completely_done_with_adrbks(void)5356 completely_done_with_adrbks(void)
5357 {
5358     register PerAddrBook *pab;
5359     int i;
5360 
5361     dprint((2, "- completely_done_with_adrbks -\n"));
5362 
5363     ab_nesting_level = 0;
5364 
5365     if(!as.initialized)
5366       return;
5367 
5368     for(i = 0; i < as.n_addrbk; i++)
5369       init_abook(&as.adrbks[i], TotallyClosed);
5370 
5371     for(i = 0; i < as.n_addrbk; i++){
5372 	pab = &as.adrbks[i];
5373 
5374 	if(pab->filename)
5375 	  fs_give((void **)&pab->filename);
5376 
5377 	if(pab->abnick)
5378 	  fs_give((void **)&pab->abnick);
5379     }
5380 
5381     done_with_dlc_cache();
5382 
5383     if(as.adrbks)
5384       fs_give((void **)&as.adrbks);
5385 
5386     as.n_addrbk    = 0;
5387     as.initialized = 0;
5388 }
5389 
5390 
5391 /*
5392  * Initialize or re-initialize this address book.
5393  *
5394  *  Args: pab        -- the PerAddrBook ptr
5395  *       want_status -- desired OpenStatus for this address book
5396  */
5397 void
init_abook(PerAddrBook * pab,OpenStatus want_status)5398 init_abook(PerAddrBook *pab, OpenStatus want_status)
5399 {
5400     register OpenStatus new_status;
5401 
5402     dprint((4, "- init_abook -\n"));
5403     dprint((7, "    addrbook nickname = %s filename = %s",
5404 	   pab->abnick ? pab->abnick : "<null>",
5405 	   pab->filename ? pab->filename : "<null>"));
5406     dprint((7, "  ostatus was %s, want %s\n",
5407 	   pab->ostatus==Open ? "Open" :
5408 	     pab->ostatus==HalfOpen ? "HalfOpen" :
5409 	       pab->ostatus==ThreeQuartOpen ? "ThreeQuartOpen" :
5410 		 pab->ostatus==NoDisplay ? "NoDisplay" :
5411 		   pab->ostatus==Closed ? "Closed" : "TotallyClosed",
5412 	   want_status==Open ? "Open" :
5413 	     want_status==HalfOpen ? "HalfOpen" :
5414 	       want_status==ThreeQuartOpen ? "ThreeQuartOpen" :
5415 		 want_status==NoDisplay ? "NoDisplay" :
5416 		   want_status==Closed ? "Closed" : "TotallyClosed"));
5417 
5418     new_status = want_status;  /* optimistic default */
5419 
5420     if(want_status == TotallyClosed){
5421 	if(pab->address_book != NULL){
5422 	    adrbk_close(pab->address_book);
5423 	    pab->address_book = NULL;
5424 	}
5425 
5426 	if(pab->so != NULL){
5427 	    so_give(&pab->so);
5428 	    pab->so = NULL;
5429 	}
5430     }
5431     /*
5432      * If we don't need it, release some addrbook memory by calling
5433      * adrbk_partial_close().
5434      */
5435     else if((want_status == Closed || want_status == HalfOpen) &&
5436 	pab->address_book != NULL){
5437 	adrbk_partial_close(pab->address_book);
5438     }
5439     /* If we want the addrbook read in and it hasn't been, do so */
5440     else if(want_status == Open || want_status == NoDisplay){
5441       if(pab->address_book == NULL){  /* abook handle is not currently active */
5442 	if(pab->access != NoAccess){
5443 	    char warning[800]; /* place to put a warning */
5444 	    int sort_rule;
5445 
5446 	    warning[0] = '\0';
5447 	    if(pab->access == ReadOnly)
5448 	      sort_rule = AB_SORT_RULE_NONE;
5449 	    else
5450 	      sort_rule = ps_global->ab_sort_rule;
5451 
5452 	    pab->address_book = adrbk_open(pab,
5453 		   ps_global->home_dir, warning, sizeof(warning), sort_rule);
5454 
5455 	    if(pab->address_book == NULL){
5456 		pab->access = NoAccess;
5457 		if(want_status == Open){
5458 		    new_status = HalfOpen;  /* best we can do */
5459 		    q_status_message1(SM_ORDER | SM_DING, *warning?1:3, 4,
5460 				      _("Error opening/creating address book %.200s"),
5461 				      pab->abnick);
5462 		    if(*warning)
5463 			q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5464 			    as.n_addrbk > 1 ? pab->abnick : "addressbook",
5465 			    warning);
5466 		}
5467 		else
5468 		    new_status  = Closed;
5469 
5470 		dprint((1, "Error opening address book %s: %s\n",
5471 			  pab->abnick ? pab->abnick : "?",
5472 			  error_description(errno)));
5473 	    }
5474 	    else{
5475 		if(pab->access == NoExists)
5476 		  pab->access = ReadWrite;
5477 
5478 		if(pab->access == ReadWrite){
5479 		    /*
5480 		     * Add forced entries if there are any.  These are
5481 		     * entries that are always supposed to show up in
5482 		     * personal address books.  They're specified in the
5483 		     * global config file.
5484 		     */
5485 		    add_forced_entries(pab->address_book);
5486 		}
5487 
5488 		new_status = want_status;
5489 		dprint((2, "Address book %s (%s) opened with %ld items\n",
5490 			pab->abnick ? pab->abnick : "?",
5491 			pab->filename ? pab->filename : "?",
5492 			(long)adrbk_count(pab->address_book)));
5493 		if(*warning){
5494 		    dprint((1,
5495 				 "Addressbook parse error in %s (%s): %s\n",
5496 				 pab->abnick ? pab->abnick : "?",
5497 				 pab->filename ? pab->filename : "?",
5498 				 warning));
5499 		    if(!pab->gave_parse_warnings && want_status == Open){
5500 			pab->gave_parse_warnings++;
5501 			q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5502 			    as.n_addrbk > 1 ? pab->abnick : "addressbook",
5503 			    warning);
5504 		    }
5505 		}
5506 	    }
5507 	}
5508 	else{
5509 	    if(want_status == Open){
5510 		new_status = HalfOpen;  /* best we can do */
5511 		q_status_message1(SM_ORDER | SM_DING, 3, 4,
5512 		   "Insufficient permissions for opening address book %.200s",
5513 		   pab->abnick);
5514 	    }
5515 	    else
5516 	      new_status = Closed;
5517 	}
5518       }
5519       /*
5520        * File handle was already open but we were in Closed or HalfOpen
5521        * state. Check to see if someone else has changed the addrbook
5522        * since we first opened it.
5523        */
5524       else if((pab->ostatus == Closed || pab->ostatus == HalfOpen) &&
5525 	      ps_global->remote_abook_validity > 0)
5526 	(void)adrbk_check_and_fix(pab, 1, 0, 0);
5527     }
5528 
5529     pab->ostatus = new_status;
5530 }
5531 
5532 
5533 /*
5534  * Does a validity check on all the open address books.
5535  *
5536  * Return -- number of address books with invalid data
5537  */
5538 int
adrbk_check_all_validity_now(void)5539 adrbk_check_all_validity_now(void)
5540 {
5541     int          i;
5542     int          something_out_of_date = 0;
5543     PerAddrBook *pab;
5544 
5545     dprint((7, "- adrbk_check_all_validity_now -\n"));
5546 
5547     if(as.initialized){
5548 	for(i = 0; i < as.n_addrbk; i++){
5549 	    pab = &as.adrbks[i];
5550 	    if(pab->address_book){
5551 		adrbk_check_validity(pab->address_book, 1L);
5552 		if(pab->address_book->flags & FILE_OUTOFDATE ||
5553 		   (pab->address_book->rd &&
5554 		    pab->address_book->rd->flags & REM_OUTOFDATE))
5555 		  something_out_of_date++;
5556 	    }
5557 	}
5558     }
5559 
5560     return(something_out_of_date);
5561 }
5562 
5563 
5564 /*
5565  * Fix out-of-date address book. This only fixes the AdrBk part of the
5566  * problem, not the pab and display. It closes and reopens the address_book
5567  * file, clears the cache, reads new data.
5568  *
5569  * Arg        pab -- Pointer to the PerAddrBook data for this addrbook
5570  *           safe -- It is safe to apply the fix
5571  *       low_freq -- This is a low frequency check (longer between checks)
5572  *
5573  * Returns non-zero if addrbook was fixed
5574  */
5575 int
adrbk_check_and_fix(PerAddrBook * pab,int safe,int low_freq,int check_now)5576 adrbk_check_and_fix(PerAddrBook *pab, int safe, int low_freq, int check_now)
5577 {
5578     int  ret = 0;
5579     long chk_interval;
5580 
5581     if(!as.initialized || !pab)
5582       return(ret);
5583 
5584     dprint((7, "- adrbk_check_and_fix(%s) -\n",
5585 	   pab->filename ? pab->filename : "?"));
5586 
5587     if(pab->address_book){
5588 	if(check_now)
5589 	  chk_interval = 1L;
5590 	else if(low_freq)
5591 	  chk_interval = -60L * MAX(LOW_FREQ_CHK_INTERVAL,
5592 				    ps_global->remote_abook_validity + 5);
5593 	else
5594 	  chk_interval = 0L;
5595 
5596 	adrbk_check_validity(pab->address_book, chk_interval);
5597 
5598 	if(pab->address_book->flags & FILE_OUTOFDATE ||
5599 	   (pab->address_book->rd &&
5600 	    pab->address_book->rd->flags & REM_OUTOFDATE &&
5601 	    !(pab->address_book->rd->flags & USER_SAID_NO))){
5602 	    if(safe){
5603 		OpenStatus save_status;
5604 		int        save_rem_abook_valid = 0;
5605 
5606 		dprint((2, "adrbk_check_and_fix %s: fixing %s\n",
5607 		       debug_time(0,0,ps_global->signal_in_progress),
5608 		       pab->filename ? pab->filename : "?"));
5609 		if(ab_nesting_level > 0){
5610 		    q_status_message3(SM_ORDER, 0, 2,
5611 				     "Resyncing address book%.200s%.200s%.200s",
5612 				     as.n_addrbk > 1 ? " \"" : "",
5613 				     as.n_addrbk > 1 ? pab->abnick : "",
5614 				     as.n_addrbk > 1 ? "\"" : "");
5615 		    display_message('x');
5616 		    fflush(stdout);
5617 		}
5618 
5619 		ret++;
5620 		save_status = pab->ostatus;
5621 
5622 		/* don't do the trim right now */
5623 		if(pab->address_book->rd)
5624 		  pab->address_book->rd->flags &= ~DO_REMTRIM;
5625 
5626 		/*
5627 		 * Have to change this from -1 or we won't actually do
5628 		 * the resync.
5629 		 */
5630 		if((pab->address_book->rd &&
5631 		    pab->address_book->rd->flags & REM_OUTOFDATE) &&
5632 		   ps_global->remote_abook_validity == -1){
5633 		    save_rem_abook_valid = -1;
5634 		    ps_global->remote_abook_validity = 0;
5635 		}
5636 
5637 		init_abook(pab, TotallyClosed);
5638 
5639 		pab->so = NULL;
5640 		/* this sets up pab->so, so is important */
5641 		pab->access = adrbk_access(pab);
5642 
5643 		/*
5644 		 * If we just re-init to HalfOpen... we won't actually
5645 		 * open the address book, which was open before. That
5646 		 * would be fine but it's a little nicer if we can open
5647 		 * it now so that we don't defer the resync until
5648 		 * the next open, which would be a user action for sure.
5649 		 * Right now we may be here during a newmail check
5650 		 * timeout, so this is a good time to do the resync.
5651 		 */
5652 		if(save_status == HalfOpen ||
5653 		   save_status == ThreeQuartOpen ||
5654 		   save_status == Closed)
5655 		  init_abook(pab, NoDisplay);
5656 
5657 		init_abook(pab, save_status);
5658 
5659 		if(save_rem_abook_valid)
5660 		  ps_global->remote_abook_validity = save_rem_abook_valid;
5661 
5662 		if(ab_nesting_level > 0 && pab->ostatus == save_status)
5663 		    q_status_message3(SM_ORDER, 0, 2,
5664 				     "Resynced address book%.200s%.200s%.200s",
5665 				     as.n_addrbk > 1 ? " \"" : "",
5666 				     as.n_addrbk > 1 ? pab->abnick : "",
5667 				     as.n_addrbk > 1 ? "\"" : "");
5668 	    }
5669 	    else
5670 	      dprint((2,
5671 		     "adrbk_check_and_fix: not safe to fix %s\n",
5672 		     pab->filename ? pab->filename : "?"));
5673 	}
5674     }
5675 
5676     return(ret);
5677 }
5678 
5679 
5680 static time_t last_check_and_fix_all;
5681 /*
5682  * Fix out of date address books. This only fixes the AdrBk part of the
5683  * problem, not the pab and display. It closes and reopens the address_book
5684  * files, clears the caches, reads new data.
5685  *
5686  * Args      safe -- It is safe to apply the fix
5687  *       low_freq -- This is a low frequency check (longer between checks)
5688  *
5689  * Returns non-zero if an addrbook was fixed
5690  */
5691 int
adrbk_check_and_fix_all(int safe,int low_freq,int check_now)5692 adrbk_check_and_fix_all(int safe, int low_freq, int check_now)
5693 {
5694     int          i, ret = 0;
5695     PerAddrBook *pab;
5696 
5697     if(!as.initialized)
5698       return(ret);
5699 
5700     dprint((7, "- adrbk_check_and_fix_all -\n"));
5701 
5702     last_check_and_fix_all = get_adj_time();
5703 
5704     for(i = 0; i < as.n_addrbk; i++){
5705 	pab = &as.adrbks[i];
5706 	if(pab->address_book)
5707 	  ret += adrbk_check_and_fix(pab, safe, low_freq, check_now);
5708     }
5709 
5710     return(ret);
5711 }
5712 
5713 
5714 /*
5715  *
5716  */
5717 void
adrbk_maintenance(void)5718 adrbk_maintenance(void)
5719 {
5720     static time_t last_time_here = 0;
5721     time_t        now;
5722     int           i;
5723     long          low_freq_interval;
5724 
5725     dprint((9, "- adrbk_maintenance -\n"));
5726 
5727     if(!as.initialized)
5728       return;
5729 
5730     now = get_adj_time();
5731 
5732     if(now < last_time_here + 120)
5733       return;
5734 
5735     last_time_here = now;
5736 
5737     low_freq_interval = MAX(LOW_FREQ_CHK_INTERVAL,
5738 			    ps_global->remote_abook_validity + 5);
5739 
5740     /* check the time here to make it cheap */
5741     if(ab_nesting_level == 0 &&
5742        ps_global->remote_abook_validity > 0 &&
5743        now > last_check_and_fix_all + low_freq_interval * 60L)
5744       (void)adrbk_check_and_fix_all(1, 1, 0);
5745 
5746     /* close down idle connections */
5747     for(i = 0; i < as.n_addrbk; i++){
5748 	PerAddrBook *pab;
5749 
5750 	pab = &as.adrbks[i];
5751 
5752 	if(pab->address_book &&
5753 	   pab->address_book->type == Imap &&
5754 	   pab->address_book->rd &&
5755 	   rd_stream_exists(pab->address_book->rd)){
5756 	    dprint((7,
5757 		   "adrbk_maint: %s: idle cntr %ld (%ld)\n",
5758 		   pab->address_book->orig_filename
5759 		     ? pab->address_book->orig_filename : "?",
5760 		   (long)(now - pab->address_book->rd->last_use),
5761 		   (long)pab->address_book->rd->last_use));
5762 
5763 	    if(now > pab->address_book->rd->last_use + IMAP_IDLE_TIMEOUT){
5764 		dprint((2,
5765 		    "adrbk_maint %s: closing idle (%ld secs) connection: %s\n",
5766 		    debug_time(0,0,ps_global->signal_in_progress),
5767 		    (long)(now - pab->address_book->rd->last_use),
5768 		    pab->address_book->orig_filename
5769 		      ? pab->address_book->orig_filename : "?"));
5770 		rd_close_remote(pab->address_book->rd);
5771 	    }
5772 	    else{
5773 		/*
5774 		 * If we aren't going to close it, we ping it instead to
5775 		 * make sure it stays open and doesn't timeout on us.
5776 		 * This shouldn't be necessary unless the server has a
5777 		 * really short timeout. If we got killed for some reason
5778 		 * we set imap.stream to NULL.
5779 		 * Instead of just pinging, we may as well check for
5780 		 * updates, too.
5781 		 */
5782 		if(ab_nesting_level == 0 &&
5783 		   ps_global->remote_abook_validity > 0){
5784 		    time_t save_last_use;
5785 
5786 		    /*
5787 		     * We shouldn't count this as a real last_use.
5788 		     */
5789 		    save_last_use = pab->address_book->rd->last_use;
5790 		    (void)adrbk_check_and_fix(pab, 1, 0, 1);
5791 		    pab->address_book->rd->last_use = save_last_use;
5792 		}
5793 		/* just ping it if not safe to fix it */
5794 		else if(!rd_ping_stream(pab->address_book->rd)){
5795 		    dprint((2,
5796 		      "adrbk_maint: %s: abook stream closed unexpectedly: %s\n",
5797 		      debug_time(0,0,ps_global->signal_in_progress),
5798 		      pab->address_book->orig_filename
5799 		        ? pab->address_book->orig_filename : "?"));
5800 		}
5801 	    }
5802 	}
5803     }
5804 }
5805 
5806 
5807 /*
5808  * Parses a string of comma-separated addresses or nicknames into an
5809  * array.
5810  *
5811  * Returns an allocated, null-terminated list, or NULL.
5812  */
5813 char **
parse_addrlist(char * addrfield)5814 parse_addrlist(char *addrfield)
5815 {
5816 #define LISTCHUNK  500   /* Alloc this many addresses for list at a time */
5817     char **al, **ad;
5818     char *next_addr, *cur_addr, *p, *q;
5819     int slots = LISTCHUNK;
5820 
5821     if(!addrfield)
5822       return((char **)NULL);
5823 
5824     /* allocate first chunk */
5825     slots = LISTCHUNK;
5826     al    = (char **)fs_get(sizeof(char *) * (slots+1));
5827     ad    = al;
5828 
5829     p = addrfield;
5830 
5831     /* skip any leading whitespace */
5832     for(q = p; *q && *q == SPACE; q++)
5833       ;/* do nothing */
5834 
5835     next_addr = (*q) ? q : NULL;
5836 
5837     /* Loop adding each address in list to array al */
5838     for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
5839 
5840 	next_addr = skip_to_next_addr(cur_addr);
5841 
5842 	q = cur_addr;
5843 	SKIP_SPACE(q);
5844 
5845 	/* allocate more space */
5846 	if((ad-al) >= slots){
5847 	    slots += LISTCHUNK;
5848 	    fs_resize((void **)&al, sizeof(char *) * (slots+1));
5849 	    ad = al + slots - LISTCHUNK;
5850 	}
5851 
5852 	if(*q)
5853 	  *ad++ = cpystr(q);
5854     }
5855 
5856     *ad++ = NULL;
5857 
5858     /* free up any excess we've allocated */
5859     fs_resize((void **)&al, sizeof(char *) * (ad - al));
5860     return(al);
5861 }
5862 
5863 
5864 /*
5865  * Args  cur -- pointer to the start of the current addr in list.
5866  *
5867  * Returns a pointer to the start of the next addr or NULL if there are
5868  * no more addrs.
5869  *
5870  * Side effect: current addr has trailing white space removed
5871  * and is null terminated.
5872  */
5873 char *
skip_to_next_addr(char * cur)5874 skip_to_next_addr(char *cur)
5875 {
5876     register char *p,
5877 		  *q;
5878     char          *ret_pointer;
5879     int in_quotes  = 0,
5880         in_comment = 0;
5881     char prev_char = '\0';
5882 
5883     /*
5884      * Find delimiting comma or end.
5885      * Quoted commas and commented commas don't count.
5886      */
5887     for(q = cur; *q; q++){
5888 	switch(*q){
5889 	  case COMMA:
5890 	    if(!in_quotes && !in_comment)
5891 	      goto found_comma;
5892 	    break;
5893 
5894 	  case LPAREN:
5895 	    if(!in_quotes && !in_comment)
5896 	      in_comment = 1;
5897 	    break;
5898 
5899 	  case RPAREN:
5900 	    if(in_comment && prev_char != BSLASH)
5901 	      in_comment = 0;
5902 	    break;
5903 
5904 	  case QUOTE:
5905 	    if(in_quotes && prev_char != BSLASH)
5906 	      in_quotes = 0;
5907 	    else if(!in_quotes && !in_comment)
5908 	      in_quotes = 1;
5909 	    break;
5910 
5911 	  default:
5912 	    break;
5913 	}
5914 
5915 	prev_char = *q;
5916     }
5917 
5918 found_comma:
5919     if(*q){  /* trailing comma case */
5920 	*q = '\0';
5921 	ret_pointer = q + 1;
5922     }
5923     else
5924       ret_pointer = NULL;  /* no more addrs after cur */
5925 
5926     /* remove trailing white space from cur */
5927     for(p = q - 1; p >= cur && isspace((unsigned char)*p); p--)
5928       *p = '\0';
5929 
5930     return(ret_pointer);
5931 }
5932 
5933 
5934 /*
5935  * Add entries specified by system administrator.  If the nickname already
5936  * exists, it is not touched.
5937  */
5938 void
add_forced_entries(AdrBk * abook)5939 add_forced_entries(AdrBk *abook)
5940 {
5941     AdrBk_Entry *abe;
5942     char *nickname, *fullname, *address;
5943     char *end_of_nick, *end_of_full, **t;
5944 
5945 
5946     if(!ps_global->VAR_FORCED_ABOOK_ENTRY ||
5947        !ps_global->VAR_FORCED_ABOOK_ENTRY[0] ||
5948        !ps_global->VAR_FORCED_ABOOK_ENTRY[0][0])
5949 	return;
5950 
5951     for(t = ps_global->VAR_FORCED_ABOOK_ENTRY; t[0] && t[0][0]; t++){
5952 	nickname = *t;
5953 
5954 	/*
5955 	 * syntax for each element is
5956 	 * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
5957 	 */
5958 
5959 	/* find end of nickname */
5960 	end_of_nick = nickname;
5961 	while(*end_of_nick
5962 	      && !isspace((unsigned char)*end_of_nick)
5963 	      && *end_of_nick != '|')
5964 	  end_of_nick++;
5965 
5966 	/* find the pipe character between nickname and fullname */
5967 	fullname = end_of_nick;
5968 	while(*fullname && *fullname != '|')
5969 	  fullname++;
5970 
5971 	if(*fullname)
5972 	  fullname++;
5973 
5974 	*end_of_nick = '\0';
5975 	abe = adrbk_lookup_by_nick(abook, nickname, NULL);
5976 
5977 	if(!abe){  /* If it isn't there, add it */
5978 
5979 	    /* skip whitespace before fullname */
5980 	    fullname = skip_white_space(fullname);
5981 
5982 	    /* find the pipe character between fullname and address */
5983 	    end_of_full = fullname;
5984 	    while(*end_of_full && *end_of_full != '|')
5985 	      end_of_full++;
5986 
5987 	    if(!*end_of_full){
5988 		dprint((2,
5989 		    "missing | in forced-abook-entry \"%s\"\n",
5990 		    nickname ? nickname : "?"));
5991 		continue;
5992 	    }
5993 
5994 	    address = end_of_full + 1;
5995 
5996 	    /* skip whitespace before address */
5997 	    address = skip_white_space(address);
5998 
5999 	    if(*address == '('){
6000 		dprint((2,
6001 		   "no lists allowed in forced-abook-entry \"%s\"\n",
6002 		   address ? address : "?"));
6003 		continue;
6004 	    }
6005 
6006 	    /* go back and remove trailing white space from fullname */
6007 	    while(*end_of_full == '|' || isspace((unsigned char)*end_of_full)){
6008 		*end_of_full = '\0';
6009 		end_of_full--;
6010 	    }
6011 
6012 	    dprint((2,
6013 	       "Adding forced abook entry \"%s\"\n", nickname ? nickname : ""));
6014 
6015 	    (void)adrbk_add(abook,
6016 			   NO_NEXT,
6017 			   nickname,
6018 			   fullname,
6019 			   address,
6020 			   NULL,
6021 			   NULL,
6022 			   Single,
6023 			   (adrbk_cntr_t *)NULL,
6024 			   (int *)NULL,
6025 			   1,
6026 			   0,
6027 			   1);
6028 	}
6029     }
6030 }
6031 
6032 /* Go through the list of addressbooks and check if any
6033  * of them point to the given stream.
6034  */
6035 int
any_addressbook_in_remote_stream(MAILSTREAM * stream)6036 any_addressbook_in_remote_stream(MAILSTREAM *stream)
6037 {
6038   int rv = 0;
6039   int i = 0, num = 0;
6040   char *nickname = NULL;
6041   char *filename = NULL;
6042   char *q = NULL;
6043 
6044   do{
6045     if(ps_global->VAR_ADDRESSBOOK &&
6046        ps_global->VAR_ADDRESSBOOK[num] &&
6047        ps_global->VAR_ADDRESSBOOK[num][0]){
6048 	q = ps_global->VAR_ADDRESSBOOK[num++];
6049 	i = num;
6050     }
6051     else if(ps_global->VAR_GLOB_ADDRBOOK &&
6052 	    ps_global->VAR_GLOB_ADDRBOOK[i-num] &&
6053 	    ps_global->VAR_GLOB_ADDRBOOK[i-num][0]){
6054 	q = ps_global->VAR_GLOB_ADDRBOOK[i - num];
6055 	i++;
6056     } else q = NULL;
6057     if(q != NULL){
6058       get_pair(q, &nickname, &filename, 0, 0);
6059 
6060       if(nickname) fs_give((void **)&nickname);
6061 
6062       if(filename){
6063 	if(*filename == '{'
6064 	   && same_stream(filename, stream) != NULL)
6065 	 rv = 1;
6066 	fs_give((void **)&filename);
6067       }
6068     }
6069   } while (rv == 0 && q != NULL);
6070 
6071   return rv;
6072 }
6073