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, ×)){
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