Add support for tab-completion when selecting by rule
[alpine.git] / pith / adrbklib.c
blob51099302accc2c1ee77d9da0c3d0c3ccb1ed50e2
1 /* ========================================================================
2 * Copyright 2013-2022 Eduardo Chappa
3 * Copyright 2006-2009 University of Washington
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
14 #include "../pith/headers.h"
15 #include "../pith/adrbklib.h"
16 #include "../pith/abdlc.h"
17 #include "../pith/addrbook.h"
18 #include "../pith/addrstring.h"
19 #include "../pith/state.h"
20 #include "../pith/conf.h"
21 #include "../pith/status.h"
22 #include "../pith/remote.h"
23 #include "../pith/tempfile.h"
24 #include "../pith/bldaddr.h"
25 #include "../pith/signal.h"
26 #include "../pith/busy.h"
27 #include "../pith/util.h"
30 AddrScrState as;
32 void (*pith_opt_save_and_restore)(int, SAVE_STATE_S *);
36 * We don't want any end of line fixups to occur, so include "b" in DOS modes.
38 #if defined(DOS) || defined(OS2)
39 #define ADRBK_NAME "addrbook"
40 #else
41 #define ADRBK_NAME ".addressbook"
42 #endif
44 #define OPEN_WRITE_MODE (O_TRUNC|O_WRONLY|O_CREAT|O_BINARY)
47 #ifndef MAXPATH
48 #define MAXPATH 1000 /* Longest file path we can deal with */
49 #endif
51 #define TABWIDTH 8
52 #define INDENTSTR " "
53 #define INDENTXTRA " : "
54 #define INDENT 3 /* length of INDENTSTR */
57 #define SLOP 3
59 static int writing; /* so we can give understandable error message */
61 AdrBk *edited_abook;
63 static char empty[] = "";
65 jmp_buf jump_over_qsort;
68 /* internal prototypes */
69 int copy_abook_to_tempfile(AdrBk *, char *, size_t);
70 char *dir_containing(char *);
71 char *get_next_abook_entry(FILE *, int);
72 void strip_addr_string(char *, char **, char **);
73 void adrbk_check_local_validity(AdrBk *, long);
74 int write_single_abook_entry(AdrBk_Entry *, FILE *, int *, int *, int *, int *);
75 char *backcompat_encoding_for_abook(char *, size_t, char *, size_t, char *);
76 int percent_abook_saved(void);
77 void exp_del_nth(EXPANDED_S *, a_c_arg_t);
78 void exp_add_nth(EXPANDED_S *, a_c_arg_t);
79 int cmp_ae_by_full_lists_last(const qsort_t *,const qsort_t *);
80 int cmp_cntr_by_full_lists_last(const qsort_t *, const qsort_t *);
81 int cmp_ae_by_full(const qsort_t *, const qsort_t *);
82 int cmp_cntr_by_full(const qsort_t *, const qsort_t *);
83 int cmp_ae_by_nick_lists_last(const qsort_t *,const qsort_t *);
84 int cmp_cntr_by_nick_lists_last(const qsort_t *, const qsort_t *);
85 int cmp_ae_by_nick(const qsort_t *, const qsort_t *);
86 int cmp_cntr_by_nick(const qsort_t *, const qsort_t *);
87 int cmp_addr(const qsort_t *, const qsort_t *);
88 void sort_addr_list(char **);
89 int build_abook_datastruct(AdrBk *, char *, size_t);
90 AdrBk_Entry *init_ae(AdrBk *, AdrBk_Entry *, char *);
91 adrbk_cntr_t count_abook_entries_on_disk(AdrBk *, a_c_arg_t *);
92 AdrBk_Entry *adrbk_get_delae(AdrBk *, a_c_arg_t);
93 void free_ae_parts(AdrBk_Entry *);
94 void add_entry_to_trie(AdrBk_Trie **, char *, a_c_arg_t);
95 int build_abook_tries(AdrBk *, char *);
96 void repair_abook_tries(AdrBk *);
97 adrbk_cntr_t lookup_nickname_in_trie(AdrBk *, char *);
98 adrbk_cntr_t lookup_address_in_trie(AdrBk *, char *);
99 adrbk_cntr_t lookup_in_abook_trie(AdrBk_Trie *, char *);
100 void free_abook_trie(AdrBk_Trie **);
101 adrbk_cntr_t re_sort_particular_entry(AdrBk *, a_c_arg_t);
102 void move_ab_entry(AdrBk *, a_c_arg_t, a_c_arg_t);
103 void insert_ab_entry(AdrBk *, a_c_arg_t, AdrBk_Entry *, int);
104 void delete_ab_entry(AdrBk *, a_c_arg_t, int);
105 void defvalue_ae(AdrBk_Entry *);
109 * Open, read, and parse an address book.
111 * Args: pab -- the PerAddrBook structure
112 * homedir -- the user's home directory if specified
113 * warning -- put "why failed" message to user here
114 * (provide space of at least 201 chars)
116 * If filename is NULL, the default will be used in the homedir
117 * passed in. If homedir is NULL, the current dir will be used.
118 * If filename is not NULL and is an absolute path, just the filename
119 * will be used. Otherwise, it will be used relative to the homedir, or
120 * to the current dir depending on whether or not homedir is NULL.
122 * Expected addressbook file format is:
123 * <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
125 * The last two fields (\t<fcc>\t<comment>) are optional.
127 * Lines that start with SPACE are continuation lines. Ends of lines are
128 * treated as if they were spaces. The address field is either a single
129 * address or a list of comma-separated addresses inside parentheses.
131 * Fields missing from the end of an entry are considered blank.
133 * Commas in the address field will cause problems, as will tabs in any
134 * field.
136 * There may be some deleted entries in the addressbook that don't get
137 * used. They look like regular entries except their nicknames start
138 * with the string "#DELETED-YY/MM/DD#".
140 AdrBk *
141 adrbk_open(PerAddrBook *pab, char *homedir, char *warning, size_t warninglen, int sort_rule)
143 char path[MAXPATH], *filename;
144 AdrBk *ab;
145 int abook_validity_was_minusone = 0;
148 filename = pab ? pab->filename : NULL;
150 dprint((2, "- adrbk_open(%s) -\n", filename ? filename : ""));
152 ab = (AdrBk *)fs_get(sizeof(AdrBk));
153 memset(ab, 0, sizeof(*ab));
155 ab->orig_filename = filename ? cpystr(filename) : NULL;
157 if(pab->type & REMOTE_VIA_IMAP){
158 int try_cache;
160 ab->type = Imap;
162 if(!ab->orig_filename || *(ab->orig_filename) != '{'){
163 dprint((1, "adrbk_open: remote: filename=%s\n",
164 ab->orig_filename ? ab->orig_filename : "NULL"));
165 goto bail_out;
168 if(!(ab->rd = rd_new_remdata(RemImap, ab->orig_filename, REMOTE_ABOOK_SUBTYPE))){
169 dprint((1,
170 "adrbk_open: remote: new_remdata failed: %s\n",
171 ab->orig_filename ? ab->orig_filename : "NULL"));
172 goto bail_out;
175 /* Transfer responsibility for the storage object */
176 ab->rd->so = pab->so;
177 pab->so = NULL;
179 try_cache = rd_read_metadata(ab->rd);
181 if(ab->rd->lf)
182 ab->filename = cpystr(ab->rd->lf);
184 /* Transfer responsibility for removal of temp file */
185 if(ab->rd->flags & DEL_FILE){
186 ab->flags |= DEL_FILE;
187 ab->rd->flags &= ~DEL_FILE;
190 if(pab->access == MaybeRorW){
191 if(ab->rd->read_status == 'R')
192 pab->access = ab->rd->access = ReadOnly;
193 else
194 pab->access = ab->rd->access = ReadWrite;
196 else if(pab->access == ReadOnly){
198 * Pass on readonly-ness from being a global addrbook.
199 * This should cause us to open the remote folder readonly,
200 * avoiding error messages about readonly-ness.
202 ab->rd->access = ReadOnly;
206 * The plan is to fetch addrbook data and copy into local file.
207 * Then we open the local copy for reading. We use the IMAP STATUS
208 * command to tell us if we need to update from the remote addrbook.
210 * If access is NoExists, that probably means we had trouble
211 * opening the remote folder in the adrbk_access routine.
212 * In that case we'll use a cached copy if we have one.
214 if(pab->access != NoExists){
216 bootstrap_nocheck_policy:
217 if(try_cache && ps_global->remote_abook_validity == -1 &&
218 !abook_validity_was_minusone)
219 abook_validity_was_minusone++;
220 else{
221 rd_check_remvalid(ab->rd, 1L);
222 abook_validity_was_minusone = 0;
226 * If the cached info on this addrbook says it is readonly but
227 * it looks like it's been fixed now, change it to readwrite.
229 if(!(pab->type & GLOBAL) && ab->rd->read_status == 'R'){
231 * We go to this trouble since readonly addrbooks
232 * are likely a mistake. They are usually supposed to be
233 * readwrite. So we open it and check if it's been fixed.
235 rd_check_readonly_access(ab->rd);
236 if(ab->rd->read_status == 'W'){
237 pab->access = ab->rd->access = ReadWrite;
238 ab->rd->flags |= REM_OUTOFDATE;
240 else
241 pab->access = ab->rd->access = ReadOnly;
244 if(ab->rd->flags & REM_OUTOFDATE){
245 if(rd_update_local(ab->rd) != 0){
246 dprint((1,
247 "adrbk_open: remote: rd_update_local failed\n"));
249 * Don't give up altogether. We still may be
250 * able to use a cached copy.
253 else{
254 dprint((7,
255 "%s: copied remote to local (%ld)\n",
256 ab->rd->rn ? ab->rd->rn : "?",
257 (long)ab->rd->last_use));
261 if(pab->access == ReadWrite)
262 ab->rd->flags |= DO_REMTRIM;
265 /* If we couldn't get to remote folder, try using the cached copy */
266 if(pab->access == NoExists || ab->rd->flags & REM_OUTOFDATE){
267 if(try_cache){
268 pab->access = ab->rd->access = ReadOnly;
269 ab->rd->flags |= USE_OLD_CACHE;
270 q_status_message(SM_ORDER, 3, 4,
271 _("Can't contact remote address book server, using cached copy"));
272 dprint((2,
273 "Can't open remote addrbook %s, using local cached copy %s readonly\n",
274 ab->rd->rn ? ab->rd->rn : "?",
275 ab->rd->lf ? ab->rd->lf : "?"));
277 else
278 goto bail_out;
281 else{
282 ab->type = Local;
284 /*------------ figure out and save name of file to open ---------*/
285 if(filename == NULL){
286 if(homedir != NULL){
287 build_path(path, homedir, ADRBK_NAME, sizeof(path));
288 ab->filename = cpystr(path);
290 else
291 ab->filename = cpystr(ADRBK_NAME);
293 else{
294 if(is_absolute_path(filename)){
295 ab->filename = cpystr(filename);
297 else{
298 if(homedir != NULL){
299 build_path(path, homedir, filename, sizeof(path));
300 ab->filename = cpystr(path);
302 else
303 ab->filename = cpystr(filename);
308 if(ab->filename && ab->filename[0]){
309 char buf[MAXPATH];
311 strncpy(buf, ab->filename, sizeof(buf)-4);
312 buf[sizeof(buf)-4] = '\0';
315 * Our_filecopy is used in _WINDOWS to allow
316 * multiple pines to update the address book. The problem is
317 * that if a file is open it can't be deleted, so we need to keep
318 * the main filename closed most of the time.
319 * In Unix, our_filecopy just points to filename.
322 #ifdef _WINDOWS
324 * If we can't write in the same directory as filename is in, put
325 * the copies in /tmp instead.
327 if(!(ab->our_filecopy = tempfile_in_same_dir(ab->filename, "a3", NULL)))
328 ab->our_filecopy = temp_nam(NULL, "a3");
329 #endif /* _WINDOWS */
332 * We don't need the copies on Unix because we can rename/delete
333 * open files. Turn the feature off by making the copies point to
334 * the originals.
336 if(!ab->our_filecopy)
337 ab->our_filecopy = ab->filename;
339 else{
340 dprint((1, "adrbk_open: ab->filename is NULL???\n"));
341 goto bail_out;
346 * We're going to make our own copy of the address book file so that
347 * we won't conflict with other instances of pine trying to change it.
348 * In particular, on Windows the address book file cannot be deleted
349 * or renamed into if it is open in another process.
351 ab->flags |= FILE_OUTOFDATE;
352 if(copy_abook_to_tempfile(ab, warning, warninglen) < 0){
353 dprint((1, "adrbk_open: copy_file failed\n"));
354 if(abook_validity_was_minusone){
356 * The file copy failed when it shouldn't have. If the user has
357 * remote_abook_validity == -1 then we'll go back and try to
358 * do the validity check in case that can get us the file we
359 * need to copy. Without the validity check first time we won't
360 * contact the imap server.
362 dprint((1, "adrbk_open: trying to bootstrap\n"));
364 if(ab->our_filecopy){
365 if(ab->our_filecopy != ab->filename){
366 our_unlink(ab->our_filecopy);
367 fs_give((void **)&ab->our_filecopy);
370 ab->our_filecopy = NULL;
373 goto bootstrap_nocheck_policy;
376 goto bail_out;
379 if(!ab->fp)
380 goto bail_out;
382 ab->sort_rule = sort_rule;
383 if(pab->access == ReadOnly)
384 ab->sort_rule = AB_SORT_RULE_NONE;
386 if(ab){
387 /* allocate header for expanded lists list */
388 ab->exp = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
389 /* first real element is NULL */
390 ab->exp->next = (EXPANDED_S *)NULL;
392 /* allocate header for checked entries list */
393 ab->checks = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
394 /* first real element is NULL */
395 ab->checks->next = (EXPANDED_S *)NULL;
397 /* allocate header for selected entries list */
398 ab->selects = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
399 /* first real element is NULL */
400 ab->selects->next = (EXPANDED_S *)NULL;
402 return(ab);
405 bail_out:
406 dprint((2, "adrbk_open: bailing: filenames=%s %s %s fp=%s\n",
407 ab->orig_filename ? ab->orig_filename : "NULL",
408 ab->filename ? ab->filename : "NULL",
409 ab->our_filecopy ? ab->our_filecopy : "NULL",
410 ab->fp ? "open" : "NULL"));
412 if(ab->rd){
413 ab->rd->flags &= ~DO_REMTRIM;
414 rd_close_remdata(&ab->rd);
417 if(ab->fp)
418 (void)fclose(ab->fp);
420 if(ab->orig_filename)
421 fs_give((void **) &ab->orig_filename);
423 if(ab->our_filecopy && ab->our_filecopy != ab->filename){
424 our_unlink(ab->our_filecopy);
425 fs_give((void **) &ab->our_filecopy);
428 if(ab->filename)
429 fs_give((void **) &ab->filename);
431 if(pab->so){
432 so_give(&(pab->so));
433 pab->so = NULL;
436 fs_give((void **) &ab);
438 return NULL;
443 * Copy the address book file to the temporary session copy. Also copy
444 * the hashfile. Any of these files which don't exist will be created.
446 * Returns 0 success
447 * -1 failure
450 copy_abook_to_tempfile(AdrBk *ab, char *warning, size_t warninglen)
452 int got_it, fd, c,
453 ret = -1,
454 we_cancel = 0;
455 FILE *fp_read = (FILE *)NULL,
456 *fp_write = (FILE *)NULL;
457 char *lc;
458 time_t mtime;
461 dprint((3, "copy_file(%s) -\n",
462 (ab && ab->filename) ? ab->filename : ""));
464 if(!ab || !ab->filename || !ab->filename[0])
465 goto get_out;
467 if(!(ab->flags & FILE_OUTOFDATE))
468 return(0);
470 /* open filename for reading */
471 fp_read = our_fopen(ab->filename, "rb");
472 if(fp_read == NULL){
475 * filename probably doesn't exist so we try to create it
478 /* don't want to create in these cases, should already be there */
479 if(ab->type == Imap){
480 if(warning){
481 if(ab->type == Imap){
482 snprintf(warning, warninglen,
483 /* TRANSLATORS: A temporary file for the address book can't
484 be opened. */
485 _("Temp addrbook file can't be opened: %s"),
486 ab->filename);
487 warning[warninglen-1] = '\0';
489 else{
490 strncpy(warning, _("Address book doesn't exist"), warninglen);
491 warning[warninglen-1] = '\0';
495 goto get_out;
498 q_status_message1(SM_INFO, 0, 3,
499 _("Address book %.200s doesn't exist, creating"),
500 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
501 dprint((2, "Address book %s doesn't exist, creating\n",
502 ab->filename ? ab->filename : "?"));
505 * Just use user's umask for permissions. Mode is "w" so the create
506 * will happen. We close it right after creating and open it in
507 * read mode again later.
509 fp_read = our_fopen(ab->filename, "wb"); /* create */
510 if(fp_read == NULL ||
511 fclose(fp_read) == EOF ||
512 (fp_read = our_fopen(ab->filename, "rb")) == NULL){
513 /*--- Create failed, bail out ---*/
514 if(warning){
515 strncpy(warning, error_description(errno), warninglen);
516 warning[warninglen-1] = '\0';
519 dprint((2, "create failed: %s\n",
520 error_description(errno)));
522 goto get_out;
526 /* record new change date of addrbook file */
527 ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
529 ab->last_local_valid_chk = get_adj_time();
532 /* now there is an ab->filename and we have it open for reading */
534 got_it = 0;
536 /* copy ab->filename to ab->our_filecopy, preserving mtime */
537 if(ab->filename != ab->our_filecopy){
538 struct stat sbuf;
539 struct utimbuf times;
540 int valid_stat = 0;
542 dprint((7, "Before abook copies\n"));
543 if((fd = our_open(ab->our_filecopy, OPEN_WRITE_MODE, 0600)) < 0)
544 goto get_out;
546 fp_write = fdopen(fd, "wb");
547 rewind(fp_read);
548 if(fstat(fileno(fp_read), &sbuf)){
549 q_status_message1(SM_INFO, 0, 3,
550 "Error: can't stat addrbook \"%.200s\"",
551 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
552 dprint((2, "Error: can't stat addrbook \"%s\"\n",
553 ab->filename ? ab->filename : "?"));
555 else{
556 valid_stat++;
557 times.actime = sbuf.st_atime;
558 times.modtime = sbuf.st_mtime;
561 while((c = getc(fp_read)) != EOF)
562 if(putc(c, fp_write) == EOF)
563 goto get_out;
565 (void)fclose(fp_write);
566 fp_write = (FILE *)NULL;
567 if(valid_stat && our_utime(ab->our_filecopy, &times)){
568 q_status_message1(SM_INFO, 0, 3,
569 "Error: can't set mtime for \"%.200s\"",
570 (lc=last_cmpnt(ab->filename)) ? lc : ab->our_filecopy);
571 dprint((2, "Error: can't set mtime for \"%s\"\n",
572 ab->our_filecopy ? ab->our_filecopy : "?"));
575 (void)fclose(fp_read);
576 fp_read = (FILE *)NULL;
577 if(!(ab->fp = our_fopen(ab->our_filecopy, "rb")))
578 goto get_out;
580 dprint((7, "After abook file copy\n"));
582 else{ /* already open to the right file */
583 ab->fp = fp_read;
584 fp_read = (FILE *)NULL;
588 * Now we've copied filename to our_filecopy.
589 * Operate on the copy now. Ab->fp is open readonly on
590 * our_filecopy.
593 got_it = 0;
594 mtime = get_adj_name_file_mtime(ab->our_filecopy);
595 we_cancel = busy_cue(NULL, NULL, 1);
596 if(build_abook_datastruct(ab, (warning && !*warning) ? warning : NULL, warninglen)){
597 if(we_cancel)
598 cancel_busy_cue(-1);
600 dprint((2, "failed in build_abook_datastruct\n"));
601 goto get_out;
604 if(ab->arr
605 && build_abook_tries(ab, (warning && !*warning) ? warning : NULL)){
606 if(we_cancel)
607 cancel_busy_cue(-1);
609 dprint((2, "failed in build_abook_tries\n"));
610 goto get_out;
613 if(we_cancel)
614 cancel_busy_cue(-1);
616 ab->flags &= ~FILE_OUTOFDATE; /* turn off out of date flag */
617 ret = 0;
619 get_out:
620 if(fp_read)
621 (void)fclose(fp_read);
623 if(fp_write)
624 (void)fclose(fp_write);
626 if(ret < 0 && ab){
627 if(ab->our_filecopy && ab->our_filecopy != ab->filename)
628 our_unlink(ab->our_filecopy);
631 return(ret);
636 * Returns an allocated copy of the directory which contains filename.
638 char *
639 dir_containing(char *filename)
641 char dir[MAXPATH+1];
642 char *dirp = NULL;
644 if(filename){
645 char *lc;
647 if((lc = last_cmpnt(filename)) != NULL){
648 int to_copy;
650 to_copy = (lc - filename > 1) ? (lc - filename - 1) : 1;
651 strncpy(dir, filename, MIN(to_copy, sizeof(dir)-1));
652 dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
654 else{
655 dir[0] = '.';
656 dir[1] = '\0';
659 dirp = cpystr(dir);
662 return(dirp);
667 * Checks whether or not the addrbook is sorted correctly according to
668 * the SortType. Returns 1 if is sorted correctly, 0 otherwise.
671 adrbk_is_in_sort_order(AdrBk *ab, int be_quiet)
673 adrbk_cntr_t entry;
674 AdrBk_Entry *ae, *ae_prev;
675 int (*cmp_func)();
676 int we_cancel = 0;
678 dprint((9, "- adrbk_is_in_sort_order -\n"));
680 if(!ab)
681 return 0;
683 if(ab->sort_rule == AB_SORT_RULE_NONE || ab->count < 2)
684 return 1;
686 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
687 cmp_ae_by_full_lists_last :
688 (ab->sort_rule == AB_SORT_RULE_FULL) ?
689 cmp_ae_by_full :
690 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
691 cmp_ae_by_nick_lists_last :
692 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
693 cmp_ae_by_nick;
695 ae_prev = adrbk_get_ae(ab, (a_c_arg_t) 0);
697 if(!be_quiet)
698 we_cancel = busy_cue(NULL, NULL, 1);
700 for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t) entry);
701 ae != (AdrBk_Entry *)NULL;
702 ae = adrbk_get_ae(ab, (a_c_arg_t) (++entry))){
704 if((*cmp_func)((qsort_t *)&ae_prev, (qsort_t *)&ae) > 0){
705 if(we_cancel)
706 cancel_busy_cue(-1);
708 dprint((9, "- adrbk_is_in_sort_order : no (entry %ld) -\n", (long) entry));
709 return 0;
712 ae_prev = ae;
715 if(we_cancel)
716 cancel_busy_cue(-1);
718 dprint((9, "- adrbk_is_in_sort_order : yes -\n"));
720 return 1;
725 * Look through the ondisk address book and count the number of entries.
727 * Args ab -- address book pointer
728 * deleted -- pointer to location to return number of deleted entries
729 * warning -- place to put warning
731 * Returns number of non-deleted entries.
733 adrbk_cntr_t
734 count_abook_entries_on_disk(AdrBk *ab, a_c_arg_t *deleted)
736 FILE *fp_in;
737 char *nickname;
738 adrbk_cntr_t count = 0;
739 adrbk_cntr_t deleted_count = 0;
740 int rew = 1;
741 char nickbuf[50];
743 if(!ab || !ab->fp)
744 return -1;
746 fp_in = ab->fp;
748 while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
749 rew = 0;
750 strncpy(nickbuf, nickname, sizeof(nickbuf));
751 nickbuf[sizeof(nickbuf)-1] = '\0';
752 fs_give((void **) &nickname);
753 if(strncmp(nickbuf, DELETED, DELETED_LEN) == 0
754 && isdigit((unsigned char)nickbuf[DELETED_LEN])
755 && isdigit((unsigned char)nickbuf[DELETED_LEN+1])
756 && nickbuf[DELETED_LEN+2] == '/'
757 && isdigit((unsigned char)nickbuf[DELETED_LEN+3])
758 && isdigit((unsigned char)nickbuf[DELETED_LEN+4])
759 && nickbuf[DELETED_LEN+5] == '/'
760 && isdigit((unsigned char)nickbuf[DELETED_LEN+6])
761 && isdigit((unsigned char)nickbuf[DELETED_LEN+7])
762 && nickbuf[DELETED_LEN+8] == '#'){
763 deleted_count++;
764 continue;
767 count++;
770 if(deleted)
771 *deleted = (a_c_arg_t) deleted_count;
773 return(count);
778 * Builds the data structures used with the address book which is
779 * simply an array of entries.
782 build_abook_datastruct(AdrBk *ab, char *warning, size_t warninglen)
784 FILE *fp_in;
785 char *nickname;
786 char *lc;
787 a_c_arg_t count, deleted;
788 adrbk_cntr_t used = 0, delused = 0;
789 int max_nick = 0,
790 max_full = 0, full_two = 0, full_three = 0,
791 max_fcc = 0, fcc_two = 0, fcc_three = 0,
792 max_addr = 0, addr_two = 0, addr_three = 0,
793 this_nick_width, this_full_width, this_addr_width,
794 this_fcc_width;
795 WIDTH_INFO_S *widths;
796 int rew = 1, is_deleted;
797 AdrBk_Entry *ae;
799 dprint((9, "- build_abook_datastruct -\n"));
801 if(!ab || !ab->fp)
802 return -1;
804 errno = 0;
806 fp_in = ab->fp;
809 * If we used a list instead of an array to store the entries we
810 * could avoid this pass through the file to count the entries, which
811 * we use to allocate the array.
813 * Since we use entry_nums a lot to access address book entries it is
814 * convenient to have an array for quick access, so we'll probably
815 * leave this for now.
817 count = count_abook_entries_on_disk(ab, &deleted);
819 if(count < 0)
820 return -1;
822 ab->count = (adrbk_cntr_t) count;
823 ab->del_count = (adrbk_cntr_t) deleted;
825 if(count > 0){
826 ab->arr = (AdrBk_Entry *) fs_get(count * sizeof(AdrBk_Entry));
827 memset(ab->arr, 0, count * sizeof(AdrBk_Entry));
830 if(deleted > 0){
831 ab->del = (AdrBk_Entry *) fs_get(deleted * sizeof(AdrBk_Entry));
832 memset(ab->del, 0, deleted * sizeof(AdrBk_Entry));
835 while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
838 ae = NULL;
839 rew = 0;
840 is_deleted = 0;
842 if(strncmp(nickname, DELETED, DELETED_LEN) == 0
843 && isdigit((unsigned char)nickname[DELETED_LEN])
844 && isdigit((unsigned char)nickname[DELETED_LEN+1])
845 && nickname[DELETED_LEN+2] == '/'
846 && isdigit((unsigned char)nickname[DELETED_LEN+3])
847 && isdigit((unsigned char)nickname[DELETED_LEN+4])
848 && nickname[DELETED_LEN+5] == '/'
849 && isdigit((unsigned char)nickname[DELETED_LEN+6])
850 && isdigit((unsigned char)nickname[DELETED_LEN+7])
851 && nickname[DELETED_LEN+8] == '#'){
852 is_deleted++;
855 ALARM_BLIP();
856 if(!is_deleted && (long) used > MAX_ADRBK_SIZE){
857 q_status_message2(SM_ORDER | SM_DING, 4, 5,
858 "Max addrbook size is %.200s, %.200s too large, giving up",
859 long2string(MAX_ADRBK_SIZE),
860 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
861 dprint((1, "build_ondisk: used=%ld > %s\n",
862 (long) used, long2string(MAX_ADRBK_SIZE)));
863 goto io_err;
866 if(is_deleted){
867 if(delused < ab->del_count)
868 ae = &ab->del[delused++];
870 else{
871 if(used < ab->count)
872 ae = &ab->arr[used++];
875 if(ae)
876 init_ae(ab, ae, nickname);
878 fs_give((void **) &nickname);
880 if(!ae || is_deleted)
881 continue;
884 * We're calculating the widths as we read in the data.
885 * We could just go with some default widths to save time.
887 this_nick_width = 0;
888 this_full_width = 0;
889 this_addr_width = 0;
890 this_fcc_width = 0;
892 if(ae->nickname)
893 this_nick_width = (int) utf8_width(ae->nickname);
895 if(ae->fullname)
896 this_full_width = (int) utf8_width(ae->fullname);
898 if(ae->tag == Single){
899 if(ae->addr.addr)
900 this_addr_width = (int) utf8_width(ae->addr.addr);
902 else{
903 char **a2;
905 this_addr_width = 0;
906 for(a2 = ae->addr.list; *a2 != NULL; a2++)
907 this_addr_width = MAX(this_addr_width, (int) utf8_width(*a2));
910 if(ae->fcc)
911 this_fcc_width = (int) utf8_width(ae->fcc);
913 max_nick = MAX(max_nick, this_nick_width);
915 if(this_full_width > max_full){
916 full_three = full_two;
917 full_two = max_full;
918 max_full = this_full_width;
920 else if(this_full_width > full_two){
921 full_three = full_two;
922 full_two = this_full_width;
924 else if(this_full_width > full_three){
925 full_three = this_full_width;
928 if(this_addr_width > max_addr){
929 addr_three = addr_two;
930 addr_two = max_addr;
931 max_addr = this_addr_width;
933 else if(this_addr_width > addr_two){
934 addr_three = addr_two;
935 addr_two = this_addr_width;
937 else if(this_addr_width > addr_three){
938 addr_three = this_addr_width;
941 if(this_fcc_width > max_fcc){
942 fcc_three = fcc_two;
943 fcc_two = max_fcc;
944 max_fcc = this_fcc_width;
946 else if(this_fcc_width > fcc_two){
947 fcc_three = fcc_two;
948 fcc_two = this_fcc_width;
950 else if(this_fcc_width > fcc_three){
951 fcc_three = this_fcc_width;
955 widths = &ab->widths;
956 widths->max_nickname_width = MIN(max_nick, 99);
957 widths->max_fullname_width = MIN(max_full, 99);
958 widths->max_addrfield_width = MIN(max_addr, 99);
959 widths->max_fccfield_width = MIN(max_fcc, 99);
960 widths->third_biggest_fullname_width = MIN(full_three, 99);
961 widths->third_biggest_addrfield_width = MIN(addr_three, 99);
962 widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
964 dprint((9, "- build_abook_datastruct done -\n"));
965 return 0;
967 io_err:
968 if(warning && errno != 0){
969 strncpy(warning, error_description(errno), warninglen);
970 warning[warninglen-1] = '\0';
973 dprint((1, "build_ondisk: io_err: %s\n",
974 error_description(errno)));
976 return -1;
981 * Builds the trees used for nickname and address lookups.
984 build_abook_tries(AdrBk *ab, char *warning)
986 adrbk_cntr_t entry_num;
987 AdrBk_Entry *ae;
988 int we_cancel = 0;
990 if(!ab)
991 return -1;
993 dprint((9, "- build_abook_tries(%s) -\n", ab->filename));
996 if(ab->nick_trie)
997 free_abook_trie(&ab->nick_trie);
999 if(ab->addr_trie)
1000 free_abook_trie(&ab->addr_trie);
1002 if(ab->full_trie)
1003 free_abook_trie(&ab->full_trie);
1005 if(ab->revfull_trie)
1006 free_abook_trie(&ab->revfull_trie);
1009 * Go through addrbook entries and add each to the tries it
1010 * belongs in.
1012 for(entry_num = 0; entry_num < ab->count; entry_num++){
1013 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1014 if(ae){
1015 /* every nickname in the nick trie */
1016 if(ae->nickname && ae->nickname[0])
1017 add_entry_to_trie(&ab->nick_trie, ae->nickname, (a_c_arg_t) entry_num);
1019 if(ae->fullname && ae->fullname[0]){
1020 char *reverse = NULL;
1021 char *forward = NULL;
1022 char *comma = NULL;
1025 * We have some fullnames stored as Last, First. Put both in.
1027 if(ae->fullname[0] != '"'
1028 && (comma=strindex(ae->fullname, ',')) != NULL
1029 && comma - ae->fullname > 0){
1030 forward = adrbk_formatname(ae->fullname, NULL, NULL);
1031 if(forward && forward[0]){
1032 *comma = '\0';
1033 reverse = cpystr(ae->fullname);
1034 *comma = ',';
1036 else{
1037 if(forward)
1038 fs_give((void **) &forward);
1040 forward = ae->fullname;
1043 else
1044 forward = ae->fullname;
1046 if(forward){
1047 char *addthis;
1050 * Make this add not only the full name as is (forward) but
1051 * also add the middle name and last name (names after spaces).
1052 * so that they'll be found.
1054 for(addthis=forward;
1055 addthis && (*addthis);
1056 addthis = strindex(addthis, ' ')){
1057 while(*addthis == ' ')
1058 addthis++;
1060 if(*addthis)
1061 add_entry_to_trie(&ab->full_trie, addthis, (a_c_arg_t) entry_num);
1065 if(reverse){
1066 add_entry_to_trie(&ab->revfull_trie, reverse, (a_c_arg_t) entry_num);
1067 fs_give((void **) &reverse);
1070 if(forward && forward != ae->fullname)
1071 fs_give((void **) &forward);
1074 if(ae->tag == Single && ae->addr.addr && ae->addr.addr[0]){
1075 char buf[1000];
1076 char *tmp_a_string, *simple_addr = NULL;
1077 ADDRESS *addr = NULL;
1078 char *fakedomain = "@";
1081 * Isolate the actual address out of ae.
1083 tmp_a_string = cpystr(ae->addr.addr);
1084 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1085 if(tmp_a_string)
1086 fs_give((void **) &tmp_a_string);
1088 if(addr){
1089 if(addr->mailbox && addr->host
1090 && !(addr->host[0] == '@' && addr->host[1] == '\0'))
1091 simple_addr = simple_addr_string(addr, buf, sizeof(buf));
1094 * If the fullname wasn't set in the addrbook entry there
1095 * may still be one that is part of the address. We need
1096 * to be careful because we may be in the middle of opening
1097 * the address book right now. Don't call something like
1098 * our_build_address because it will probably re-open this
1099 * same addrbook infinitely.
1101 if(!(ae->fullname && ae->fullname[0])
1102 && addr->personal && addr->personal[0])
1103 add_entry_to_trie(&ab->full_trie, addr->personal,
1104 (a_c_arg_t) entry_num);
1106 mail_free_address(&addr);
1109 if(simple_addr)
1110 add_entry_to_trie(&ab->addr_trie, simple_addr, (a_c_arg_t) entry_num);
1115 dprint((9, "- build_abook_tries done -\n"));
1117 if(we_cancel)
1118 cancel_busy_cue(-1);
1120 return 0;
1124 void
1125 add_entry_to_trie(AdrBk_Trie **head, char *str, a_c_arg_t entry_num)
1127 AdrBk_Trie *temp, *trail;
1128 char *addthis, *p, buf[1000];
1130 if(!head || !str || !*str)
1131 return;
1133 /* add as lower case */
1135 for(p = str; *p && ((*p & 0x80) || !isupper((unsigned char) *p)); p++)
1138 if(*p){
1139 strncpy(buf, str, sizeof(buf));
1140 buf[sizeof(buf)-1] = '\0';
1141 for(p = buf; *p; p++)
1142 if(!(*p & 0x80) && isupper((unsigned char) *p))
1143 *p = tolower(*p);
1145 addthis = buf;
1147 else
1148 addthis = str;
1150 temp = trail = (*head);
1153 * Find way down the trie, adding missing nodes as we go.
1155 for(p = addthis; *p;){
1156 if(temp == NULL){
1157 temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1158 memset(temp, 0, sizeof(*temp));
1159 temp->value = *p;
1160 temp->entrynum = NO_NEXT;
1162 if(*head == NULL)
1163 *head = temp;
1164 else
1165 trail->down = temp;
1167 else{
1168 while((temp != NULL) && (temp->value != *p)){
1169 trail = temp;
1170 temp = temp->right;
1173 /* wasn't there, add new node */
1174 if(temp == NULL){
1175 temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1176 memset(temp, 0, sizeof(*temp));
1177 temp->value = *p;
1178 temp->entrynum = NO_NEXT;
1179 trail->right = temp;
1183 if(*(++p)){
1184 trail = temp;
1185 temp = temp->down;
1190 * If entrynum is already filled in there must be an entry with
1191 * the same nickname earlier in the abook. Use that earlier entry.
1193 if(temp != NULL && temp->entrynum == NO_NEXT)
1194 temp->entrynum = (adrbk_cntr_t) entry_num;
1199 * Returns entry_num of first entry with this nickname, else NO_NEXT.
1201 adrbk_cntr_t
1202 lookup_nickname_in_trie(AdrBk *ab, char *nickname)
1204 if(!ab || !nickname || !ab->nick_trie)
1205 return(-1L);
1207 return(lookup_in_abook_trie(ab->nick_trie, nickname));
1212 * Returns entry_num of first entry with this address, else NO_NEXT.
1214 adrbk_cntr_t
1215 lookup_address_in_trie(AdrBk *ab, char *address)
1217 dprint((9, "lookup_address_in_trie: %s\n", ab ? (ab->addr_trie ? (address ? address : "?") : "null addr_trie") : "null ab"));
1218 if(!ab || !address || !ab->addr_trie)
1219 return(-1L);
1221 return(lookup_in_abook_trie(ab->addr_trie, address));
1225 adrbk_cntr_t
1226 lookup_in_abook_trie(AdrBk_Trie *t, char *str)
1228 char *p, *lookthisup;
1229 char buf[1000];
1230 adrbk_cntr_t ret = NO_NEXT;
1232 if(!t || !str)
1233 return(ret);
1235 /* make lookup case independent */
1237 for(p = str; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
1240 if(*p){
1241 strncpy(buf, str, sizeof(buf));
1242 buf[sizeof(buf)-1] = '\0';
1243 for(p = buf; *p; p++)
1244 if(!(*p & 0x80) && isupper((unsigned char) *p))
1245 *p = tolower(*p);
1247 lookthisup = buf;
1249 else
1250 lookthisup = str;
1252 p = lookthisup;
1255 * We usually return out from inside the loop (unless str == "").
1257 while(*p){
1258 /* search for character at this level */
1259 while(t->value != *p){
1260 if(t->right == NULL)
1261 return(ret); /* no match */
1263 t = t->right;
1266 if(*++p == '\0') /* end of str, a match */
1267 return(t->entrynum);
1269 /* need to go down to match next character */
1270 if(t->down == NULL) /* no match */
1271 return(ret);
1273 t = t->down;
1276 return(ret);
1280 void
1281 free_abook_trie(AdrBk_Trie **trie)
1283 if(trie){
1284 if(*trie){
1285 free_abook_trie(&(*trie)->down);
1286 free_abook_trie(&(*trie)->right);
1287 fs_give((void **) trie);
1294 * Returns pointer to start of next address book entry from disk file.
1295 * The return will be in raw form from the file with newlines still
1296 * embedded. Or NULL at end of file.
1298 * If rew is set, rewind the file and start over at beginning.
1300 char *
1301 get_next_abook_entry(FILE *fp, int rew)
1303 char *returned_lines = NULL, *p;
1304 char line[1024];
1305 static int will_be_done_next_time = 0;
1306 static long next_nickname_offset = 0L;
1307 size_t lsize;
1308 long offset, saved_offset;
1309 long len = 0L;
1311 #define CHUNKSIZE 500
1313 lsize = sizeof(line);
1315 if(rew){
1316 will_be_done_next_time = 0;
1317 rewind(fp);
1318 /* skip leading (bogus) continuation lines */
1320 offset = ftell(fp);
1321 line[0] = '\0';
1322 line[lsize-2] = '\0';
1323 p = fgets(line, lsize, fp);
1325 if(p == NULL)
1326 return(NULL);
1328 /* line is too long to fit, read the rest and discard */
1329 while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1330 && p != NULL){
1332 /* get next lsize-1 characters, leaving line[0] */
1333 line[lsize-2] = '\0';
1334 p = fgets(line+1, lsize-1, fp);
1336 }while(line[0] == SPACE);
1338 /* offset is start of first good line now */
1339 next_nickname_offset = offset;
1342 if(will_be_done_next_time)
1343 return(NULL);
1345 /* we set this up in rew==1 case or on previous call to this routine */
1346 offset = next_nickname_offset;
1349 * The rest is working on finding the start of the next entry so
1350 * skip continuation lines
1353 next_nickname_offset = ftell(fp);
1354 line[0] = '\0';
1355 line[lsize-2] = '\0';
1356 p = fgets(line, lsize, fp);
1358 /* line is too long to fit, read the rest and discard */
1359 while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1360 && p != NULL){
1362 /* get next lsize-1 characters, leaving line[0] */
1363 line[lsize-2] = '\0';
1364 p = fgets(line+1, lsize-1, fp);
1366 }while(line[0] == SPACE);
1368 /* next_nickname_offset is start of next entry now */
1370 if(!line[0])
1371 will_be_done_next_time = 1;
1373 len = next_nickname_offset - offset;
1375 returned_lines = (char *) fs_get((len + 1) * sizeof(char));
1377 saved_offset = ftell(fp);
1378 if(fseek(fp, offset, 0)){
1379 dprint((2, "get_next_ab_entry: trouble fseeking\n"));
1380 len = 0;
1381 fs_give((void **) &returned_lines);
1383 else{
1384 if(fread(returned_lines, sizeof(char), (unsigned) len, fp) != len){
1385 dprint((2, "get_next_ab_entry: trouble freading\n"));
1386 len = 0;
1387 fs_give((void **) &returned_lines);
1391 if(fseek(fp, saved_offset, 0)){
1392 dprint((2, "get_next_ab_entry: trouble fseeking to saved_offset\n"));
1393 len = 0;
1394 fs_give((void **) &returned_lines);
1397 if(returned_lines)
1398 returned_lines[len] = '\0';
1400 return(returned_lines);
1405 * Returns a pointer to the start of the mailbox@host part of this
1406 * address string, and a pointer to the end + 1. The caller can then
1407 * replace the end char with \0 and call the hash function, then put
1408 * back the end char. Start_addr and end_addr are assumed to be non-null.
1410 void
1411 strip_addr_string(char *addrstr, char **start_addr, char **end_addr)
1413 register char *q;
1414 int in_quotes = 0,
1415 in_comment = 0;
1416 char prev_char = '\0';
1418 if(!addrstr || !*addrstr){
1419 *start_addr = NULL;
1420 *end_addr = NULL;
1421 return;
1424 *start_addr = addrstr;
1426 for(q = addrstr; *q; q++){
1427 switch(*q){
1428 case '<':
1429 if(!in_quotes && !in_comment){
1430 if(*++q){
1431 *start_addr = q;
1432 /* skip to > */
1433 while(*q && *q != '>')
1434 q++;
1436 /* found > */
1437 if(*q){
1438 *end_addr = q;
1439 return;
1441 else
1442 q--;
1446 break;
1448 case LPAREN:
1449 if(!in_quotes && !in_comment)
1450 in_comment = 1;
1451 break;
1453 case RPAREN:
1454 if(in_comment && prev_char != BSLASH)
1455 in_comment = 0;
1456 break;
1458 case QUOTE:
1459 if(in_quotes && prev_char != BSLASH)
1460 in_quotes = 0;
1461 else if(!in_quotes && !in_comment)
1462 in_quotes = 1;
1463 break;
1465 default:
1466 break;
1469 prev_char = *q;
1472 *end_addr = q;
1477 * Fill in the passed in ae pointer by parsing the str that is passed.
1479 * Args ab --
1480 * ae -- pointer we want to fill in. The individual members of
1481 * the ae struct will be allocated here
1482 * str -- the string from the on-disk address book file for this entry
1484 * Returns a pointer to ae or NULL if there are problems.
1486 AdrBk_Entry *
1487 init_ae(AdrBk *ab, AdrBk_Entry *a, char *str)
1489 char *p;
1490 char *addrfield = (char *) NULL;
1491 char *addrfield_end;
1492 char *nickname, *fullname, *fcc, *extra;
1494 if(!ab){
1495 dprint((2, "init_ae: found trouble: NULL ab\n"));
1496 return((AdrBk_Entry *) NULL);
1499 defvalue_ae(a);
1501 p = str;
1503 REPLACE_NEWLINES_WITH_SPACE(p);
1505 nickname = p;
1506 SKIP_TO_TAB(p);
1507 if(!*p){
1508 RM_END_SPACE(nickname, p);
1509 a->nickname = cpystr(nickname);
1511 else{
1512 *p = '\0';
1513 RM_END_SPACE(nickname, p);
1514 a->nickname = cpystr(nickname);
1515 p++;
1516 SKIP_SPACE(p);
1517 fullname = p;
1518 SKIP_TO_TAB(p);
1519 if(!*p){
1520 RM_END_SPACE(fullname, p);
1521 a->fullname = cpystr(fullname);
1523 else{
1524 *p = '\0';
1525 RM_END_SPACE(fullname, p);
1526 a->fullname = cpystr(fullname);
1527 p++;
1528 SKIP_SPACE(p);
1529 addrfield = p;
1530 SKIP_TO_TAB(p);
1531 if(!*p){
1532 RM_END_SPACE(addrfield, p);
1534 else{
1535 *p = '\0';
1536 RM_END_SPACE(addrfield, p);
1537 p++;
1538 SKIP_SPACE(p);
1539 fcc = p;
1540 SKIP_TO_TAB(p);
1541 if(!*p){
1542 RM_END_SPACE(fcc, p);
1543 a->fcc = cpystr(fcc);
1545 else{
1546 char *src, *dst;
1548 *p = '\0';
1549 RM_END_SPACE(fcc, p);
1550 a->fcc = cpystr(fcc);
1551 p++;
1552 SKIP_SPACE(p);
1553 extra = p;
1554 p = extra + strlen(extra);
1555 RM_END_SPACE(extra, p);
1558 * When we wrap long comments we insert an extra colon
1559 * in the wrap so we can spot it and take it back out.
1560 * Pretty much a hack since we thought of it a long
1561 * time after designing it, but it eliminates the limit
1562 * on length of comments. Here we are looking for
1563 * <SP> <SP> : <SP> or
1564 * <SP> <SP> <SP> : <SP> and replacing it with <SP>.
1565 * There could have been a single \n or \r\n, so that
1566 * is why we check for 2 or 3 spaces before the colon.
1567 * (This was another mistake.)
1569 dst = src = extra;
1570 while(*src != '\0'){
1571 if(*src == SPACE && *(src+1) == SPACE &&
1572 *(src+2) == ':' && *(src+3) == SPACE){
1575 * If there was an extra space because of the
1576 * CRLF (instead of LF) then we already put
1577 * a SP in dst last time through the loop
1578 * and don't need to add another.
1580 if(src == extra || *(src-1) != SPACE)
1581 *dst++ = *src;
1583 src += 4;
1585 else
1586 *dst++ = *src++;
1589 *dst = '\0';
1590 a->extra = cpystr(extra);
1596 /* decode and convert to UTF-8 if we need to */
1598 convert_possibly_encoded_str_to_utf8(&a->nickname);
1599 convert_possibly_encoded_str_to_utf8(&a->fullname);
1600 convert_possibly_encoded_str_to_utf8(&a->fcc);
1601 convert_possibly_encoded_str_to_utf8(&a->extra);
1603 /* parse addrfield */
1604 if(addrfield){
1605 if(*addrfield == '('){ /* it's a list */
1606 a->tag = List;
1607 p = addrfield;
1608 addrfield_end = p + strlen(p);
1611 * Get rid of the parens.
1612 * If this isn't true the input file is messed up.
1614 if(p[strlen(p)-1] == ')'){
1615 char **ll;
1617 p[strlen(p)-1] = '\0';
1618 p++;
1619 a->addr.list = parse_addrlist(p);
1620 for(ll = a->addr.list; ll && *ll; ll++)
1621 convert_possibly_encoded_str_to_utf8(ll);
1623 else{
1624 /* put back what was there to start with */
1625 *addrfield_end = ')';
1626 a->addr.list = (char **)fs_get(sizeof(char *) * 2);
1627 a->addr.list[0] = cpystr(addrfield);
1628 a->addr.list[1] = NULL;
1629 dprint((2, "parsing error reading addressbook: missing right paren: %s\n",
1630 addrfield ? addrfield : "?"));
1633 else{ /* A plain, single address */
1635 a->tag = Single;
1636 a->addr.addr = cpystr(addrfield);
1637 convert_possibly_encoded_str_to_utf8(&a->addr.addr);
1640 else{
1642 * If no addrfield, assume an empty Single.
1644 a->addr.addr = cpystr("");
1645 a->tag = Single;
1648 return(a);
1653 * Return the size of the address book
1655 adrbk_cntr_t
1656 adrbk_count(AdrBk *ab)
1658 return(ab ? ab->count : (adrbk_cntr_t) 0);
1663 * Return a pointer to the ae that has index number "entry_num".
1665 AdrBk_Entry *
1666 adrbk_get_ae(AdrBk *ab, a_c_arg_t entry_num)
1668 if(!ab || entry_num >= (a_c_arg_t) ab->count)
1669 return((AdrBk_Entry *) NULL);
1671 return(&ab->arr[entry_num]);
1676 * Return a pointer to the deleted ae that has index number "entry_num".
1678 AdrBk_Entry *
1679 adrbk_get_delae(AdrBk *ab, a_c_arg_t entry_num)
1681 if(!ab || entry_num >= (a_c_arg_t) ab->del_count)
1682 return((AdrBk_Entry *) NULL);
1684 return(&ab->del[entry_num]);
1689 * Look up an entry in the address book given a nickname
1691 * Args: ab -- the address book
1692 * nickname -- nickname to match
1693 * entry_num -- if matched, return entry_num of match here
1695 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1697 * Lookups usually need to be recursive in case the address
1698 * book references itself. This is left to the next level up.
1699 * adrbk_clearrefs() is provided to clear all the reference tags in
1700 * the address book for loop detection.
1701 * When there are duplicates of the same nickname we return the first.
1702 * This can only happen if addrbook was edited externally.
1704 AdrBk_Entry *
1705 adrbk_lookup_by_nick(AdrBk *ab, char *nickname, adrbk_cntr_t *entry_num)
1707 adrbk_cntr_t num;
1708 AdrBk_Entry *ae;
1710 dprint((5, "- adrbk_lookup_by_nick(%s) (in %s) -\n",
1711 nickname ? nickname : "?",
1712 (ab && ab->filename) ? ab->filename : "?"));
1714 if(!ab || !nickname || !nickname[0])
1715 return NULL;
1718 num = lookup_nickname_in_trie(ab, nickname);
1720 if(num != NO_NEXT){
1721 ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1722 if(entry_num && ae)
1723 *entry_num = num;
1725 return(ae);
1727 else
1728 return((AdrBk_Entry *) NULL);
1733 * Look up an entry in the address book given an address
1735 * Args: ab -- the address book
1736 * address -- address to match
1737 * entry_num -- if matched, return entry_num of match here
1739 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1741 * Note: When there are multiple occurrences of an address in an addressbook,
1742 * which there will be if more than one nickname points to same address, then
1743 * we want this to match the first occurrence so that the fcc you get will
1744 * be predictable.
1746 AdrBk_Entry *
1747 adrbk_lookup_by_addr(AdrBk *ab, char *address, adrbk_cntr_t *entry_num)
1749 adrbk_cntr_t num;
1750 AdrBk_Entry *ae;
1752 dprint((5, "- adrbk_lookup_by_addr(%s) (in %s) -\n",
1753 address ? address : "?",
1754 (ab && ab->filename) ? ab->filename : "?"));
1756 if(!ab || !address || !address[0])
1757 return((AdrBk_Entry *)NULL);
1759 num = lookup_address_in_trie(ab, address);
1761 if(num != NO_NEXT){
1762 ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1763 if(entry_num && ae)
1764 *entry_num = num;
1766 return(ae);
1768 else
1769 return((AdrBk_Entry *)NULL);
1774 * Format a full name.
1776 * Args: fullname -- full name out of address book for formatting
1777 * first -- Return a pointer to first name here.
1778 * last -- Return a pointer to last name here.
1780 * Result: Returns pointer to name formatted for a mail header. Space is
1781 * allocated here and should be freed by caller.
1783 * We need this because we store full names as Last, First.
1784 * If the name has no comma, then no change is made.
1785 * Otherwise the text before the first comma is moved to the end and
1786 * the comma is deleted.
1788 * Last and first have to be freed by caller.
1790 char *
1791 adrbk_formatname(char *fullname, char **first, char **last)
1793 char *comma;
1794 char *new_name;
1796 if(first)
1797 *first = NULL;
1798 if(last)
1799 *last = NULL;
1802 * There is an assumption that the fullname is a UTF-8 string.
1805 if(fullname[0] != '"' && (comma = strindex(fullname, ',')) != NULL){
1806 size_t l;
1807 int last_name_len = comma - fullname;
1809 comma++;
1810 while(*comma && isspace((unsigned char)*comma))
1811 comma++;
1813 if(first)
1814 *first = cpystr(comma);
1816 if(last){
1817 *last = (char *)fs_get((last_name_len + 1) * sizeof(char));
1818 strncpy(*last, fullname, last_name_len);
1819 (*last)[last_name_len] = '\0';
1822 l = strlen(comma) + 1 + last_name_len;
1823 new_name = (char *) fs_get((l+1) * sizeof(char));
1824 strncpy(new_name, comma, l);
1825 new_name[l] = '\0';
1826 strncat(new_name, " ", l+1-1-strlen(new_name));
1827 new_name[l] = '\0';
1828 strncat(new_name, fullname, MIN(last_name_len,l+1-1-strlen(new_name)));
1829 new_name[l] = '\0';
1831 else
1832 new_name = cpystr(fullname);
1834 return(new_name);
1839 * Clear reference flags in preparation for a recursive lookup.
1841 * For loop detection during address book look up. This clears all the
1842 * referenced flags, then as the lookup proceeds the referenced flags can
1843 * be checked and set.
1845 void
1846 adrbk_clearrefs(AdrBk *ab)
1848 adrbk_cntr_t entry_num;
1849 AdrBk_Entry *ae;
1851 dprint((9, "- adrbk_clearrefs -\n"));
1853 if(!ab)
1854 return;
1856 for(entry_num = 0; entry_num < ab->count; entry_num++){
1857 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1858 ae->referenced = 0;
1864 * Allocate a new AdrBk_Entry
1866 AdrBk_Entry *
1867 adrbk_newentry(void)
1869 AdrBk_Entry *ae;
1871 ae = (AdrBk_Entry *) fs_get(sizeof(AdrBk_Entry));
1872 defvalue_ae(ae);
1874 return(ae);
1879 * Just sets ae values to default.
1880 * Parts should be freed before calling this or they will leak.
1882 void
1883 defvalue_ae(AdrBk_Entry *ae)
1885 ae->nickname = empty;
1886 ae->fullname = empty;
1887 ae->addr.addr = empty;
1888 ae->fcc = empty;
1889 ae->extra = empty;
1890 ae->tag = NotSet;
1891 ae->referenced = 0;
1895 AdrBk_Entry *
1896 copy_ae(AdrBk_Entry *src)
1898 AdrBk_Entry *a;
1900 a = adrbk_newentry();
1901 a->tag = src->tag;
1902 a->nickname = cpystr(src->nickname ? src->nickname : "");
1903 a->fullname = cpystr(src->fullname ? src->fullname : "");
1904 a->fcc = cpystr(src->fcc ? src->fcc : "");
1905 a->extra = cpystr(src->extra ? src->extra : "");
1906 if(a->tag == Single)
1907 a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
1908 else if(a->tag == List){
1909 char **p;
1910 int i, n;
1912 /* count list */
1913 for(p = src->addr.list; p && *p; p++)
1914 ;/* do nothing */
1916 if(p == NULL)
1917 n = 0;
1918 else
1919 n = p - src->addr.list;
1921 a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
1922 for(i = 0; i < n; i++)
1923 a->addr.list[i] = cpystr(src->addr.list[i]);
1925 a->addr.list[n] = NULL;
1928 return(a);
1933 * Add an entry to the address book, or modify an existing entry
1935 * Args: ab -- address book to add to
1936 * old_entry_num -- the entry we want to modify. If this is NO_NEXT, then
1937 * we look up the nickname passed in to see if that's the
1938 * entry to modify, else it is a new entry.
1939 * nickname -- the nickname for new entry
1940 * fullname -- the fullname for new entry
1941 * address -- the address for new entry
1942 * fcc -- the fcc for new entry
1943 * extra -- the extra field for new entry
1944 * tag -- the type of new entry
1945 * new_entry_num -- return entry_num of new or modified entry here
1946 * resort_happened -- means that more than just the current entry changed,
1947 * either something was added or order was changed
1948 * enable_intr -- tell adrbk_write to enable interrupt handling
1949 * be_quiet -- tell adrbk_write to not do percent done messages
1950 * write_it -- only do adrbk_write if this is set
1952 * Result: return code: 0 all went well
1953 * -2 error writing address book, check errno
1954 * -3 no modification, the tag given didn't match
1955 * existing tag
1956 * -4 tabs are in one of the fields passed in
1958 * If the nickname exists in the address book already, the operation is
1959 * considered a modification even if the case does not match exactly,
1960 * otherwise it is an add. The entry the operation occurs on is returned
1961 * in new. All fields are set to those passed in; that is, passing in NULL
1962 * even on a modification will set those fields to NULL as opposed to leaving
1963 * them unchanged. It is acceptable to pass in the current strings
1964 * in the entry in the case of modification. For address lists, the
1965 * structure passed in is what is used, so the storage has to all have
1966 * come from fs_get(). If the pointer passed in is the same as
1967 * the current field, no change is made.
1970 adrbk_add(AdrBk *ab, a_c_arg_t old_entry_num, char *nickname, char *fullname,
1971 char *address, char *fcc, char *extra, Tag tag, adrbk_cntr_t *new_entry_num,
1972 int *resort_happened, int enable_intr, int be_quiet, int write_it)
1974 AdrBk_Entry *a;
1975 AdrBk_Entry *ae;
1976 adrbk_cntr_t old_enum;
1977 adrbk_cntr_t new_enum;
1978 int (*cmp_func)();
1979 int retval = 0;
1980 int need_write = 0;
1981 int set_mangled = 0;
1983 dprint((3, "- adrbk_add(%s) -\n", nickname ? nickname : ""));
1985 if(!ab)
1986 return -2;
1988 /* ---- Make sure there are no tabs in the stuff to add ------*/
1989 if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
1990 (fullname != NULL && strindex(fullname, TAB) != NULL) ||
1991 (fcc != NULL && strindex(fcc, TAB) != NULL) ||
1992 (tag == Single && address != NULL && strindex(address, TAB) != NULL))
1993 return -4;
1996 * Are we adding or updating ?
1998 * If old_entry_num was passed in, we're updating that. If nickname
1999 * already exists, we're updating that entry. Otherwise, this is an add.
2001 if((adrbk_cntr_t)old_entry_num != NO_NEXT){
2002 ae = adrbk_get_ae(ab, old_entry_num);
2003 if(ae)
2004 old_enum = (adrbk_cntr_t)old_entry_num;
2006 else
2007 ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
2009 if(ae == NULL){ /*----- adding a new entry ----*/
2011 ae = adrbk_newentry();
2012 ae->tag = tag;
2013 if(nickname)
2014 ae->nickname = cpystr(nickname);
2015 if(fullname)
2016 ae->fullname = cpystr(fullname);
2017 if(fcc)
2018 ae->fcc = cpystr(fcc);
2019 if(extra)
2020 ae->extra = cpystr(extra);
2022 if(tag == Single)
2023 ae->addr.addr = cpystr(address);
2024 else
2025 ae->addr.list = (char **)NULL;
2027 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2028 cmp_ae_by_full_lists_last :
2029 (ab->sort_rule == AB_SORT_RULE_FULL) ?
2030 cmp_ae_by_full :
2031 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2032 cmp_ae_by_nick_lists_last :
2033 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2034 cmp_ae_by_nick;
2036 if(ab->sort_rule == AB_SORT_RULE_NONE) /* put it last */
2037 new_enum = ab->count;
2038 else /* Find slot for it */
2039 for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t) new_enum);
2040 a != (AdrBk_Entry *)NULL;
2041 a = adrbk_get_ae(ab, (a_c_arg_t) (++new_enum))){
2042 if((*cmp_func)((qsort_t *)&a, (qsort_t *)&ae) >= 0)
2043 break;
2046 /* Insert ae before entry new_enum. */
2047 insert_ab_entry(ab, (a_c_arg_t) new_enum, ae, 0);
2049 if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2050 exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
2052 exp_add_nth(ab->selects, (a_c_arg_t)new_enum);
2055 * insert_ab_entry copies the pointers of ae so the things
2056 * being pointed to (nickname, ...) are still in use.
2057 * Don't free them but free the ae struct itself.
2059 fs_give((void **) &ae);
2061 /*---- return in pointer if requested -----*/
2062 if(new_entry_num)
2063 *new_entry_num = new_enum;
2065 if(resort_happened)
2066 *resort_happened = 1;
2068 if(write_it)
2069 retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num, &set_mangled,
2070 enable_intr, be_quiet);
2072 else{
2073 /*----- Updating an existing entry ----*/
2075 int fix_tries = 0;
2077 if(ae->tag != tag)
2078 return -3;
2081 * Instead of just freeing and reallocating here we attempt to re-use
2082 * the space that was already allocated if possible.
2084 if(ae->nickname != nickname
2085 && ae->nickname != NULL
2086 && nickname != NULL
2087 && strcmp(nickname, ae->nickname) != 0){
2088 need_write++;
2089 fix_tries++;
2090 /* can use already alloc'd space */
2091 if(ae->nickname != NULL && nickname != NULL &&
2092 strlen(nickname) <= strlen(ae->nickname)){
2094 strncpy(ae->nickname, nickname, strlen(ae->nickname)+1);
2096 else{
2097 if(ae->nickname != NULL && ae->nickname != empty)
2098 fs_give((void **)&ae->nickname);
2100 ae->nickname = nickname ? cpystr(nickname) : nickname;
2104 if(ae->fullname != fullname
2105 && ae->fullname != NULL
2106 && fullname != NULL
2107 && strcmp(fullname, ae->fullname) != 0){
2108 need_write++;
2109 if(ae->fullname != NULL && fullname != NULL &&
2110 strlen(fullname) <= strlen(ae->fullname)){
2112 strncpy(ae->fullname, fullname, strlen(ae->fullname)+1);
2114 else{
2115 if(ae->fullname != NULL && ae->fullname != empty)
2116 fs_give((void **)&ae->fullname);
2118 ae->fullname = fullname ? cpystr(fullname) : fullname;
2122 if(ae->fcc != fcc
2123 && ae->fcc != NULL
2124 && fcc != NULL
2125 && strcmp(fcc, ae->fcc) != 0){
2126 need_write++;
2127 if(ae->fcc != NULL && fcc != NULL &&
2128 strlen(fcc) <= strlen(ae->fcc)){
2130 strncpy(ae->fcc, fcc, strlen(ae->fcc)+1);
2132 else{
2133 if(ae->fcc != NULL && ae->fcc != empty)
2134 fs_give((void **)&ae->fcc);
2136 ae->fcc = fcc ? cpystr(fcc) : fcc;
2140 if(ae->extra != extra
2141 && ae->extra != NULL
2142 && extra != NULL
2143 && strcmp(extra, ae->extra) != 0){
2144 need_write++;
2145 if(ae->extra != NULL && extra != NULL &&
2146 strlen(extra) <= strlen(ae->extra)){
2148 strncpy(ae->extra, extra, strlen(ae->extra)+1);
2150 else{
2151 if(ae->extra != NULL && ae->extra != empty)
2152 fs_give((void **)&ae->extra);
2154 ae->extra = extra ? cpystr(extra) : extra;
2158 if(tag == Single){
2159 /*---- Single ----*/
2160 if(ae->addr.addr != address
2161 && ae->addr.addr != NULL
2162 && address != NULL
2163 && strcmp(address, ae->addr.addr) != 0){
2164 need_write++;
2165 fix_tries++;
2166 if(ae->addr.addr != NULL && address != NULL &&
2167 strlen(address) <= strlen(ae->addr.addr)){
2169 strncpy(ae->addr.addr, address, strlen(ae->addr.addr)+1);
2171 else{
2172 if(ae->addr.addr != NULL && ae->addr.addr != empty)
2173 fs_give((void **)&ae->addr.addr);
2175 ae->addr.addr = address ? cpystr(address) : address;
2179 else{
2180 /*---- List -----*/
2182 * We don't mess with lists here.
2183 * The caller has to do it with adrbk_listadd().
2185 ;/* do nothing */
2189 /*---------- Make sure it's still in order ---------*/
2192 * old_enum is where ae is currently located
2193 * put it where it belongs
2195 if(need_write){
2196 new_enum = re_sort_particular_entry(ab, (a_c_arg_t) old_enum);
2197 if(old_enum != new_enum)
2198 fix_tries++;
2200 else
2201 new_enum = old_enum;
2203 /*---- return in pointer if requested -----*/
2204 if(new_entry_num)
2205 *new_entry_num = new_enum;
2207 if(resort_happened)
2208 *resort_happened = (old_enum != new_enum);
2210 if(fix_tries)
2211 repair_abook_tries(ab);
2213 if(write_it && need_write){
2214 int sort_happened = 0;
2216 retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num,
2217 &sort_happened, enable_intr, be_quiet);
2219 set_mangled = sort_happened;
2221 if(new_entry_num)
2222 new_enum = (*new_entry_num);
2224 if(resort_happened && (sort_happened || (old_enum != new_enum)))
2225 *resort_happened = 1;
2227 else
2228 retval = 0;
2231 if(set_mangled)
2232 ps_global->mangled_screen = 1;
2234 return(retval);
2239 * Similar to adrbk_add, but lower cost. No sorting is done, the new entry
2240 * goes on the end. This won't work if it is an edit instead of an append.
2241 * The address book is not committed to disk.
2243 * Args: ab -- address book to add to
2244 * nickname -- the nickname for new entry
2245 * fullname -- the fullname for new entry
2246 * address -- the address for new entry
2247 * fcc -- the fcc for new entry
2248 * extra -- the extra field for new entry
2249 * tag -- the type of new entry
2251 * Result: return code: 0 all went well
2252 * -2 error writing address book, check errno
2253 * -3 no modification, the tag given didn't match
2254 * existing tag
2255 * -4 tabs are in one of the fields passed in
2258 adrbk_append(AdrBk *ab, char *nickname, char *fullname, char *address, char *fcc,
2259 char *extra, Tag tag, adrbk_cntr_t *new_entry_num)
2261 AdrBk_Entry *ae;
2263 dprint((3, "- adrbk_append(%s) -\n", nickname ? nickname : ""));
2265 if(!ab)
2266 return -2;
2268 /* ---- Make sure there are no tabs in the stuff to add ------*/
2269 if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
2270 (fullname != NULL && strindex(fullname, TAB) != NULL) ||
2271 (fcc != NULL && strindex(fcc, TAB) != NULL) ||
2272 (tag == Single && address != NULL && strindex(address, TAB) != NULL))
2273 return -4;
2275 ae = adrbk_newentry();
2276 ae->tag = tag;
2277 if(nickname)
2278 ae->nickname = cpystr(nickname);
2279 if(fullname)
2280 ae->fullname = cpystr(fullname);
2281 if(fcc)
2282 ae->fcc = cpystr(fcc);
2283 if(extra)
2284 ae->extra = cpystr(extra);
2286 if(tag == Single)
2287 ae->addr.addr = cpystr(address);
2288 else
2289 ae->addr.list = (char **)NULL;
2291 if(new_entry_num)
2292 *new_entry_num = ab->count;
2294 insert_ab_entry(ab, (a_c_arg_t) ab->count, ae, 0);
2297 * insert_ab_entry copies the pointers of ae so the things
2298 * being pointed to (nickname, ...) are still in use.
2299 * Don't free them but free the ae struct itself.
2301 fs_give((void **) &ae);
2303 return(0);
2308 * The entire address book is assumed sorted correctly except perhaps for
2309 * entry number cur. Put it in the correct place. Return the new entry
2310 * number for cur.
2312 adrbk_cntr_t
2313 re_sort_particular_entry(AdrBk *ab, a_c_arg_t cur)
2315 AdrBk_Entry *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
2316 long small_enough;
2317 adrbk_cntr_t big_enough;
2318 adrbk_cntr_t new_entry_num;
2319 int (*cmp_func)(const qsort_t *, const qsort_t *);
2321 dprint((9, "- re_sort -\n"));
2323 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2324 cmp_ae_by_full_lists_last :
2325 (ab->sort_rule == AB_SORT_RULE_FULL) ?
2326 cmp_ae_by_full :
2327 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2328 cmp_ae_by_nick_lists_last :
2329 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2330 cmp_ae_by_nick;
2332 new_entry_num = (adrbk_cntr_t) cur;
2334 if(ab->sort_rule == AB_SORT_RULE_NONE)
2335 return(new_entry_num);
2337 ae_cur = adrbk_get_ae(ab, cur);
2339 if(cur > 0)
2340 ae_prev = adrbk_get_ae(ab, cur - 1);
2342 if(cur < ab->count -1)
2343 ae_next = adrbk_get_ae(ab, cur + 1);
2346 * A possible optimization here would be to implement some sort of
2347 * binary search to find where it goes instead of stepping through the
2348 * entries one at a time.
2350 if(cur > 0 &&
2351 (*cmp_func)((qsort_t *)&ae_cur,(qsort_t *)&ae_prev) < 0){
2352 /*--- Out of order, needs to be moved up ----*/
2353 for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
2354 ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t) small_enough);
2355 if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_small_enough) >= 0)
2356 break;
2358 new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
2359 move_ab_entry(ab, cur, (a_c_arg_t) new_entry_num);
2361 else if(cur < ab->count - 1 &&
2362 (*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_next) > 0){
2363 /*---- Out of order needs, to be moved towards end of list ----*/
2364 for(big_enough = (adrbk_cntr_t)(cur + 2);
2365 big_enough < ab->count;
2366 big_enough++){
2367 ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t) big_enough);
2368 if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_big_enough) <= 0)
2369 break;
2371 new_entry_num = big_enough - 1;
2372 move_ab_entry(ab, cur, (a_c_arg_t) big_enough);
2375 dprint((9, "- re_sort done -\n"));
2377 return(new_entry_num);
2382 * Delete an entry from the address book
2384 * Args: ab -- the address book
2385 * entry_num -- entry to delete
2386 * save_deleted -- save deleted as a #DELETED- entry
2387 * enable_intr -- tell adrbk_write to enable interrupt handling
2388 * be_quiet -- tell adrbk_write to not do percent done messages
2389 * write_it -- only do adrbk_write if this is set
2391 * Result: returns: 0 if all went well
2392 * -1 if there is no such entry
2393 * -2 error writing address book, check errno
2396 adrbk_delete(AdrBk *ab, a_c_arg_t entry_num, int save_deleted, int enable_intr,
2397 int be_quiet, int write_it)
2399 int retval = 0;
2400 int set_mangled = 0;
2402 dprint((3, "- adrbk_delete(%ld) -\n", (long)entry_num));
2404 if(!ab)
2405 return -2;
2407 delete_ab_entry(ab, entry_num, save_deleted);
2408 if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2409 exp_del_nth(ab->exp, entry_num);
2411 exp_del_nth(ab->selects, entry_num);
2413 if(write_it)
2414 retval = adrbk_write(ab, 0, NULL, &set_mangled, enable_intr, be_quiet);
2416 if(set_mangled)
2417 ps_global->mangled_screen = 1;
2419 return(retval);
2424 * Delete an address out of an address list
2426 * Args: ab -- the address book
2427 * entry_num -- the address list we are deleting from
2428 * addr -- address in above list to be deleted
2430 * Result: 0: Deletion complete, address book written
2431 * -1: Address for deletion not found
2432 * -2: Error writing address book. Check errno.
2434 * The address to be deleted is located by matching the string.
2437 adrbk_listdel(AdrBk *ab, a_c_arg_t entry_num, char *addr)
2439 char **p, *to_free;
2440 AdrBk_Entry *ae;
2441 int ret;
2442 int set_mangled = 0;
2444 dprint((3, "- adrbk_listdel(%ld) -\n", (long) entry_num));
2446 if(!ab || entry_num >= ab->count)
2447 return -2;
2449 if(!addr)
2450 return -1;
2452 ae = adrbk_get_ae(ab, entry_num);
2454 if(ae->tag != List)
2455 return -1;
2457 for(p = ae->addr.list; *p; p++)
2458 if(strcmp(*p, addr) == 0)
2459 break;
2461 if(*p == NULL)
2462 return -1;
2464 /* note storage to be freed */
2465 if(*p != empty)
2466 to_free = *p;
2467 else
2468 to_free = NULL;
2470 /* slide all the entries below up (including NULL) */
2471 for(; *p; p++)
2472 *p = *(p+1);
2474 if(to_free)
2475 fs_give((void **) &to_free);
2477 ret = adrbk_write(ab, 0, NULL, &set_mangled, 1, 0);
2479 if(set_mangled)
2480 ps_global->mangled_screen = 1;
2482 return(ret);
2487 * Delete all addresses out of an address list
2489 * Args: ab -- the address book
2490 * entry_num -- the address list we are deleting from
2492 * Result: 0: Deletion complete, address book written
2493 * -1: Address for deletion not found
2494 * -2: Error writing address book. Check errno.
2497 adrbk_listdel_all(AdrBk *ab, a_c_arg_t entry_num)
2499 char **p;
2500 AdrBk_Entry *ae;
2502 dprint((3, "- adrbk_listdel_all(%ld) -\n", (long) entry_num));
2504 if(!ab || entry_num >= ab->count)
2505 return -2;
2507 ae = adrbk_get_ae(ab, entry_num);
2509 if(ae->tag != List)
2510 return -1;
2512 /* free old list */
2513 for(p = ae->addr.list; p && *p; p++)
2514 if(*p != empty)
2515 fs_give((void **)p);
2517 if(ae->addr.list)
2518 fs_give((void **) &ae->addr.list);
2520 ae->addr.list = NULL;
2522 return 0;
2527 * Add a list of addresses to an already existing address list
2529 * Args: ab -- the address book
2530 * entry_num -- the address list we are adding to
2531 * addrs -- address list to be added
2532 * enable_intr -- tell adrbk_write to enable interrupt handling
2533 * be_quiet -- tell adrbk_write to not do percent done messages
2534 * write_it -- only do adrbk_write if this is set
2536 * Result: returns 0 : addition made, address book written
2537 * -1 : addition to non-list attempted
2538 * -2 : error writing address book -- check errno
2541 adrbk_nlistadd(AdrBk *ab, a_c_arg_t entry_num, adrbk_cntr_t *new_entry_num,
2542 int *resort_happened, char **addrs,
2543 int enable_intr, int be_quiet, int write_it)
2545 char **p;
2546 int cur_size, size_of_additional_list, new_size;
2547 int i, rc = 0;
2548 int set_mangled = 0;
2549 AdrBk_Entry *ae;
2551 dprint((3, "- adrbk_nlistadd(%ld) -\n", (long) entry_num));
2553 if(!ab || entry_num >= ab->count)
2554 return -2;
2556 ae = adrbk_get_ae(ab, entry_num);
2558 if(ae->tag != List)
2559 return -1;
2561 /* count up size of existing list */
2562 for(p = ae->addr.list; p != NULL && *p != NULL; p++)
2563 ;/* do nothing */
2565 cur_size = p - ae->addr.list;
2567 /* count up size of new list */
2568 for(p = addrs; p != NULL && *p != NULL; p++)
2569 ;/* do nothing */
2571 size_of_additional_list = p - addrs;
2572 new_size = cur_size + size_of_additional_list;
2574 /* make room at end of list for it */
2575 if(cur_size == 0)
2576 ae->addr.list = (char **) fs_get(sizeof(char *) * (new_size + 1));
2577 else
2578 fs_resize((void **) &ae->addr.list, sizeof(char *) * (new_size + 1));
2580 /* Put new list at the end */
2581 for(i = cur_size; i < new_size; i++)
2582 (ae->addr.list)[i] = cpystr(addrs[i - cur_size]);
2584 (ae->addr.list)[new_size] = NULL;
2586 /*---- sort it into the correct place ------*/
2587 if(ab->sort_rule != AB_SORT_RULE_NONE)
2588 sort_addr_list(ae->addr.list);
2590 if(write_it)
2591 rc = adrbk_write(ab, entry_num, new_entry_num, &set_mangled, enable_intr, be_quiet);
2593 if(set_mangled){
2594 ps_global->mangled_screen = 1;
2595 if(resort_happened)
2596 *resort_happened = 1;
2599 return(rc);
2604 * Set the valid variable if we determine that the address book has
2605 * been changed by something other than us. This means we should update
2606 * our view of the address book when next possible.
2608 * Args ab -- AdrBk handle
2609 * do_it_now -- If > 0, check now regardless
2610 * If = 0, check if time since last chk more than default
2611 * If < 0, check if time since last chk more than -do_it_now
2613 void
2614 adrbk_check_validity(AdrBk *ab, long int do_it_now)
2616 dprint((9, "- adrbk_check_validity(%s) -\n",
2617 (ab && ab->filename) ? ab->filename : ""));
2619 if(!ab || ab->flags & FILE_OUTOFDATE)
2620 return;
2622 adrbk_check_local_validity(ab, do_it_now);
2624 if(ab->type == Imap && !(ab->flags & FILE_OUTOFDATE ||
2625 ab->rd->flags & REM_OUTOFDATE))
2626 rd_check_remvalid(ab->rd, do_it_now);
2631 * Set the valid variable if we determine that the address book has
2632 * been changed by something other than us. This means we should update
2633 * our view of the address book when next possible.
2635 * Args ab -- AdrBk handle
2636 * do_it_now -- If > 0, check now regardless
2637 * If = 0, check if time since last chk more than default
2638 * If < 0, check if time since last chk more than -do_it_now
2640 void
2641 adrbk_check_local_validity(AdrBk *ab, long int do_it_now)
2643 time_t mtime, chk_interval;
2645 dprint((9, "- adrbk_check_local_validity(%s) -\n",
2646 (ab && ab->filename) ? ab->filename : ""));
2648 if(!ab)
2649 return;
2651 if(do_it_now < 0L){
2652 chk_interval = -1L * do_it_now;
2653 do_it_now = 0L;
2655 else
2656 chk_interval = FILE_VALID_CHK_INTERVAL;
2658 if(!do_it_now &&
2659 get_adj_time() <= ab->last_local_valid_chk + chk_interval)
2660 return;
2662 ab->last_local_valid_chk = get_adj_time();
2665 * Check local file for a modification time change.
2666 * If this is out of date, don't even bother checking for the remote
2667 * folder being out of date. That will get fixed when we reopen.
2669 if(!(ab->flags & FILE_OUTOFDATE) &&
2670 ab->last_change_we_know_about != (time_t)(-1) &&
2671 (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
2672 ab->last_change_we_know_about != mtime){
2674 dprint((2, "adrbk_check_local_validity: addrbook %s has changed\n",
2675 ab->filename ? ab->filename : "?"));
2676 ab->flags |= FILE_OUTOFDATE;
2682 * See if we can re-use an existing stream.
2684 * [ We don't believe we need this anymore now that the stuff in pine.c ]
2685 * [ is recycling streams for us, but we haven't thought it through all ]
2686 * [ the way enough to get rid of this. Hubert 2003-07-09 ]
2688 * Args name -- Name of folder we want a stream for.
2690 * Returns -- A mail stream suitable for status cmd or append cmd.
2691 * NULL if none were available.
2693 MAILSTREAM *
2694 adrbk_handy_stream(char *name)
2696 MAILSTREAM *stat_stream = NULL;
2697 int i;
2699 dprint((9, "- adrbk_handy_stream(%s) -\n", name ? name : "?"));
2701 stat_stream = sp_stream_get(name, SP_SAME);
2704 * Look through our imap streams to see if there is one we can use.
2706 for(i = 0; !stat_stream && i < as.n_addrbk; i++){
2707 PerAddrBook *pab;
2709 pab = &as.adrbks[i];
2711 if(pab->address_book &&
2712 pab->address_book->type == Imap &&
2713 pab->address_book->rd &&
2714 pab->address_book->rd->type == RemImap &&
2715 same_stream(name, pab->address_book->rd->t.i.stream)){
2716 stat_stream = pab->address_book->rd->t.i.stream;
2717 pab->address_book->rd->last_use = get_adj_time();
2718 dprint((7,
2719 "%s: used other abook stream for status (%ld)\n",
2720 pab->address_book->orig_filename
2721 ? pab->address_book->orig_filename : "?",
2722 (long)pab->address_book->rd->last_use));
2726 dprint((9, "adrbk_handy_stream: returning %s\n",
2727 stat_stream ? "good stream" : "NULL"));
2729 return(stat_stream);
2734 * Close address book
2736 * All that is done here is to free the storage, since the address book is
2737 * rewritten on every change.
2739 void
2740 adrbk_close(AdrBk *ab)
2742 int we_cancel = 0;
2744 dprint((4, "- adrbk_close(%s) -\n",
2745 (ab && ab->filename) ? ab->filename : ""));
2747 if(!ab)
2748 return;
2750 if(ab->rd)
2751 rd_close_remdata(&ab->rd);
2753 if(ab->fp)
2754 (void)fclose(ab->fp);
2756 if(ab->our_filecopy && ab->filename != ab->our_filecopy){
2757 our_unlink(ab->our_filecopy);
2758 fs_give((void**) &ab->our_filecopy);
2761 if(ab->exp){
2762 exp_free(ab->exp);
2763 fs_give((void **)&ab->exp); /* free head of list, too */
2766 if(ab->checks){
2767 exp_free(ab->checks);
2768 fs_give((void **)&ab->checks); /* free head of list, too */
2771 if(ab->selects){
2772 exp_free(ab->selects);
2773 fs_give((void **)&ab->selects); /* free head of list, too */
2776 if(ab->arr){
2777 adrbk_cntr_t entry_num;
2780 * ab->arr is an allocated array. Each element of the array contains
2781 * several pointers that point to other allocated stuff, so we
2782 * free_ae_parts to get those and then free the array after the loop.
2784 for(entry_num = 0; entry_num < ab->count; entry_num++)
2785 free_ae_parts(&ab->arr[entry_num]);
2787 fs_give((void **) &ab->arr);
2790 if(ab->del){
2791 adrbk_cntr_t entry_num;
2793 for(entry_num = 0; entry_num < ab->del_count; entry_num++)
2794 free_ae_parts(&ab->del[entry_num]);
2796 fs_give((void **) &ab->del);
2799 if(ab->nick_trie)
2800 free_abook_trie(&ab->nick_trie);
2802 if(ab->addr_trie)
2803 free_abook_trie(&ab->addr_trie);
2805 if(ab->full_trie)
2806 free_abook_trie(&ab->full_trie);
2808 if(ab->revfull_trie)
2809 free_abook_trie(&ab->revfull_trie);
2811 if(we_cancel)
2812 cancel_busy_cue(0);
2814 if(ab->filename){
2815 if(ab->flags & DEL_FILE)
2816 our_unlink(ab->filename);
2818 fs_give((void**)&ab->filename);
2821 if(ab->orig_filename)
2822 fs_give((void**)&ab->orig_filename);
2824 fs_give((void **) &ab);
2828 void
2829 adrbk_partial_close(AdrBk *ab)
2831 dprint((4, "- adrbk_partial_close(%s) -\n",
2832 (ab && ab->filename) ? ab->filename : ""));
2834 exp_free(ab->exp); /* leaves head of list */
2835 exp_free(ab->checks); /* leaves head of list */
2840 * It has been noticed that this stream is dead and it is about to
2841 * be removed from the stream pool. We may have a pointer to it for one
2842 * of the remote address books. We have to note that the pointer is stale.
2844 void
2845 note_closed_adrbk_stream(MAILSTREAM *stream)
2847 PerAddrBook *pab;
2848 int i;
2850 if(!stream)
2851 return;
2853 for(i = 0; i < as.n_addrbk; i++){
2854 pab = &as.adrbks[i];
2855 if(pab->address_book
2856 && pab->address_book->type == Imap
2857 && pab->address_book->rd
2858 && pab->address_book->rd->type == RemImap
2859 && (stream == pab->address_book->rd->t.i.stream)){
2860 dprint((4, "- note_closed_adrbk_stream(%s) -\n",
2861 (pab->address_book && pab->address_book->orig_filename)
2862 ? pab->address_book->orig_filename : ""));
2863 pab->address_book->rd->t.i.stream = NULL;
2869 static adrbk_cntr_t tot_for_percent;
2870 static adrbk_cntr_t entry_num_for_percent;
2873 * Write out the address book.
2875 * If be_quiet is set, don't turn on busy_cue.
2877 * If enable_intr_handling is set, turn on and off interrupt handling.
2879 * Format is as in comment in the adrbk_open routine. Lines are wrapped
2880 * to be under 80 characters. This is called on every change to the
2881 * address book. Write is first to a temporary file,
2882 * which is then renamed to be the real address book so that we won't
2883 * destroy the real address book in case of something like a full file
2884 * system.
2886 * Writing a temp file and then renaming has the bad side affect of
2887 * destroying links. It also overrides any read only permissions on
2888 * the mail file since rename ignores such permissions. However, we
2889 * handle readonly-ness in addrbook.c before we call this.
2890 * We retain the permissions by doing a stat on the old file and a
2891 * chmod on the new one (with file_attrib_copy).
2893 * In pre-alpine pine the address book entries were encoded on disk.
2894 * We would be happy with raw UTF-8 now but in order to preserve some
2895 * backwards compatibility we encode the entries before writing.
2896 * We first try to translate to the user's character set and encode
2897 * in that, else we use UTF-8 but with 1522 encoding.
2899 * Returns: 0 write was successful
2900 * -2 write failed
2901 * -5 interrupted
2904 adrbk_write(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num,
2905 int *sort_happened, int enable_intr_handling, int be_quiet)
2907 FILE *ab_stream = NULL;
2908 AdrBk_Entry *ae = NULL;
2909 adrbk_cntr_t entry_num;
2910 #ifndef DOS
2911 void (*save_sighup)();
2912 #endif
2913 int max_nick = 0,
2914 max_full = 0, full_two = 0, full_three = 0,
2915 max_fcc = 0, fcc_two = 0, fcc_three = 0,
2916 max_addr = 0, addr_two = 0, addr_three = 0,
2917 this_nick_width, this_full_width, this_addr_width,
2918 this_fcc_width, fd, i;
2919 int interrupt_happened = 0, we_cancel = 0, we_turned_on = 0;
2920 char *temp_filename = NULL;
2921 WIDTH_INFO_S *widths;
2923 if(!ab)
2924 return -2;
2926 dprint((2, "- adrbk_write(\"%s\") - writing %lu entries\n",
2927 ab->filename ? ab->filename : "", (unsigned long) ab->count));
2929 errno = 0;
2931 adrbk_check_local_validity(ab, 1L);
2932 /* verify that file has not been changed by something else */
2933 if(ab->flags & FILE_OUTOFDATE){
2934 /* It has changed! */
2935 q_status_message(SM_ORDER | SM_DING, 5, 15,
2936 /* TRANSLATORS: The address book was changed by something else so alpine
2937 is not making the change the user wanted to make to avoid damaging
2938 the address book. */
2939 _("Addrbook changed by another process, aborting our change to avoid damage..."));
2940 dprint((1, "adrbk_write: addrbook %s changed while we had it open, aborting write\n",
2941 ab->filename ? ab->filename : "?"));
2942 longjmp(addrbook_changed_unexpectedly, 1);
2943 /*NOTREACHED*/
2947 * Verify that remote folder has not been
2948 * changed by something else (not if checked in last 5 seconds).
2950 * The -5 is so we won't check every time on a series of quick writes.
2952 rd_check_remvalid(ab->rd, -5L);
2953 if(ab->type == Imap){
2954 int ro;
2957 * We'll eventually need this open to write to remote, so see if
2958 * we can open it now.
2960 rd_open_remote(ab->rd);
2963 * Did someone else change the remote copy?
2965 if((ro=rd_remote_is_readonly(ab->rd)) || ab->rd->flags & REM_OUTOFDATE){
2966 if(ro == 1){
2967 q_status_message(SM_ORDER | SM_DING, 5, 15,
2968 _("Can't access remote addrbook, aborting change..."));
2969 dprint((1,
2970 "adrbk_write: Can't write to remote addrbook %s, aborting write: open failed\n",
2971 ab->rd->rn ? ab->rd->rn : "?"));
2973 else if(ro == 2){
2974 if(!(ab->rd->flags & NO_META_UPDATE)){
2975 unsigned long save_chk_nmsgs;
2978 * Should have some non-type-specific method of doing this.
2980 switch(ab->rd->type){
2981 case RemImap:
2982 save_chk_nmsgs = ab->rd->t.i.chk_nmsgs;
2983 ab->rd->t.i.chk_nmsgs = 0;/* cause it to be OUTOFDATE */
2984 rd_write_metadata(ab->rd, 0);
2985 ab->rd->t.i.chk_nmsgs = save_chk_nmsgs;
2986 break;
2988 default:
2989 q_status_message(SM_ORDER | SM_DING, 3, 5,
2990 "Adrbk_write: Type not supported");
2991 break;
2995 q_status_message(SM_ORDER | SM_DING, 5, 15,
2996 _("No write permission for remote addrbook, aborting change..."));
2998 else{
2999 q_status_message(SM_ORDER | SM_DING, 5, 15,
3000 _("Remote addrbook changed, aborting our change to avoid damage..."));
3001 dprint((1,
3002 "adrbk_write: remote addrbook %s changed while we had it open, aborting write\n",
3003 ab->orig_filename ? ab->orig_filename : "?"));
3006 rd_close_remote(ab->rd);
3008 longjmp(addrbook_changed_unexpectedly, 1);
3009 /*NOTREACHED*/
3013 #ifndef DOS
3014 save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
3015 #endif
3018 * If we want to be able to modify the address book, we will
3019 * need a temp_filename in the same directory as our_filecopy.
3021 if(!(ab->our_filecopy && ab->our_filecopy[0])
3022 || !(temp_filename = tempfile_in_same_dir(ab->our_filecopy,"a1",NULL))
3023 || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3024 dprint((1, "adrbk_write(%s): failed opening temp file (%s)\n",
3025 ab->filename ? ab->filename : "?",
3026 temp_filename ? temp_filename : "NULL"));
3027 goto io_error;
3030 ab_stream = fdopen(fd, "wb");
3031 if(ab_stream == NULL){
3032 dprint((1, "adrbk_write(%s): fdopen failed\n", temp_filename ? temp_filename : "?"));
3033 goto io_error;
3036 if(adrbk_is_in_sort_order(ab, be_quiet)){
3037 if(sort_happened)
3038 *sort_happened = 0;
3040 if(new_entry_num)
3041 *new_entry_num = current_entry_num;
3043 else{
3044 if(sort_happened)
3045 *sort_happened = 1;
3047 (void) adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet);
3050 /* accept keyboard interrupts */
3051 if(enable_intr_handling)
3052 we_turned_on = intr_handling_on();
3054 if(!be_quiet){
3055 tot_for_percent = MAX(ab->count, 1);
3056 entry_num_for_percent = 0;
3057 we_cancel = busy_cue(_("Saving address book"), percent_abook_saved, 0);
3060 writing = 1;
3063 * If there are any old deleted entries, copy them to new file.
3065 if(ab->del_count > 0){
3066 char *nickname;
3067 long delindex;
3070 * If there are deleted entries old enough that we no longer want
3071 * to save them, remove them from the list.
3073 for(delindex = (long) ab->del_count-1;
3074 delindex >= 0L && ab->del_count > 0; delindex--){
3076 ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3077 nickname = ae->nickname;
3079 if(strncmp(nickname, DELETED, DELETED_LEN) == 0
3080 && isdigit((unsigned char)nickname[DELETED_LEN])
3081 && isdigit((unsigned char)nickname[DELETED_LEN+1])
3082 && nickname[DELETED_LEN+2] == '/'
3083 && isdigit((unsigned char)nickname[DELETED_LEN+3])
3084 && isdigit((unsigned char)nickname[DELETED_LEN+4])
3085 && nickname[DELETED_LEN+5] == '/'
3086 && isdigit((unsigned char)nickname[DELETED_LEN+6])
3087 && isdigit((unsigned char)nickname[DELETED_LEN+7])
3088 && nickname[DELETED_LEN+8] == '#'){
3089 int year, month, day;
3090 struct tm *tm_before;
3091 time_t now, before;
3093 now = time((time_t *)0);
3094 before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
3095 tm_before = localtime(&before);
3096 tm_before->tm_mon++;
3099 * Check to see if it is older than 100 days.
3101 year = atoi(&nickname[DELETED_LEN]);
3102 month = atoi(&nickname[DELETED_LEN+3]);
3103 day = atoi(&nickname[DELETED_LEN+6]);
3104 if(year < 95)
3105 year += 100; /* this breaks in year 2095 */
3108 * remove it if it is more than 100 days old
3110 if(!(year > tm_before->tm_year
3111 || (year == tm_before->tm_year
3112 && month > tm_before->tm_mon)
3113 || (year == tm_before->tm_year
3114 && month == tm_before->tm_mon
3115 && day >= tm_before->tm_mday))){
3117 /* it's old, dump it */
3118 free_ae_parts(ae);
3119 ab->del_count--;
3120 /* patch the array by moving everything below up */
3121 if(delindex < ab->del_count){
3122 memmove(&ab->del[delindex],
3123 &ab->del[delindex+1],
3124 (ab->del_count - delindex) *
3125 sizeof(AdrBk_Entry));
3131 /* write out what remains */
3132 if(ab->del_count){
3133 for(delindex = 0L; delindex < ab->del_count; delindex++){
3134 ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3135 if(write_single_abook_entry(ae, ab_stream, NULL, NULL,
3136 NULL, NULL) == EOF){
3137 dprint((1, "adrbk_write(%s): failed writing deleted entry\n",
3138 temp_filename ? temp_filename : "?"));
3139 goto io_error;
3143 else{
3144 /* nothing left, get rid of del array */
3145 fs_give((void **) &ab->del);
3149 if(ab->del_count)
3150 dprint((4, " adrbk_write: saving %ld deleted entries\n",
3151 (long) ab->del_count));
3153 for(entry_num = 0; entry_num < ab->count; entry_num++){
3154 entry_num_for_percent++;
3156 ALARM_BLIP();
3158 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
3160 if(ae == (AdrBk_Entry *) NULL){
3161 dprint((1, "adrbk_write(%s): can't find ae while writing addrbook, entry_num = %ld\n",
3162 ab->filename ? ab->filename : "?",
3163 (long) entry_num));
3164 goto io_error;
3167 /* write to temp file */
3168 if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
3169 &this_full_width, &this_addr_width, &this_fcc_width) == EOF){
3170 dprint((1, "adrbk_write(%s): failed writing for entry %ld\n",
3171 temp_filename ? temp_filename : "?",
3172 (long) entry_num));
3173 goto io_error;
3176 /* keep track of widths */
3177 max_nick = MAX(max_nick, this_nick_width);
3178 if(this_full_width > max_full){
3179 full_three = full_two;
3180 full_two = max_full;
3181 max_full = this_full_width;
3183 else if(this_full_width > full_two){
3184 full_three = full_two;
3185 full_two = this_full_width;
3187 else if(this_full_width > full_three){
3188 full_three = this_full_width;
3191 if(this_addr_width > max_addr){
3192 addr_three = addr_two;
3193 addr_two = max_addr;
3194 max_addr = this_addr_width;
3196 else if(this_addr_width > addr_two){
3197 addr_three = addr_two;
3198 addr_two = this_addr_width;
3200 else if(this_addr_width > addr_three){
3201 addr_three = this_addr_width;
3204 if(this_fcc_width > max_fcc){
3205 fcc_three = fcc_two;
3206 fcc_two = max_fcc;
3207 max_fcc = this_fcc_width;
3209 else if(this_fcc_width > fcc_two){
3210 fcc_three = fcc_two;
3211 fcc_two = this_fcc_width;
3213 else if(this_fcc_width > fcc_three){
3214 fcc_three = this_fcc_width;
3218 * Check to see if we've been interrupted. We check at the bottom
3219 * of the loop so that we can handle an interrupt in the last
3220 * iteration.
3222 if(enable_intr_handling && ps_global->intr_pending){
3223 interrupt_happened++;
3224 goto io_error;
3228 if(we_cancel){
3229 cancel_busy_cue(-1);
3230 we_cancel = 0;
3233 if(enable_intr_handling && we_turned_on){
3234 intr_handling_off();
3235 we_turned_on = 0;
3238 if(fclose(ab_stream) == EOF){
3239 dprint((1, "adrbk_write: fclose for %s failed\n",
3240 temp_filename ? temp_filename : "?"));
3241 goto io_error;
3244 ab_stream = (FILE *) NULL;
3246 file_attrib_copy(temp_filename, ab->our_filecopy);
3247 if(ab->fp){
3248 (void) fclose(ab->fp);
3249 ab->fp = NULL; /* in case of problems */
3252 if((i=rename_file(temp_filename, ab->our_filecopy)) < 0){
3253 dprint((1, "adrbk_write: rename(%s, %s) failed: %s\n",
3254 temp_filename ? temp_filename : "?",
3255 ab->our_filecopy ? ab->our_filecopy : "?",
3256 error_description(errno)));
3257 #ifdef _WINDOWS
3258 if(i == -5){
3259 q_status_message2(SM_ORDER | SM_DING, 5, 7,
3260 _("Can't replace address book %.200sfile \"%.200s\""),
3261 (ab->type == Imap) ? "cache " : "",
3262 ab->filename);
3263 q_status_message(SM_ORDER | SM_DING, 5, 7,
3264 _("If another Alpine is running, quit that Alpine before updating address book."));
3266 #endif /* _WINDOWS */
3267 goto io_error;
3270 /* reopen fp to new file */
3271 if(!(ab->fp = our_fopen(ab->our_filecopy, "rb"))){
3272 dprint((1, "adrbk_write: can't reopen %s\n",
3273 ab->our_filecopy ? ab->our_filecopy : "?"));
3274 goto io_error;
3277 if(temp_filename){
3278 our_unlink(temp_filename);
3279 fs_give((void **) &temp_filename);
3283 * Now copy our_filecopy back to filename.
3285 if(ab->filename != ab->our_filecopy){
3286 int c, err = 0;
3287 char *lc;
3289 if(!(ab->filename && ab->filename[0])
3290 || !(temp_filename = tempfile_in_same_dir(ab->filename,"a1",NULL))
3291 || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3292 dprint((1,
3293 "adrbk_write(%s): failed opening temp file (%s)\n",
3294 ab->filename ? ab->filename : "?",
3295 temp_filename ? temp_filename : "NULL"));
3296 err++;
3299 if(!err)
3300 ab_stream = fdopen(fd, "wb");
3302 if(ab_stream == NULL){
3303 dprint((1, "adrbk_write(%s): fdopen failed\n",
3304 temp_filename ? temp_filename : "?"));
3305 err++;
3308 if(!err){
3309 rewind(ab->fp);
3310 while((c = getc(ab->fp)) != EOF)
3311 if(putc(c, ab_stream) == EOF){
3312 err++;
3313 break;
3317 if(!err && fclose(ab_stream) == EOF)
3318 err++;
3320 if(!err){
3321 #ifdef _WINDOWS
3322 int tries = 3;
3323 #endif
3325 file_attrib_copy(temp_filename, ab->filename);
3328 * This could fail if somebody else has it open, but they should
3329 * only have it open for a short time.
3331 while(!err && rename_file(temp_filename, ab->filename) < 0){
3332 #ifdef _WINDOWS
3333 if(i == -5){
3334 if(--tries <= 0)
3335 err++;
3337 if(!err){
3338 q_status_message2(SM_ORDER, 0, 3,
3339 _("Replace of \"%.200s\" failed, trying %.200s"),
3340 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename,
3341 (tries > 1) ? "again" : "one more time");
3342 display_message('x');
3343 sleep(3);
3346 #else /* UNIX */
3347 err++;
3348 #endif /* UNIX */
3352 if(err){
3353 q_status_message1(SM_ORDER | SM_DING, 5, 5,
3354 _("Copy of addrbook to \"%.200s\" failed, changes NOT saved!"),
3355 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
3356 dprint((2, "adrbk_write: failed copying our_filecopy (%s)back to filename (%s): %s, continuing without a net\n",
3357 ab->our_filecopy ? ab->our_filecopy : "?",
3358 ab->filename ? ab->filename : "?",
3359 error_description(errno)));
3363 widths = &ab->widths;
3364 widths->max_nickname_width = MIN(max_nick, 99);
3365 widths->max_fullname_width = MIN(max_full, 99);
3366 widths->max_addrfield_width = MIN(max_addr, 99);
3367 widths->max_fccfield_width = MIN(max_fcc, 99);
3368 widths->third_biggest_fullname_width = MIN(full_three, 99);
3369 widths->third_biggest_addrfield_width = MIN(addr_three, 99);
3370 widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
3372 /* record new change date of addrbook file */
3373 ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
3375 #ifndef DOS
3376 (void)signal(SIGHUP, save_sighup);
3377 #endif
3380 * If this is a remote addressbook, copy the file over.
3381 * If it fails we warn but continue to operate on the changed,
3382 * locally cached addressbook file.
3384 if(ab->type == Imap){
3385 int e;
3386 char datebuf[200];
3388 datebuf[0] = '\0';
3389 if(we_cancel)
3390 cancel_busy_cue(-1);
3392 we_cancel = busy_cue(_("Copying to remote addressbook"), NULL, 1);
3394 * We don't want a cookie upgrade to blast our data in rd->lf by
3395 * copying back the remote data before doing the upgrade, and then
3396 * proceeding to finish with that data. So we tell rd_upgrade_cookie
3397 * about that here.
3399 ab->rd->flags |= BELIEVE_CACHE;
3400 if((e = rd_update_remote(ab->rd, datebuf)) != 0){
3401 if(e == -1){
3402 q_status_message2(SM_ORDER | SM_DING, 3, 5,
3403 _("Error opening temporary addrbook file %.200s: %.200s"),
3404 ab->rd->lf, error_description(errno));
3405 dprint((1,
3406 "adrbk_write: error opening temp file %s\n",
3407 ab->rd->lf ? ab->rd->lf : "?"));
3409 else{
3410 q_status_message2(SM_ORDER | SM_DING, 3, 5,
3411 _("Error copying to %.200s: %.200s"),
3412 ab->rd->rn, error_description(errno));
3413 dprint((1,
3414 "adrbk_write: error copying from %s to %s\n",
3415 ab->rd->lf ? ab->rd->lf : "?",
3416 ab->rd->rn ? ab->rd->rn : "?"));
3419 q_status_message(SM_ORDER | SM_DING, 5, 5,
3420 _("Copy of addrbook to remote folder failed, changes NOT saved remotely"));
3422 else{
3423 rd_update_metadata(ab->rd, datebuf);
3424 ab->rd->read_status = 'W';
3425 dprint((7,
3426 "%s: copied local to remote in adrbk_write (%ld)\n",
3427 ab->rd->rn ? ab->rd->rn : "?",
3428 (long)ab->rd->last_use));
3431 ab->rd->flags &= ~BELIEVE_CACHE;
3434 writing = 0;
3436 if(we_cancel)
3437 cancel_busy_cue(0);
3439 if(temp_filename){
3440 our_unlink(temp_filename);
3441 fs_give((void **) &temp_filename);
3444 return 0;
3447 io_error:
3448 if(we_cancel)
3449 cancel_busy_cue(-1);
3451 if(enable_intr_handling && we_turned_on)
3452 intr_handling_off();
3454 #ifndef DOS
3455 (void)signal(SIGHUP, save_sighup);
3456 #endif
3457 if(interrupt_happened){
3458 q_status_message(0, 1, 2, _("Interrupt! Reverting to previous version"));
3459 display_message('x');
3460 dprint((1, "adrbk_write(%s): Interrupt\n",
3461 ab->filename ? ab->filename : "?"));
3463 else
3464 dprint((1, "adrbk_write(%s) (%s): some sort of io_error\n",
3465 ab->filename ? ab->filename : "?",
3466 ab->our_filecopy ? ab->our_filecopy : "?"));
3468 writing = 0;
3470 if(ab_stream != NULL){
3471 fclose(ab_stream);
3472 ab_stream = (FILE *)NULL;
3475 if(temp_filename){
3476 our_unlink(temp_filename);
3477 fs_give((void **) &temp_filename);
3480 adrbk_partial_close(ab);
3482 if(interrupt_happened){
3483 errno = EINTR; /* for nicer error message */
3484 return -5;
3486 else
3487 return -2;
3492 * Writes one addrbook entry with wrapping. Fills in widths
3493 * for display purposes. Returns 0, or EOF on error.
3495 * Continuation lines always start with spaces. Tabs are treated as
3496 * separators, never as whitespace. When we output tab separators we
3497 * always put them on the ends of lines, never on the start of a line
3498 * after a continuation. That is, there is always something printable
3499 * after continuation spaces.
3502 write_single_abook_entry(AdrBk_Entry *ae, FILE *fp, int *ret_nick_width,
3503 int *ret_full_width, int *ret_addr_width, int *ret_fcc_width)
3505 int len = 0;
3506 int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
3507 int tmplen, this_len;
3508 char *write_this = NULL;
3510 if(fp == (FILE *) NULL){
3511 dprint((1, "write_single_abook_entry: fp is NULL\n"));
3512 return(EOF);
3515 if(ae->nickname){
3516 nick_width = utf8_width(ae->nickname);
3518 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3519 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->nickname);
3521 this_len = strlen(write_this ? write_this : "");
3524 * We aren't too concerned with where it wraps.
3525 * Long lines are ok as long as they aren't super long.
3527 if(len > 100 || (len+this_len > 150 && len > 20)){
3528 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3529 dprint((1,
3530 "write_single_abook_entry: fputs ind1 failed\n"));
3531 return(EOF);
3534 len = this_len + INDENT;
3537 len += this_len;
3539 if(fputs(write_this, fp) == EOF){
3540 dprint((1,
3541 "write_single_abook_entry: fputs nick failed\n"));
3542 return(EOF);
3545 else
3546 nick_width = 0;
3548 putc(TAB, fp);
3549 len++;
3551 if(ae->fullname){
3552 full_width = utf8_width(ae->fullname);
3554 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3555 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fullname);
3557 this_len = strlen(write_this ? write_this : "");
3560 if(len > 100 || (len+this_len > 150 && len > 20)){
3561 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3562 dprint((1,
3563 "write_single_abook_entry: fputs ind2 failed\n"));
3564 return(EOF);
3567 len = this_len + INDENT;
3570 len += this_len;
3572 if(fputs(write_this, fp) == EOF){
3573 dprint((1,
3574 "write_single_abook_entry: fputs full failed\n"));
3575 return(EOF);
3578 else
3579 full_width = 0;
3581 putc(TAB, fp);
3582 len++;
3584 /* special case, make sure empty list has () */
3585 if(ae->tag == List && ae->addr.list == NULL){
3586 addr_width = 0;
3587 putc('(', fp);
3588 putc(')', fp);
3589 len += 2;
3591 else if(ae->addr.addr != NULL || ae->addr.list != NULL){
3592 if(ae->tag == Single){
3593 /*----- Single: just one address ----*/
3594 if(ae->addr.addr){
3595 addr_width = utf8_width(ae->addr.addr);
3597 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3598 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->addr.addr);
3600 this_len = strlen(write_this ? write_this : "");
3602 if(len > 100 || (len+this_len > 150 && len > 20)){
3603 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3604 dprint((1,
3605 "write_single_abook_entry: fputs ind3 failed\n"));
3606 return(EOF);
3609 len = this_len + INDENT;
3612 len += this_len;
3614 if(fputs(write_this, fp) == EOF){
3615 dprint((1,
3616 "write_single_abook_entry: fputs addr failed\n"));
3617 return(EOF);
3620 else
3621 addr_width = 0;
3623 else if(ae->tag == List){
3624 register char **a2;
3626 /*----- List: a distribution list ------*/
3627 putc('(', fp);
3628 len++;
3629 addr_width = 0;
3630 for(a2 = ae->addr.list; *a2 != NULL; a2++){
3631 if(*a2){
3632 if(a2 != ae->addr.list){
3633 putc(',', fp);
3634 len++;
3637 addr_width = MAX(addr_width, utf8_width(*a2));
3639 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3640 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, *a2);
3642 this_len = strlen(write_this ? write_this : "");
3644 if(len > 100 || (len+this_len > 150 && len > 20)){
3645 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3646 dprint((1,
3647 "write_single_abook_entry: fputs ind3 failed\n"));
3648 return(EOF);
3651 len = this_len + INDENT;
3654 len += this_len;
3656 if(fputs(write_this, fp) == EOF){
3657 dprint((1,
3658 "write_single_abook_entry: fputs addrl failed\n"));
3659 return(EOF);
3664 putc(')', fp);
3665 len++;
3669 /* If either fcc or extra exists, output both, otherwise, neither */
3670 if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
3671 putc(TAB, fp);
3672 len++;
3674 if(ae->fcc && ae->fcc[0]){
3675 fcc_width = utf8_width(ae->fcc);
3677 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3678 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fcc);
3680 this_len = strlen(write_this ? write_this : "");
3682 if(len > 100 || (len+this_len > 150 && len > 20)){
3683 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3684 dprint((1,
3685 "write_single_abook_entry: fputs ind4 failed\n"));
3686 return(EOF);
3689 len = this_len + INDENT;
3692 len += this_len;
3694 if(fputs(write_this, fp) == EOF){
3695 dprint((1,
3696 "write_single_abook_entry: fputs fcc failed\n"));
3697 return(EOF);
3700 else
3701 fcc_width = 0;
3703 putc(TAB, fp);
3704 len++;
3706 if(ae->extra && ae->extra[0]){
3707 int space;
3708 char *cur, *end;
3709 char *extra_copy;
3711 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3712 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->extra);
3715 * Copy ae->extra and replace newlines with spaces.
3716 * The reason we do this is because the continuation lines
3717 * produced by rfc1522_encode may interact with the
3718 * continuation lines produced below in a bad way.
3719 * In particular, what can happen is that the 1522 continuation
3720 * newline can be followed immediately by a newline produced
3721 * below. That breaks the continuation since that is a
3722 * line with nothing on it. Just turn 1522 continuations into
3723 * spaces, which work fine with 1522_decode.
3725 extra_copy = cpystr(write_this);
3726 REPLACE_NEWLINES_WITH_SPACE(extra_copy);
3728 tmplen = strlen(extra_copy);
3730 if(len > 100 || (len+tmplen > 150 && len > 20)){
3731 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3732 dprint((1,
3733 "write_single_abook_entry: fprintf indent failed\n"));
3734 return(EOF);
3737 len = INDENT;
3740 space = MAX(70 - len, 5);
3741 cur = extra_copy;
3742 end = cur + tmplen;
3743 while(cur < end){
3744 if(end-cur > space){
3745 int i;
3747 /* find first space after spot we want to break */
3748 for(i = space; cur+i < end && cur[i] != SPACE; i++)
3751 cur[i] = '\0';
3752 if(fputs(cur, fp) == EOF){
3753 dprint((1,
3754 "write_single_abook_entry: fputs extra failed\n"));
3755 return(EOF);
3758 cur += (i+1);
3760 if(cur < end){
3761 if(fprintf(fp, "%s%s", NEWLINE, INDENTXTRA) == EOF){
3762 dprint((1,
3763 "write_single_abook_entry: fprintf indent failed\n"));
3764 return(EOF);
3767 space = 70 - INDENT;
3770 else{
3771 if(fputs(cur, fp) == EOF){
3772 dprint((1,
3773 "write_single_abook_entry: fputs extra failed\n"));
3774 return(EOF);
3777 cur = end;
3781 if(extra_copy)
3782 fs_give((void **)&extra_copy);
3785 else
3786 fcc_width = 0;
3788 fprintf(fp, "%s", NEWLINE);
3790 if(ret_nick_width)
3791 *ret_nick_width = nick_width;
3792 if(ret_full_width)
3793 *ret_full_width = full_width;
3794 if(ret_addr_width)
3795 *ret_addr_width = addr_width;
3796 if(ret_fcc_width)
3797 *ret_fcc_width = fcc_width;
3799 return(0);
3803 char *
3804 backcompat_encoding_for_abook(char *buf1, size_t buf1len, char *buf2,
3805 size_t buf2len, char *srcstr)
3807 char *encoded = NULL;
3808 char *p;
3809 int its_ascii = 1;
3812 for(p = srcstr; *p && its_ascii; p++)
3813 if(*p & 0x80)
3814 its_ascii = 0;
3816 /* if it is ascii, go with that */
3817 if(its_ascii)
3818 encoded = srcstr;
3819 else{
3820 char *trythischarset = NULL;
3823 * If it is possible to translate the UTF-8
3824 * string into the user's character set then
3825 * do that. For backwards compatibility with
3826 * old pines.
3828 if(ps_global->keyboard_charmap && ps_global->keyboard_charmap[0])
3829 trythischarset = ps_global->keyboard_charmap;
3830 else if(ps_global->display_charmap && ps_global->display_charmap[0])
3831 trythischarset = ps_global->display_charmap;
3833 if(trythischarset){
3834 SIZEDTEXT src, dst;
3836 src.data = (unsigned char *) srcstr;
3837 src.size = strlen(srcstr);
3838 memset(&dst, 0, sizeof(dst));
3839 if(utf8_cstext(&src, trythischarset, &dst, 0)){
3840 if(dst.data){
3841 strncpy(buf1, (char *) dst.data, buf1len);
3842 buf1[buf1len-1] = '\0';
3843 fs_give((void **) &dst.data);
3844 encoded = rfc1522_encode(buf2, buf2len,
3845 (unsigned char *) buf1, trythischarset);
3846 if(encoded)
3847 REPLACE_NEWLINES_WITH_SPACE(encoded);
3852 if(!encoded){
3853 encoded = rfc1522_encode(buf1, buf1len, (unsigned char *) srcstr, "UTF-8");
3854 if(encoded)
3855 REPLACE_NEWLINES_WITH_SPACE(encoded);
3859 return(encoded);
3864 percent_abook_saved(void)
3866 return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
3867 (unsigned long)tot_for_percent));
3872 * Free memory associated with entry ae.
3874 * Args: ae -- Address book entry to be freed.
3876 void
3877 free_ae(AdrBk_Entry **ae)
3879 if(!ae || !(*ae))
3880 return;
3882 free_ae_parts(*ae);
3883 fs_give((void **) ae);
3888 * Free memory associated with entry ae but not ae itself.
3890 void
3891 free_ae_parts(AdrBk_Entry *ae)
3893 char **p;
3895 if(!ae)
3896 return;
3898 if(ae->nickname && ae->nickname != empty)
3899 fs_give((void **) &ae->nickname);
3901 if(ae->fullname && ae->fullname != empty)
3902 fs_give((void **) &ae->fullname);
3904 if(ae->tag == Single){
3905 if(ae->addr.addr && ae->addr.addr != empty)
3906 fs_give((void **) &ae->addr.addr);
3908 else if(ae->tag == List){
3909 if(ae->addr.list){
3910 for(p = ae->addr.list; *p; p++)
3911 if(*p != empty)
3912 fs_give((void **) p);
3914 fs_give((void **) &ae->addr.list);
3918 if(ae->fcc && ae->fcc != empty)
3919 fs_give((void **) &ae->fcc);
3921 if(ae->extra && ae->extra != empty)
3922 fs_give((void **) &ae->extra);
3924 defvalue_ae(ae);
3929 * Inserts element new_ae before element put_it_before_this.
3931 void
3932 insert_ab_entry(AdrBk *ab, a_c_arg_t put_it_before_this, AdrBk_Entry *new_ae,
3933 int use_deleted_list)
3935 adrbk_cntr_t before, old_count, new_count;
3936 AdrBk_Entry *ae_before, *ae_before_next;
3938 dprint((7, "- insert_ab_entry(before_this=%ld) -\n", (long) put_it_before_this));
3940 before = (adrbk_cntr_t) put_it_before_this;
3942 if(!ab || before == NO_NEXT || (!use_deleted_list && before > ab->count)
3943 || (use_deleted_list && before > ab->del_count)){
3946 else{
3948 * add space for new entry to array
3949 * slide entries [before ... old_count-1] down one
3950 * put new_ae into opened up slot
3952 old_count = use_deleted_list ? ab->del_count : ab->count;
3953 new_count = old_count + 1;
3954 if(old_count == 0){
3955 if(use_deleted_list){
3956 if(ab->del) /* shouldn't happen */
3957 fs_give((void **) &ab->del);
3959 /* first entry in new array */
3960 ab->del = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3961 defvalue_ae(ab->del);
3963 else{
3964 if(ab->arr) /* shouldn't happen */
3965 fs_give((void **) &ab->arr);
3967 /* first entry in new array */
3968 ab->arr = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3969 defvalue_ae(ab->arr);
3972 else{
3973 if(use_deleted_list){
3974 fs_resize((void **) &ab->del, new_count * sizeof(AdrBk_Entry));
3975 defvalue_ae(&ab->del[new_count-1]);
3977 else{
3978 fs_resize((void **) &ab->arr, new_count * sizeof(AdrBk_Entry));
3979 defvalue_ae(&ab->arr[new_count-1]);
3983 if(use_deleted_list){
3984 ab->del_count = new_count;
3986 ae_before = adrbk_get_delae(ab, (a_c_arg_t) before);
3987 if(ae_before){
3988 if(before < old_count){
3989 ae_before_next = adrbk_get_delae(ab, (a_c_arg_t) (before+1));
3990 memmove(ae_before_next, ae_before,
3991 (old_count-before) * sizeof(AdrBk_Entry));
3994 memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
3997 else{
3998 ab->count = new_count;
4000 ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4001 if(ae_before){
4002 if(before < old_count){
4003 ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4004 memmove(ae_before_next, ae_before,
4005 (old_count-before) * sizeof(AdrBk_Entry));
4008 memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
4014 if(!use_deleted_list)
4015 repair_abook_tries(ab);
4020 * Moves element move_this_one before element put_it_before_this.
4022 void
4023 move_ab_entry(AdrBk *ab, a_c_arg_t move_this_one, a_c_arg_t put_it_before_this)
4025 adrbk_cntr_t m, before;
4026 AdrBk_Entry ae_tmp;
4027 AdrBk_Entry *ae_m, *ae_m_next, *ae_before, *ae_before_prev, *ae_before_next;
4029 dprint((7, "- move_ab_entry(move_this=%ld,before_this=%ld) -\n", (long) move_this_one, (long) put_it_before_this));
4031 m = (adrbk_cntr_t) move_this_one;
4032 before = (adrbk_cntr_t) put_it_before_this;
4034 if(!ab || m == NO_NEXT || before == NO_NEXT
4035 || m == before || m+1 == before || before > ab->count){
4038 else if(m+1 < before){
4040 * copy m
4041 * slide entries [m+1 ... before-1] up one ("up" means smaller indices)
4042 * put m into opened up slot
4044 ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
4045 ae_m_next = adrbk_get_ae(ab, (a_c_arg_t) (m+1));
4046 ae_before_prev = adrbk_get_ae(ab, (a_c_arg_t) (before-1));
4047 if(ae_m && ae_m_next && ae_before_prev){
4048 memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4049 memmove(ae_m, ae_m_next, (before-m-1) * sizeof(ae_tmp));
4050 memcpy(ae_before_prev, &ae_tmp, sizeof(ae_tmp));
4053 else if(m > before){
4055 * copy m
4056 * slide entries [before ... m-1] down one
4057 * put m into opened up slot
4059 ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
4060 ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4061 ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4062 if(ae_m && ae_before && ae_before_next){
4063 memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4064 memmove(ae_before_next, ae_before, (m-before) * sizeof(ae_tmp));
4065 memcpy(ae_before, &ae_tmp, sizeof(ae_tmp));
4069 dprint((9, "- move_ab_entry: done -\n"));
4071 repair_abook_tries(ab);
4076 * Deletes element delete_this_one from in-core data structure.
4077 * If save_it is set the deleted entry is moved to the deleted_list.
4079 void
4080 delete_ab_entry(AdrBk *ab, a_c_arg_t delete_this_one, int save_it)
4082 adrbk_cntr_t d;
4083 AdrBk_Entry *ae_deleted, *ae_deleted_next;
4085 dprint((7, "- delete_ab_entry(delete_this=%ld,save_it=%d) -\n", (long) delete_this_one, save_it));
4087 d = (adrbk_cntr_t) delete_this_one;
4089 if(!ab || d == NO_NEXT || d >= ab->count){
4092 else{
4094 * Move the entry to the deleted_list if asked to.
4096 ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
4097 if(ae_deleted){
4098 if(save_it){
4099 char *oldnick, *newnick;
4100 size_t len;
4101 struct tm *tm_now;
4102 time_t now;
4105 * First prepend the prefix
4106 * #DELETED-YY/MM/DD#
4107 * to the nickname.
4109 now = time((time_t *) NULL);
4110 tm_now = localtime(&now);
4112 oldnick = ae_deleted->nickname;
4113 len = strlen(oldnick) + DELETED_LEN + strlen("YY/MM/DD#");
4115 newnick = (char *) fs_get((len+1) * sizeof(char));
4116 snprintf(newnick, len+1, "%s%02d/%02d/%02d#%s",
4117 DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1,
4118 tm_now->tm_mday, oldnick ? oldnick : "");
4119 newnick[len] = '\0';
4121 if(ae_deleted->nickname && ae_deleted->nickname != empty)
4122 fs_give((void **) &ae_deleted->nickname);
4124 ae_deleted->nickname = newnick;
4127 * Now insert this entry in the deleted_list.
4129 insert_ab_entry(ab, (a_c_arg_t) ab->del_count, ae_deleted, 1);
4131 else
4132 free_ae_parts(ae_deleted);
4136 * slide entries [deleted+1 ... count-1] up one
4138 if(d+1 < ab->count){
4139 ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
4140 ae_deleted_next = adrbk_get_ae(ab, (a_c_arg_t) (d+1));
4141 if(ae_deleted && ae_deleted_next)
4142 memmove(ae_deleted, ae_deleted_next, (ab->count-d-1) * sizeof(AdrBk_Entry));
4145 ab->count--;
4147 if(ab->count > 0)
4148 fs_resize((void **) &ab->arr, ab->count * sizeof(AdrBk_Entry));
4149 else
4150 fs_give((void **) &ab->arr);
4153 repair_abook_tries(ab);
4158 * We may want to be smarter about this and repair instead of
4159 * rebuild these.
4161 void
4162 repair_abook_tries(AdrBk *ab)
4164 if(ab->arr){
4165 AdrBk_Trie *save_nick_trie, *save_addr_trie,
4166 *save_full_trie, *save_revfull_trie;
4168 save_nick_trie = ab->nick_trie;
4169 ab->nick_trie = NULL;
4170 save_addr_trie = ab->addr_trie;
4171 ab->addr_trie = NULL;
4172 save_full_trie = ab->full_trie;
4173 ab->full_trie = NULL;
4174 save_revfull_trie = ab->revfull_trie;
4175 ab->revfull_trie = NULL;
4176 if(build_abook_tries(ab, NULL)){
4177 dprint((2, "trouble rebuilding tries, restoring\n"));
4178 if(ab->nick_trie)
4179 free_abook_trie(&ab->nick_trie);
4181 /* better than nothing */
4182 ab->nick_trie = save_nick_trie;
4184 if(ab->addr_trie)
4185 free_abook_trie(&ab->addr_trie);
4187 ab->addr_trie = save_addr_trie;
4189 if(ab->full_trie)
4190 free_abook_trie(&ab->full_trie);
4192 ab->full_trie = save_full_trie;
4194 if(ab->revfull_trie)
4195 free_abook_trie(&ab->revfull_trie);
4197 ab->revfull_trie = save_revfull_trie;
4199 else{
4200 if(save_nick_trie)
4201 free_abook_trie(&save_nick_trie);
4203 if(save_addr_trie)
4204 free_abook_trie(&save_addr_trie);
4206 if(save_full_trie)
4207 free_abook_trie(&save_full_trie);
4209 if(save_revfull_trie)
4210 free_abook_trie(&save_revfull_trie);
4217 * Free the list of distribution lists which have been expanded.
4218 * Leaves the head of the list alone.
4220 * Args: exp_head -- Head of the expanded list.
4222 void
4223 exp_free(EXPANDED_S *exp_head)
4225 EXPANDED_S *e, *the_next_one;
4227 e = exp_head ? exp_head->next : NULL;
4229 if(!e)
4230 return;
4232 while(e){
4233 the_next_one = e->next;
4234 fs_give((void **)&e);
4235 e = the_next_one;
4238 exp_head->next = (EXPANDED_S *)NULL;
4243 * Is entry n expanded?
4245 * Args: exp_head -- Head of the expanded list.
4246 * n -- The entry num to check
4249 exp_is_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4251 register EXPANDED_S *e;
4252 adrbk_cntr_t nn;
4254 nn = (adrbk_cntr_t)n;
4256 e = exp_head ? exp_head->next : NULL;
4259 * The list is kept ordered, so we search until we find it or are
4260 * past it.
4262 while(e){
4263 if(e->ent >= nn)
4264 break;
4266 e = e->next;
4269 return(e && e->ent == nn);
4274 * How many entries expanded in this addrbook.
4276 * Args: exp_head -- Head of the expanded list.
4279 exp_howmany_expanded(EXPANDED_S *exp_head)
4281 register EXPANDED_S *e;
4282 int cnt = 0;
4284 e = exp_head ? exp_head->next : NULL;
4286 while(e){
4287 cnt++;
4288 e = e->next;
4291 return(cnt);
4296 * Are any entries expanded?
4298 * Args: exp_head -- Head of the expanded list.
4301 exp_any_expanded(EXPANDED_S *exp_head)
4303 return(exp_head && exp_head->next != NULL);
4308 * Return next entry num in list.
4310 * Args: cur -- Current position in the list.
4312 * Result: Returns the number of the next entry, or NO_NEXT if there is
4313 * no next entry. As a side effect, the cur pointer is incremented.
4315 adrbk_cntr_t
4316 exp_get_next(EXPANDED_S **cur)
4318 adrbk_cntr_t ret = NO_NEXT;
4320 if(cur && *cur && (*cur)->next){
4321 ret = (*cur)->next->ent;
4322 *cur = (*cur)->next;
4325 return(ret);
4330 * Mark entry n as being expanded.
4332 * Args: exp_head -- Head of the expanded list.
4333 * n -- The entry num to mark
4335 void
4336 exp_set_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4338 register EXPANDED_S *e;
4339 EXPANDED_S *new;
4340 adrbk_cntr_t nn;
4342 nn = (adrbk_cntr_t)n;
4343 if(!exp_head)
4344 alpine_panic("exp_head not set in exp_set_expanded");
4346 for(e = exp_head; e->next; e = e->next)
4347 if(e->next->ent >= nn)
4348 break;
4350 if(e->next && e->next->ent == nn) /* already there */
4351 return;
4353 /* add new after e */
4354 new = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
4355 new->ent = nn;
4356 new->next = e->next;
4357 e->next = new;
4362 * Mark entry n as being *not* expanded.
4364 * Args: exp_head -- Head of the expanded list.
4365 * n -- The entry num to mark
4367 void
4368 exp_unset_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4370 register EXPANDED_S *e;
4371 EXPANDED_S *delete_this_one = NULL;
4372 adrbk_cntr_t nn;
4374 nn = (adrbk_cntr_t)n;
4375 if(!exp_head)
4376 alpine_panic("exp_head not set in exp_unset_expanded");
4378 for(e = exp_head; e->next; e = e->next)
4379 if(e->next->ent >= nn)
4380 break;
4382 if(e->next && e->next->ent == nn){
4383 delete_this_one = e->next;
4384 e->next = e->next->next;
4387 if(delete_this_one)
4388 fs_give((void **)&delete_this_one);
4393 * Adjust the "expanded" list to correspond to addrbook entry n being
4394 * deleted.
4396 * Args: exp_head -- Head of the expanded list.
4397 * n -- The entry num being deleted
4399 void
4400 exp_del_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4402 register EXPANDED_S *e;
4403 int delete_when_done = 0;
4404 adrbk_cntr_t nn;
4406 nn = (adrbk_cntr_t)n;
4407 if(!exp_head)
4408 alpine_panic("exp_head not set in exp_del_nth");
4410 e = exp_head->next;
4411 while(e && e->ent < nn)
4412 e = e->next;
4414 if(e){
4415 if(e->ent == nn){
4416 delete_when_done++;
4417 e = e->next;
4420 while(e){
4421 e->ent--; /* adjust entry nums */
4422 e = e->next;
4425 if(delete_when_done)
4426 exp_unset_expanded(exp_head, n);
4432 * Adjust the "expanded" list to correspond to a new addrbook entry being
4433 * added between current entries n-1 and n.
4435 * Args: exp_head -- Head of the expanded list.
4436 * n -- The entry num being added
4438 * The new entry is not marked expanded.
4440 void
4441 exp_add_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4443 register EXPANDED_S *e;
4444 adrbk_cntr_t nn;
4446 nn = (adrbk_cntr_t)n;
4447 if(!exp_head)
4448 alpine_panic("exp_head not set in exp_add_nth");
4450 e = exp_head->next;
4451 while(e && e->ent < nn)
4452 e = e->next;
4454 while(e){
4455 e->ent++; /* adjust entry nums */
4456 e = e->next;
4461 static AdrBk *ab_for_sort;
4464 * Compare two address book entries. Args are AdrBk_Entry **'s.
4465 * Sorts lists after simple addresses and then sorts on Fullname field.
4468 cmp_ae_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4470 AdrBk_Entry **x = (AdrBk_Entry **)a,
4471 **y = (AdrBk_Entry **)b;
4472 int result;
4474 if((*x)->tag == List && (*y)->tag == Single)
4475 result = 1;
4476 else if((*x)->tag == Single && (*y)->tag == List)
4477 result = -1;
4478 else{
4479 register char *p, *q, *r, *s;
4481 p = (*x)->fullname;
4482 if(*p == '"' && *(p+1))
4483 p++;
4485 q = (*y)->fullname;
4486 if(*q == '"' && *(q+1))
4487 q++;
4489 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4490 SIZEOF_20KBUF, p);
4492 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4493 SIZEOF_20KBUF-10000, q);
4495 result = (*pcollator)(r, s);
4496 if(result == 0)
4497 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4500 return(result);
4505 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4506 * Sorts lists after simple addresses and then sorts on Fullname field.
4509 cmp_cntr_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4511 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4512 *y = (adrbk_cntr_t *)b;
4513 AdrBk_Entry *x_ae,
4514 *y_ae;
4516 if(ps_global->intr_pending)
4517 longjmp(jump_over_qsort, 1);
4519 ALARM_BLIP();
4521 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4522 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4524 return(cmp_ae_by_full_lists_last((const qsort_t *) &x_ae,
4525 (const qsort_t *) &y_ae));
4530 * Compare two address book entries. Args are AdrBk_Entry **'s.
4531 * Sorts on Fullname field.
4534 cmp_ae_by_full(const qsort_t *a, const qsort_t *b)
4536 AdrBk_Entry **x = (AdrBk_Entry **)a,
4537 **y = (AdrBk_Entry **)b;
4538 int result;
4539 register char *p, *q, *r, *s;
4541 p = (*x)->fullname;
4542 if(*p == '"' && *(p+1))
4543 p++;
4545 q = (*y)->fullname;
4546 if(*q == '"' && *(q+1))
4547 q++;
4549 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4550 SIZEOF_20KBUF, p);
4551 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4552 SIZEOF_20KBUF-10000, q);
4553 result = (*pcollator)(r, s);
4554 if(result == 0)
4555 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4557 return(result);
4562 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4563 * Sorts on Fullname field.
4566 cmp_cntr_by_full(const qsort_t *a, const qsort_t *b)
4568 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4569 *y = (adrbk_cntr_t *)b;
4570 AdrBk_Entry *x_ae,
4571 *y_ae;
4573 if(ps_global->intr_pending)
4574 longjmp(jump_over_qsort, 1);
4576 ALARM_BLIP();
4578 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4579 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4581 return(cmp_ae_by_full((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4586 * Compare two address book entries. Args are AdrBk_Entry **'s.
4587 * Sorts lists after simple addresses and then sorts on Nickname field.
4590 cmp_ae_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4592 AdrBk_Entry **x = (AdrBk_Entry **)a,
4593 **y = (AdrBk_Entry **)b;
4594 int result;
4596 if((*x)->tag == List && (*y)->tag == Single)
4597 result = 1;
4598 else if((*x)->tag == Single && (*y)->tag == List)
4599 result = -1;
4600 else
4601 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4603 return(result);
4608 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4609 * Sorts lists after simple addresses and then sorts on Nickname field.
4612 cmp_cntr_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4614 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4615 *y = (adrbk_cntr_t *)b;
4616 AdrBk_Entry *x_ae,
4617 *y_ae;
4619 if(ps_global->intr_pending)
4620 longjmp(jump_over_qsort, 1);
4622 ALARM_BLIP();
4624 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4625 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4627 return(cmp_ae_by_nick_lists_last((const qsort_t *) &x_ae,
4628 (const qsort_t *) &y_ae));
4633 * Compare two address book entries. Args are AdrBk_Entry **'s.
4634 * Sorts on Nickname field.
4637 cmp_ae_by_nick(const qsort_t *a, const qsort_t *b)
4639 AdrBk_Entry **x = (AdrBk_Entry **)a,
4640 **y = (AdrBk_Entry **)b;
4642 return((*pcollator)((*x)->nickname, (*y)->nickname));
4647 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4648 * Sorts on Nickname field.
4651 cmp_cntr_by_nick(const qsort_t *a, const qsort_t *b)
4653 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4654 *y = (adrbk_cntr_t *)b;
4655 AdrBk_Entry *x_ae,
4656 *y_ae;
4658 if(ps_global->intr_pending)
4659 longjmp(jump_over_qsort, 1);
4661 ALARM_BLIP();
4663 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4664 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4666 return(cmp_ae_by_nick((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4671 * For sorting a simple list of pointers to addresses (skip initial quotes)
4674 cmp_addr(const qsort_t *a1, const qsort_t *a2)
4676 char *x = *(char **)a1, *y = *(char **)a2;
4677 char *r, *s;
4679 if(x && *x == '"')
4680 x++;
4682 if(y && *y == '"')
4683 y++;
4685 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4686 SIZEOF_20KBUF, x);
4687 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4688 SIZEOF_20KBUF-10000, y);
4689 return((*pcollator)(r, s));
4694 * Sort an array of strings, except skip initial quotes.
4696 void
4697 sort_addr_list(char **list)
4699 register char **p;
4701 /* find size of list */
4702 for(p = list; *p != NULL; p++)
4703 ;/* do nothing */
4705 qsort((qsort_t *)list, (size_t)(p - list), sizeof(char *), cmp_addr);
4710 * Sort this address book.
4712 * Args: ab -- address book to sort
4713 * current_entry_num -- see next description
4714 * new_entry_num -- return new entry_num of current_entry_num here
4716 * Result: return code: 0 all went well
4717 * -2 error writing address book, check errno
4719 * The sorting strategy is to allocate an array of length ab->count which
4720 * contains the element numbers 0, 1, ..., ab->count - 1, representing the
4721 * entries in the addrbook, of course. Sort the array, then use that
4722 * result to swap ae's in the in-core addrbook array before writing it out.
4725 adrbk_sort(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num, int be_quiet)
4727 adrbk_cntr_t *sort_array, *inv, tmp;
4728 long i, j, hi, count;
4729 int skip_the_sort = 0, we_cancel = 0, we_turned_on = 0;
4730 AdrBk_Entry ae_tmp, *ae_i, *ae_hi;
4731 EXPANDED_S *e, *e2, *smallest;
4733 dprint((5, "- adrbk_sort -\n"));
4735 count = (long) (ab->count);
4737 if(!ab)
4738 return -2;
4740 if(ab->sort_rule == AB_SORT_RULE_NONE)
4741 return 0;
4743 if(count < 2)
4744 return 0;
4746 sort_array = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4747 inv = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4749 for(i = 0L; i < count; i++)
4750 sort_array[i] = (adrbk_cntr_t) i;
4752 ab_for_sort = ab;
4754 if(setjmp(jump_over_qsort))
4755 skip_the_sort = 1;
4757 if(!skip_the_sort){
4758 we_turned_on = intr_handling_on();
4759 if(!be_quiet)
4760 we_cancel = busy_cue(_("Sorting address book"), NULL, 0);
4762 qsort((qsort_t *)sort_array,
4763 (size_t)count,
4764 sizeof(adrbk_cntr_t),
4765 (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
4766 cmp_cntr_by_full_lists_last :
4767 (ab->sort_rule == AB_SORT_RULE_FULL) ?
4768 cmp_cntr_by_full :
4769 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
4770 cmp_cntr_by_nick_lists_last :
4771 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
4772 cmp_cntr_by_nick);
4775 dprint((9, "- adrbk_sort: done with first sort -\n"));
4777 if(we_turned_on)
4778 intr_handling_off();
4780 if(skip_the_sort){
4781 q_status_message(SM_ORDER, 3, 3,
4782 _("Address book sort cancelled, using old order for now"));
4783 goto skip_the_write_too;
4786 dprint((5, "- adrbk_sort (%s)\n",
4787 ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
4788 ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
4789 ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
4790 ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
4793 * Rearrange the in-core array of ae's to be in the new order.
4794 * We can do that by sorting the inverse into the correct order in parallel.
4796 for(i = 0L; i < count; i++)
4797 inv[sort_array[i]] = i;
4799 if(new_entry_num && (adrbk_cntr_t) current_entry_num >= 0
4800 && (adrbk_cntr_t) current_entry_num < count){
4801 *new_entry_num = inv[(adrbk_cntr_t) current_entry_num];
4805 * The expanded and selected lists will be wrong now. Correct them.
4806 * First the expanded list.
4808 e = ab->exp ? ab->exp->next : NULL;
4809 while(e){
4810 if(e->ent >= 0 && e->ent < count)
4811 e->ent = inv[e->ent];
4813 e = e->next;
4817 * And sort into ascending order as expected by the exp_ routines.
4819 e = ab->exp ? ab->exp->next : NULL;
4820 while(e){
4821 /* move smallest to e */
4822 e2 = e;
4823 smallest = e;
4824 while(e2){
4825 if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4826 smallest = e2;
4828 e2 = e2->next;
4831 /* swap values in e and smallest */
4832 if(e != smallest){
4833 tmp = e->ent;
4834 e->ent = smallest->ent;
4835 smallest->ent = tmp;
4838 e = e->next;
4842 * Same thing for the selected list.
4844 e = ab->selects ? ab->selects->next : NULL;
4845 while(e){
4846 if(e->ent >= 0 && e->ent < count)
4847 e->ent = inv[e->ent];
4849 e = e->next;
4852 e = ab->selects ? ab->selects->next : NULL;
4853 while(e){
4854 /* move smallest to e */
4855 e2 = e;
4856 smallest = e;
4857 while(e2){
4858 if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4859 smallest = e2;
4861 e2 = e2->next;
4864 /* swap values in e and smallest */
4865 if(e != smallest){
4866 tmp = e->ent;
4867 e->ent = smallest->ent;
4868 smallest->ent = tmp;
4871 e = e->next;
4874 for(i = 0L; i < count; i++){
4875 if(i != inv[i]){
4876 /* find inv[j] which = i */
4877 for(j = i+1; j < count; j++){
4878 if(i == inv[j]){
4879 hi = j;
4880 break;
4884 /* swap i and hi */
4885 ae_i = adrbk_get_ae(ab, (a_c_arg_t) i);
4886 ae_hi = adrbk_get_ae(ab, (a_c_arg_t) hi);
4887 if(ae_i && ae_hi){
4888 memcpy(&ae_tmp, ae_i, sizeof(ae_tmp));
4889 memcpy(ae_i, ae_hi, sizeof(ae_tmp));
4890 memcpy(ae_hi, &ae_tmp, sizeof(ae_tmp));
4892 /* else can't happen */
4894 inv[hi] = inv[i];
4898 if(we_cancel)
4899 cancel_busy_cue(0);
4901 repair_abook_tries(ab);
4903 dprint((9, "- adrbk_sort: done with rearranging -\n"));
4905 skip_the_write_too:
4906 if(we_cancel)
4907 cancel_busy_cue(0);
4909 if(sort_array)
4910 fs_give((void **) &sort_array);
4912 if(inv)
4913 fs_give((void **) &inv);
4915 return 0;
4920 * Returns 1 if any addrbooks are in Open state, 0 otherwise.
4922 * This is a test for ostatus == Open, not for whether or not the address book
4923 * FILE is opened.
4926 any_ab_open(void)
4928 int i, ret = 0;
4930 if(as.initialized)
4931 for(i = 0; ret == 0 && i < as.n_addrbk; i++)
4932 if(as.adrbks[i].ostatus == Open)
4933 ret++;
4935 return(ret);
4940 * Make sure addrbooks are minimally initialized.
4942 void
4943 init_ab_if_needed(void)
4945 dprint((9, "- init_ab_if_needed -\n"));
4947 if(!as.initialized)
4948 (void)init_addrbooks(Closed, 0, 0, 1);
4953 * Sets everything up to get started.
4955 * Args: want_status -- The desired OpenStatus for all addrbooks.
4956 * reset_to_top -- Forget about the old location and put cursor
4957 * at top.
4958 * open_if_only_one -- If want_status is HalfOpen and there is only
4959 * section to look at, then promote want_status
4960 * to Open.
4961 * ro_warning -- Set ReadOnly warning global
4963 * Return: 1 if ok, 0 if problem
4966 init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, int ro_warning)
4968 register PerAddrBook *pab;
4969 char *q, **t;
4970 long line;
4972 dprint((4, "-- init_addrbooks(%s, %d, %d, %d) --\n",
4973 want_status==Open ?
4974 "Open" :
4975 want_status==HalfOpen ?
4976 "HalfOpen" :
4977 want_status==ThreeQuartOpen ?
4978 "ThreeQuartOpen" :
4979 want_status==NoDisplay ?
4980 "NoDisplay" :
4981 "Closed",
4982 reset_to_top, open_if_only_one, ro_warning));
4984 as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
4985 - HEADER_ROWS(ps_global);
4986 if(as.l_p_page <= 0)
4987 as.no_op_possbl++;
4988 else
4989 as.no_op_possbl = 0;
4991 as.ro_warning = ro_warning;
4993 /* already been initialized */
4994 if(as.initialized){
4995 int i;
4998 * Special case. If there is only one addressbook we start the
4999 * user out with that open.
5001 if(want_status == HalfOpen &&
5002 ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5003 (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5004 F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5005 want_status = Open;
5007 /* open to correct state */
5008 for(i = 0; i < as.n_addrbk; i++)
5009 init_abook(&as.adrbks[i], want_status);
5011 if(reset_to_top){
5012 warp_to_beginning();
5013 as.top_ent = 0L;
5014 line = first_selectable_line(0L);
5015 if(line == NO_LINE)
5016 as.cur_row = 0L;
5017 else
5018 as.cur_row = line;
5020 if(as.cur_row >= as.l_p_page)
5021 as.top_ent += (as.cur_row - as.l_p_page + 1);
5023 as.old_cur_row = as.cur_row;
5026 dprint((4, "init_addrbooks: already initialized: %d books\n",
5027 as.n_addrbk));
5028 return((as.n_addrbk + as.n_serv) ? 1 : 0);
5031 as.initialized = 1;
5033 /* count directory servers */
5034 as.n_serv = 0;
5035 as.n_impl = 0;
5036 #ifdef ENABLE_LDAP
5037 if(ps_global->VAR_LDAP_SERVERS &&
5038 ps_global->VAR_LDAP_SERVERS[0] &&
5039 ps_global->VAR_LDAP_SERVERS[0][0])
5040 for(t = ps_global->VAR_LDAP_SERVERS; t[0] && t[0][0]; t++){
5041 LDAP_SERV_S *info;
5043 as.n_serv++;
5044 info = break_up_ldap_server(*t);
5045 as.n_impl += (info && info->impl) ? 1 : 0;
5046 if(info)
5047 free_ldap_server_info(&info);
5049 #endif /* ENABLE_LDAP */
5051 /* count addressbooks */
5052 as.how_many_personals = 0;
5053 if(ps_global->VAR_ADDRESSBOOK &&
5054 ps_global->VAR_ADDRESSBOOK[0] &&
5055 ps_global->VAR_ADDRESSBOOK[0][0])
5056 for(t = ps_global->VAR_ADDRESSBOOK; t[0] && t[0][0]; t++)
5057 as.how_many_personals++;
5059 as.n_addrbk = as.how_many_personals;
5060 if(ps_global->VAR_GLOB_ADDRBOOK &&
5061 ps_global->VAR_GLOB_ADDRBOOK[0] &&
5062 ps_global->VAR_GLOB_ADDRBOOK[0][0])
5063 for(t = ps_global->VAR_GLOB_ADDRBOOK; t[0] && t[0][0]; t++)
5064 as.n_addrbk++;
5066 if(want_status == HalfOpen &&
5067 ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5068 (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5069 F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5070 want_status = Open;
5074 * allocate array of PerAddrBooks
5075 * (we don't give this up until we exit Pine, but it's small)
5077 if(as.n_addrbk){
5078 as.adrbks = (PerAddrBook *)fs_get(as.n_addrbk * sizeof(PerAddrBook));
5079 memset((void *)as.adrbks, 0, as.n_addrbk * sizeof(PerAddrBook));
5081 /* init PerAddrBook data */
5082 for(as.cur = 0; as.cur < as.n_addrbk; as.cur++){
5083 char *nickname = NULL,
5084 *filename = NULL;
5086 if(as.cur < as.how_many_personals)
5087 q = ps_global->VAR_ADDRESSBOOK[as.cur];
5088 else
5089 q = ps_global->VAR_GLOB_ADDRBOOK[as.cur - as.how_many_personals];
5091 pab = &as.adrbks[as.cur];
5093 /* Parse entry for optional nickname and filename */
5094 get_pair(q, &nickname, &filename, 0, 0);
5096 if(nickname && !*nickname)
5097 fs_give((void **)&nickname);
5099 strncpy(tmp_20k_buf, filename, SIZEOF_20KBUF);
5100 fs_give((void **)&filename);
5102 filename = tmp_20k_buf;
5103 if(nickname == NULL)
5104 pab->abnick = cpystr(filename);
5105 else
5106 pab->abnick = nickname;
5108 if(*filename == '~')
5109 fnexpand(filename, SIZEOF_20KBUF);
5111 if(*filename == '{' || is_absolute_path(filename)){
5112 pab->filename = cpystr(filename); /* fully qualified */
5114 else{
5115 char book_path[MAXPATH+1];
5116 char *lc = last_cmpnt(ps_global->pinerc);
5118 book_path[0] = '\0';
5119 if(lc != NULL){
5120 strncpy(book_path, ps_global->pinerc,
5121 MIN(lc - ps_global->pinerc, sizeof(book_path)-1));
5122 book_path[MIN(lc - ps_global->pinerc,
5123 sizeof(book_path)-1)] = '\0';
5126 strncat(book_path, filename,
5127 sizeof(book_path)-1-strlen(book_path));
5128 pab->filename = cpystr(book_path);
5131 if(*pab->filename == '{')
5132 pab->type |= REMOTE_VIA_IMAP;
5134 if(as.cur >= as.how_many_personals)
5135 pab->type |= GLOBAL;
5137 pab->access = adrbk_access(pab);
5139 /* global address books are forced readonly */
5140 if(pab->type & GLOBAL && pab->access != NoAccess)
5141 pab->access = ReadOnly;
5143 pab->ostatus = TotallyClosed;
5146 * and remember that the memset above initializes everything
5147 * else to 0
5150 init_abook(pab, want_status);
5155 * Have to reset_to_top in this case since this is the first open,
5156 * regardless of the value of the argument, since these values haven't been
5157 * set before here.
5159 as.cur = 0;
5160 as.top_ent = 0L;
5161 warp_to_beginning();
5162 line = first_selectable_line(0L);
5164 if(line == NO_LINE)
5165 as.cur_row = 0L;
5166 else
5167 as.cur_row = line;
5169 if(as.cur_row >= as.l_p_page){
5170 as.top_ent += (as.cur_row - as.l_p_page + 1);
5171 as.cur_row = as.l_p_page - 1;
5174 as.old_cur_row = as.cur_row;
5176 return((as.n_addrbk + as.n_serv) ? 1 : 0);
5181 * Something was changed in options screen, so need to start over.
5183 void
5184 addrbook_reset(void)
5186 dprint((4, "- addrbook_reset -\n"));
5187 completely_done_with_adrbks();
5192 * Sort was changed in options screen. Since we only sort normally
5193 * when we actually make a change to the address book, we need to
5194 * go out of our way to sort here.
5196 void
5197 addrbook_redo_sorts(void)
5199 int i;
5200 PerAddrBook *pab;
5201 AdrBk *ab;
5203 dprint((4, "- addrbook_redo_sorts -\n"));
5205 addrbook_reset();
5206 init_ab_if_needed();
5208 for(i = 0; i < as.n_addrbk; i++){
5209 pab = &as.adrbks[i];
5210 init_abook(pab, NoDisplay);
5211 ab = pab->address_book;
5213 if(!adrbk_is_in_sort_order(ab, 0))
5214 adrbk_write(ab, 0, NULL, NULL, 1, 0);
5217 addrbook_reset();
5222 * Returns type of access allowed on this addrbook.
5224 AccessType
5225 adrbk_access(PerAddrBook *pab)
5227 char fbuf[MAXPATH+1];
5228 AccessType access = NoExists;
5229 CONTEXT_S *dummy_cntxt = NULL;
5231 dprint((9, "- addrbook_access -\n"));
5233 if(pab && pab->type & REMOTE_VIA_IMAP){
5235 * Open_fcc creates the folder if it didn't already exist.
5237 if((pab->so = open_fcc(pab->filename, &dummy_cntxt, 1,
5238 "Error: ",
5239 " Can't fetch remote addrbook.")) != NULL){
5241 * We know the folder is there but don't know what access
5242 * rights we have until we try to select it, which we don't
5243 * want to do unless we have to. So delay evaluating.
5245 access = MaybeRorW;
5248 else if(pab){ /* local file */
5249 #if defined(NO_LOCAL_ADDRBOOKS)
5250 /* don't allow any access to local addrbooks */
5251 access = NoAccess;
5252 #else /* !NO_LOCAL_ADDRBOOKS) */
5253 build_path(fbuf, is_absolute_path(pab->filename) ? NULL
5254 : ps_global->home_dir,
5255 pab->filename, sizeof(fbuf));
5257 #if defined(DOS) || defined(OS2)
5259 * Microsoft networking causes some access calls to do a DNS query (!!)
5260 * when it is turned on. In particular, if there is a / in the filename
5261 * this seems to happen. So, just don't allow it.
5263 if(strindex(fbuf, '/') != NULL){
5264 dprint((2, "\"/\" not allowed in addrbook name\n"));
5265 return NoAccess;
5267 #else /* !DOS */
5268 /* also prevent backslash in non-DOS addrbook names */
5269 if(strindex(fbuf, '\\') != NULL){
5270 dprint((2, "\"\\\" not allowed in addrbook name\n"));
5271 return NoAccess;
5273 #endif /* !DOS */
5275 if(can_access(fbuf, ACCESS_EXISTS) == 0){
5276 if(can_access(fbuf, EDIT_ACCESS) == 0){
5277 char *dir, *p;
5279 dir = ".";
5280 if((p = last_cmpnt(fbuf)) != NULL){
5281 *--p = '\0';
5282 dir = *fbuf ? fbuf : "/";
5285 #if defined(DOS) || defined(OS2)
5287 * If the dir has become a drive letter and : (e.g. "c:")
5288 * then append a "\". The library function access() in the
5289 * win 16 version of MSC seems to require this.
5291 if(isalpha((unsigned char) *dir)
5292 && *(dir+1) == ':' && *(dir+2) == '\0'){
5293 *(dir+2) = '\\';
5294 *(dir+3) = '\0';
5296 #endif /* DOS || OS2 */
5299 * Even if we can edit the address book file itself, we aren't
5300 * going to be able to change it unless we can also write in
5301 * the directory that contains it (because we write into a
5302 * temp file and then rename).
5304 if(can_access(dir, EDIT_ACCESS) == 0)
5305 access = ReadWrite;
5306 else{
5307 access = ReadOnly;
5308 q_status_message1(SM_ORDER, 2, 2,
5309 "Address book directory (%.200s) is ReadOnly",
5310 dir);
5313 else if(can_access(fbuf, READ_ACCESS) == 0)
5314 access = ReadOnly;
5315 else
5316 access = NoAccess;
5318 #endif /* !NO_LOCAL_ADDRBOOKS) */
5321 return(access);
5326 * Trim back remote address books if necessary.
5328 void
5329 trim_remote_adrbks(void)
5331 register PerAddrBook *pab;
5332 int i;
5334 dprint((2, "- trim_remote_adrbks -\n"));
5336 if(!as.initialized)
5337 return;
5339 for(i = 0; i < as.n_addrbk; i++){
5340 pab = &as.adrbks[i];
5341 if(pab->ostatus != TotallyClosed && pab->address_book
5342 && pab->address_book->rd)
5343 rd_trim_remdata(&pab->address_book->rd);
5349 * Free and close everything.
5351 void
5352 completely_done_with_adrbks(void)
5354 register PerAddrBook *pab;
5355 int i;
5357 dprint((2, "- completely_done_with_adrbks -\n"));
5359 ab_nesting_level = 0;
5361 if(!as.initialized)
5362 return;
5364 for(i = 0; i < as.n_addrbk; i++)
5365 init_abook(&as.adrbks[i], TotallyClosed);
5367 for(i = 0; i < as.n_addrbk; i++){
5368 pab = &as.adrbks[i];
5370 if(pab->filename)
5371 fs_give((void **)&pab->filename);
5373 if(pab->abnick)
5374 fs_give((void **)&pab->abnick);
5377 done_with_dlc_cache();
5379 if(as.adrbks)
5380 fs_give((void **)&as.adrbks);
5382 as.n_addrbk = 0;
5383 as.initialized = 0;
5388 * Initialize or re-initialize this address book.
5390 * Args: pab -- the PerAddrBook ptr
5391 * want_status -- desired OpenStatus for this address book
5393 void
5394 init_abook(PerAddrBook *pab, OpenStatus want_status)
5396 register OpenStatus new_status;
5398 dprint((4, "- init_abook -\n"));
5399 dprint((7, " addrbook nickname = %s filename = %s",
5400 pab->abnick ? pab->abnick : "<null>",
5401 pab->filename ? pab->filename : "<null>"));
5402 dprint((7, " ostatus was %s, want %s\n",
5403 pab->ostatus==Open ? "Open" :
5404 pab->ostatus==HalfOpen ? "HalfOpen" :
5405 pab->ostatus==ThreeQuartOpen ? "ThreeQuartOpen" :
5406 pab->ostatus==NoDisplay ? "NoDisplay" :
5407 pab->ostatus==Closed ? "Closed" : "TotallyClosed",
5408 want_status==Open ? "Open" :
5409 want_status==HalfOpen ? "HalfOpen" :
5410 want_status==ThreeQuartOpen ? "ThreeQuartOpen" :
5411 want_status==NoDisplay ? "NoDisplay" :
5412 want_status==Closed ? "Closed" : "TotallyClosed"));
5414 new_status = want_status; /* optimistic default */
5416 if(want_status == TotallyClosed){
5417 if(pab->address_book != NULL){
5418 adrbk_close(pab->address_book);
5419 pab->address_book = NULL;
5422 if(pab->so != NULL){
5423 so_give(&pab->so);
5424 pab->so = NULL;
5428 * If we don't need it, release some addrbook memory by calling
5429 * adrbk_partial_close().
5431 else if((want_status == Closed || want_status == HalfOpen) &&
5432 pab->address_book != NULL){
5433 adrbk_partial_close(pab->address_book);
5435 /* If we want the addrbook read in and it hasn't been, do so */
5436 else if(want_status == Open || want_status == NoDisplay){
5437 if(pab->address_book == NULL){ /* abook handle is not currently active */
5438 if(pab->access != NoAccess){
5439 char warning[800]; /* place to put a warning */
5440 int sort_rule;
5442 warning[0] = '\0';
5443 if(pab->access == ReadOnly)
5444 sort_rule = AB_SORT_RULE_NONE;
5445 else
5446 sort_rule = ps_global->ab_sort_rule;
5448 pab->address_book = adrbk_open(pab,
5449 ps_global->home_dir, warning, sizeof(warning), sort_rule);
5451 if(pab->address_book == NULL){
5452 pab->access = NoAccess;
5453 if(want_status == Open){
5454 new_status = HalfOpen; /* best we can do */
5455 q_status_message1(SM_ORDER | SM_DING, *warning?1:3, 4,
5456 _("Error opening/creating address book %.200s"),
5457 pab->abnick);
5458 if(*warning)
5459 q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5460 as.n_addrbk > 1 ? pab->abnick : "addressbook",
5461 warning);
5463 else
5464 new_status = Closed;
5466 dprint((1, "Error opening address book %s: %s\n",
5467 pab->abnick ? pab->abnick : "?",
5468 error_description(errno)));
5470 else{
5471 if(pab->access == NoExists)
5472 pab->access = ReadWrite;
5474 if(pab->access == ReadWrite){
5476 * Add forced entries if there are any. These are
5477 * entries that are always supposed to show up in
5478 * personal address books. They're specified in the
5479 * global config file.
5481 add_forced_entries(pab->address_book);
5484 new_status = want_status;
5485 dprint((2, "Address book %s (%s) opened with %ld items\n",
5486 pab->abnick ? pab->abnick : "?",
5487 pab->filename ? pab->filename : "?",
5488 (long)adrbk_count(pab->address_book)));
5489 if(*warning){
5490 dprint((1,
5491 "Addressbook parse error in %s (%s): %s\n",
5492 pab->abnick ? pab->abnick : "?",
5493 pab->filename ? pab->filename : "?",
5494 warning));
5495 if(!pab->gave_parse_warnings && want_status == Open){
5496 pab->gave_parse_warnings++;
5497 q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5498 as.n_addrbk > 1 ? pab->abnick : "addressbook",
5499 warning);
5504 else{
5505 if(want_status == Open){
5506 new_status = HalfOpen; /* best we can do */
5507 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5508 "Insufficient permissions for opening address book %.200s",
5509 pab->abnick);
5511 else
5512 new_status = Closed;
5516 * File handle was already open but we were in Closed or HalfOpen
5517 * state. Check to see if someone else has changed the addrbook
5518 * since we first opened it.
5520 else if((pab->ostatus == Closed || pab->ostatus == HalfOpen) &&
5521 ps_global->remote_abook_validity > 0)
5522 (void)adrbk_check_and_fix(pab, 1, 0, 0);
5525 pab->ostatus = new_status;
5530 * Does a validity check on all the open address books.
5532 * Return -- number of address books with invalid data
5535 adrbk_check_all_validity_now(void)
5537 int i;
5538 int something_out_of_date = 0;
5539 PerAddrBook *pab;
5541 dprint((7, "- adrbk_check_all_validity_now -\n"));
5543 if(as.initialized){
5544 for(i = 0; i < as.n_addrbk; i++){
5545 pab = &as.adrbks[i];
5546 if(pab->address_book){
5547 adrbk_check_validity(pab->address_book, 1L);
5548 if(pab->address_book->flags & FILE_OUTOFDATE ||
5549 (pab->address_book->rd &&
5550 pab->address_book->rd->flags & REM_OUTOFDATE))
5551 something_out_of_date++;
5556 return(something_out_of_date);
5561 * Fix out-of-date address book. This only fixes the AdrBk part of the
5562 * problem, not the pab and display. It closes and reopens the address_book
5563 * file, clears the cache, reads new data.
5565 * Arg pab -- Pointer to the PerAddrBook data for this addrbook
5566 * safe -- It is safe to apply the fix
5567 * low_freq -- This is a low frequency check (longer between checks)
5569 * Returns non-zero if addrbook was fixed
5572 adrbk_check_and_fix(PerAddrBook *pab, int safe, int low_freq, int check_now)
5574 int ret = 0;
5575 long chk_interval;
5577 if(!as.initialized || !pab)
5578 return(ret);
5580 dprint((7, "- adrbk_check_and_fix(%s) -\n",
5581 pab->filename ? pab->filename : "?"));
5583 if(pab->address_book){
5584 if(check_now)
5585 chk_interval = 1L;
5586 else if(low_freq)
5587 chk_interval = -60L * MAX(LOW_FREQ_CHK_INTERVAL,
5588 ps_global->remote_abook_validity + 5);
5589 else
5590 chk_interval = 0L;
5592 adrbk_check_validity(pab->address_book, chk_interval);
5594 if(pab->address_book->flags & FILE_OUTOFDATE ||
5595 (pab->address_book->rd &&
5596 pab->address_book->rd->flags & REM_OUTOFDATE &&
5597 !(pab->address_book->rd->flags & USER_SAID_NO))){
5598 if(safe){
5599 OpenStatus save_status;
5600 int save_rem_abook_valid = 0;
5602 dprint((2, "adrbk_check_and_fix %s: fixing %s\n",
5603 debug_time(0,0,ps_global->signal_in_progress),
5604 pab->filename ? pab->filename : "?"));
5605 if(ab_nesting_level > 0){
5606 q_status_message3(SM_ORDER, 0, 2,
5607 "Resyncing address book%.200s%.200s%.200s",
5608 as.n_addrbk > 1 ? " \"" : "",
5609 as.n_addrbk > 1 ? pab->abnick : "",
5610 as.n_addrbk > 1 ? "\"" : "");
5611 display_message('x');
5612 fflush(stdout);
5615 ret++;
5616 save_status = pab->ostatus;
5618 /* don't do the trim right now */
5619 if(pab->address_book->rd)
5620 pab->address_book->rd->flags &= ~DO_REMTRIM;
5623 * Have to change this from -1 or we won't actually do
5624 * the resync.
5626 if((pab->address_book->rd &&
5627 pab->address_book->rd->flags & REM_OUTOFDATE) &&
5628 ps_global->remote_abook_validity == -1){
5629 save_rem_abook_valid = -1;
5630 ps_global->remote_abook_validity = 0;
5633 init_abook(pab, TotallyClosed);
5635 pab->so = NULL;
5636 /* this sets up pab->so, so is important */
5637 pab->access = adrbk_access(pab);
5640 * If we just re-init to HalfOpen... we won't actually
5641 * open the address book, which was open before. That
5642 * would be fine but it's a little nicer if we can open
5643 * it now so that we don't defer the resync until
5644 * the next open, which would be a user action for sure.
5645 * Right now we may be here during a newmail check
5646 * timeout, so this is a good time to do the resync.
5648 if(save_status == HalfOpen ||
5649 save_status == ThreeQuartOpen ||
5650 save_status == Closed)
5651 init_abook(pab, NoDisplay);
5653 init_abook(pab, save_status);
5655 if(save_rem_abook_valid)
5656 ps_global->remote_abook_validity = save_rem_abook_valid;
5658 if(ab_nesting_level > 0 && pab->ostatus == save_status)
5659 q_status_message3(SM_ORDER, 0, 2,
5660 "Resynced address book%.200s%.200s%.200s",
5661 as.n_addrbk > 1 ? " \"" : "",
5662 as.n_addrbk > 1 ? pab->abnick : "",
5663 as.n_addrbk > 1 ? "\"" : "");
5665 else
5666 dprint((2,
5667 "adrbk_check_and_fix: not safe to fix %s\n",
5668 pab->filename ? pab->filename : "?"));
5672 return(ret);
5676 static time_t last_check_and_fix_all;
5678 * Fix out of date address books. This only fixes the AdrBk part of the
5679 * problem, not the pab and display. It closes and reopens the address_book
5680 * files, clears the caches, reads new data.
5682 * Args safe -- It is safe to apply the fix
5683 * low_freq -- This is a low frequency check (longer between checks)
5685 * Returns non-zero if an addrbook was fixed
5688 adrbk_check_and_fix_all(int safe, int low_freq, int check_now)
5690 int i, ret = 0;
5691 PerAddrBook *pab;
5693 if(!as.initialized)
5694 return(ret);
5696 dprint((7, "- adrbk_check_and_fix_all -\n"));
5698 last_check_and_fix_all = get_adj_time();
5700 for(i = 0; i < as.n_addrbk; i++){
5701 pab = &as.adrbks[i];
5702 if(pab->address_book)
5703 ret += adrbk_check_and_fix(pab, safe, low_freq, check_now);
5706 return(ret);
5713 void
5714 adrbk_maintenance(void)
5716 static time_t last_time_here = 0;
5717 time_t now;
5718 int i;
5719 long low_freq_interval;
5721 dprint((9, "- adrbk_maintenance -\n"));
5723 if(!as.initialized)
5724 return;
5726 now = get_adj_time();
5728 if(now < last_time_here + 120)
5729 return;
5731 last_time_here = now;
5733 low_freq_interval = MAX(LOW_FREQ_CHK_INTERVAL,
5734 ps_global->remote_abook_validity + 5);
5736 /* check the time here to make it cheap */
5737 if(ab_nesting_level == 0 &&
5738 ps_global->remote_abook_validity > 0 &&
5739 now > last_check_and_fix_all + low_freq_interval * 60L)
5740 (void)adrbk_check_and_fix_all(1, 1, 0);
5742 /* close down idle connections */
5743 for(i = 0; i < as.n_addrbk; i++){
5744 PerAddrBook *pab;
5746 pab = &as.adrbks[i];
5748 if(pab->address_book &&
5749 pab->address_book->type == Imap &&
5750 pab->address_book->rd &&
5751 rd_stream_exists(pab->address_book->rd)){
5752 dprint((7,
5753 "adrbk_maint: %s: idle cntr %ld (%ld)\n",
5754 pab->address_book->orig_filename
5755 ? pab->address_book->orig_filename : "?",
5756 (long)(now - pab->address_book->rd->last_use),
5757 (long)pab->address_book->rd->last_use));
5759 if(now > pab->address_book->rd->last_use + IMAP_IDLE_TIMEOUT){
5760 dprint((2,
5761 "adrbk_maint %s: closing idle (%ld secs) connection: %s\n",
5762 debug_time(0,0,ps_global->signal_in_progress),
5763 (long)(now - pab->address_book->rd->last_use),
5764 pab->address_book->orig_filename
5765 ? pab->address_book->orig_filename : "?"));
5766 rd_close_remote(pab->address_book->rd);
5768 else{
5770 * If we aren't going to close it, we ping it instead to
5771 * make sure it stays open and doesn't timeout on us.
5772 * This shouldn't be necessary unless the server has a
5773 * really short timeout. If we got killed for some reason
5774 * we set imap.stream to NULL.
5775 * Instead of just pinging, we may as well check for
5776 * updates, too.
5778 if(ab_nesting_level == 0 &&
5779 ps_global->remote_abook_validity > 0){
5780 time_t save_last_use;
5783 * We shouldn't count this as a real last_use.
5785 save_last_use = pab->address_book->rd->last_use;
5786 (void)adrbk_check_and_fix(pab, 1, 0, 1);
5787 pab->address_book->rd->last_use = save_last_use;
5789 /* just ping it if not safe to fix it */
5790 else if(!rd_ping_stream(pab->address_book->rd)){
5791 dprint((2,
5792 "adrbk_maint: %s: abook stream closed unexpectedly: %s\n",
5793 debug_time(0,0,ps_global->signal_in_progress),
5794 pab->address_book->orig_filename
5795 ? pab->address_book->orig_filename : "?"));
5804 * Parses a string of comma-separated addresses or nicknames into an
5805 * array.
5807 * Returns an allocated, null-terminated list, or NULL.
5809 char **
5810 parse_addrlist(char *addrfield)
5812 #define LISTCHUNK 500 /* Alloc this many addresses for list at a time */
5813 char **al, **ad;
5814 char *next_addr, *cur_addr, *p, *q;
5815 int slots = LISTCHUNK;
5817 if(!addrfield)
5818 return((char **)NULL);
5820 /* allocate first chunk */
5821 slots = LISTCHUNK;
5822 al = (char **)fs_get(sizeof(char *) * (slots+1));
5823 ad = al;
5825 p = addrfield;
5827 /* skip any leading whitespace */
5828 for(q = p; *q && *q == SPACE; q++)
5829 ;/* do nothing */
5831 next_addr = (*q) ? q : NULL;
5833 /* Loop adding each address in list to array al */
5834 for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
5836 next_addr = skip_to_next_addr(cur_addr);
5838 q = cur_addr;
5839 SKIP_SPACE(q);
5841 /* allocate more space */
5842 if((ad-al) >= slots){
5843 slots += LISTCHUNK;
5844 fs_resize((void **)&al, sizeof(char *) * (slots+1));
5845 ad = al + slots - LISTCHUNK;
5848 if(*q)
5849 *ad++ = cpystr(q);
5852 *ad++ = NULL;
5854 /* free up any excess we've allocated */
5855 fs_resize((void **)&al, sizeof(char *) * (ad - al));
5856 return(al);
5861 * Args cur -- pointer to the start of the current addr in list.
5863 * Returns a pointer to the start of the next addr or NULL if there are
5864 * no more addrs.
5866 * Side effect: current addr has trailing white space removed
5867 * and is null terminated.
5869 char *
5870 skip_to_next_addr(char *cur)
5872 register char *p,
5874 char *ret_pointer;
5875 int in_quotes = 0,
5876 in_comment = 0;
5877 char prev_char = '\0';
5880 * Find delimiting comma or end.
5881 * Quoted commas and commented commas don't count.
5883 for(q = cur; *q; q++){
5884 switch(*q){
5885 case COMMA:
5886 if(!in_quotes && !in_comment)
5887 goto found_comma;
5888 break;
5890 case LPAREN:
5891 if(!in_quotes && !in_comment)
5892 in_comment = 1;
5893 break;
5895 case RPAREN:
5896 if(in_comment && prev_char != BSLASH)
5897 in_comment = 0;
5898 break;
5900 case QUOTE:
5901 if(in_quotes && prev_char != BSLASH)
5902 in_quotes = 0;
5903 else if(!in_quotes && !in_comment)
5904 in_quotes = 1;
5905 break;
5907 default:
5908 break;
5911 prev_char = *q;
5914 found_comma:
5915 if(*q){ /* trailing comma case */
5916 *q = '\0';
5917 ret_pointer = q + 1;
5919 else
5920 ret_pointer = NULL; /* no more addrs after cur */
5922 /* remove trailing white space from cur */
5923 for(p = q - 1; p >= cur && isspace((unsigned char)*p); p--)
5924 *p = '\0';
5926 return(ret_pointer);
5931 * Add entries specified by system administrator. If the nickname already
5932 * exists, it is not touched.
5934 void
5935 add_forced_entries(AdrBk *abook)
5937 AdrBk_Entry *abe;
5938 char *nickname, *fullname, *address;
5939 char *end_of_nick, *end_of_full, **t;
5942 if(!ps_global->VAR_FORCED_ABOOK_ENTRY ||
5943 !ps_global->VAR_FORCED_ABOOK_ENTRY[0] ||
5944 !ps_global->VAR_FORCED_ABOOK_ENTRY[0][0])
5945 return;
5947 for(t = ps_global->VAR_FORCED_ABOOK_ENTRY; t[0] && t[0][0]; t++){
5948 nickname = *t;
5951 * syntax for each element is
5952 * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
5955 /* find end of nickname */
5956 end_of_nick = nickname;
5957 while(*end_of_nick
5958 && !isspace((unsigned char)*end_of_nick)
5959 && *end_of_nick != '|')
5960 end_of_nick++;
5962 /* find the pipe character between nickname and fullname */
5963 fullname = end_of_nick;
5964 while(*fullname && *fullname != '|')
5965 fullname++;
5967 if(*fullname)
5968 fullname++;
5970 *end_of_nick = '\0';
5971 abe = adrbk_lookup_by_nick(abook, nickname, NULL);
5973 if(!abe){ /* If it isn't there, add it */
5975 /* skip whitespace before fullname */
5976 fullname = skip_white_space(fullname);
5978 /* find the pipe character between fullname and address */
5979 end_of_full = fullname;
5980 while(*end_of_full && *end_of_full != '|')
5981 end_of_full++;
5983 if(!*end_of_full){
5984 dprint((2,
5985 "missing | in forced-abook-entry \"%s\"\n",
5986 nickname ? nickname : "?"));
5987 continue;
5990 address = end_of_full + 1;
5992 /* skip whitespace before address */
5993 address = skip_white_space(address);
5995 if(*address == '('){
5996 dprint((2,
5997 "no lists allowed in forced-abook-entry \"%s\"\n",
5998 address ? address : "?"));
5999 continue;
6002 /* go back and remove trailing white space from fullname */
6003 while(*end_of_full == '|' || isspace((unsigned char)*end_of_full)){
6004 *end_of_full = '\0';
6005 end_of_full--;
6008 dprint((2,
6009 "Adding forced abook entry \"%s\"\n", nickname ? nickname : ""));
6011 (void)adrbk_add(abook,
6012 NO_NEXT,
6013 nickname,
6014 fullname,
6015 address,
6016 NULL,
6017 NULL,
6018 Single,
6019 (adrbk_cntr_t *)NULL,
6020 (int *)NULL,
6028 /* Go through the list of addressbooks and check if any
6029 * of them point to the given stream.
6032 any_addressbook_in_remote_stream(MAILSTREAM *stream)
6034 int rv = 0;
6035 int i = 0, num = 0;
6036 char *nickname = NULL;
6037 char *filename = NULL;
6038 char *q = NULL;
6041 if(ps_global->VAR_ADDRESSBOOK &&
6042 ps_global->VAR_ADDRESSBOOK[num] &&
6043 ps_global->VAR_ADDRESSBOOK[num][0]){
6044 q = ps_global->VAR_ADDRESSBOOK[num++];
6045 i = num;
6047 else if(ps_global->VAR_GLOB_ADDRBOOK &&
6048 ps_global->VAR_GLOB_ADDRBOOK[i-num] &&
6049 ps_global->VAR_GLOB_ADDRBOOK[i-num][0]){
6050 q = ps_global->VAR_GLOB_ADDRBOOK[i - num];
6051 i++;
6052 } else q = NULL;
6053 if(q != NULL){
6054 get_pair(q, &nickname, &filename, 0, 0);
6056 if(nickname) fs_give((void **)&nickname);
6058 if(filename){
6059 if(*filename == '{'
6060 && same_stream(filename, stream) != NULL)
6061 rv = 1;
6062 fs_give((void **)&filename);
6065 } while (rv == 0 && q != NULL);
6067 return rv;