* NTLM authentication support with the ntlm library, in Unix systems.
[alpine.git] / pith / adrbklib.c
blob69550f2e884224329f29d52ea29a9da01a340a95
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: adrbklib.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /* ========================================================================
6 * Copyright 2006-2009 University of Washington
7 * Copyright 2013-2017 Eduardo Chappa
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
18 #include "../pith/headers.h"
19 #include "../pith/adrbklib.h"
20 #include "../pith/abdlc.h"
21 #include "../pith/addrbook.h"
22 #include "../pith/addrstring.h"
23 #include "../pith/state.h"
24 #include "../pith/conf.h"
25 #include "../pith/status.h"
26 #include "../pith/remote.h"
27 #include "../pith/tempfile.h"
28 #include "../pith/bldaddr.h"
29 #include "../pith/signal.h"
30 #include "../pith/busy.h"
31 #include "../pith/util.h"
34 AddrScrState as;
36 void (*pith_opt_save_and_restore)(int, SAVE_STATE_S *);
40 * We don't want any end of line fixups to occur, so include "b" in DOS modes.
42 #if defined(DOS) || defined(OS2)
43 #define ADRBK_NAME "addrbook"
44 #else
45 #define ADRBK_NAME ".addressbook"
46 #endif
48 #define OPEN_WRITE_MODE (O_TRUNC|O_WRONLY|O_CREAT|O_BINARY)
51 #ifndef MAXPATH
52 #define MAXPATH 1000 /* Longest file path we can deal with */
53 #endif
55 #define TABWIDTH 8
56 #define INDENTSTR " "
57 #define INDENTXTRA " : "
58 #define INDENT 3 /* length of INDENTSTR */
61 #define SLOP 3
63 static int writing; /* so we can give understandable error message */
65 AdrBk *edited_abook;
67 static char empty[] = "";
69 jmp_buf jump_over_qsort;
72 /* internal prototypes */
73 int copy_abook_to_tempfile(AdrBk *, char *, size_t);
74 char *dir_containing(char *);
75 char *get_next_abook_entry(FILE *, int);
76 void strip_addr_string(char *, char **, char **);
77 void adrbk_check_local_validity(AdrBk *, long);
78 int write_single_abook_entry(AdrBk_Entry *, FILE *, int *, int *, int *, int *);
79 char *backcompat_encoding_for_abook(char *, size_t, char *, size_t, char *);
80 int percent_abook_saved(void);
81 void exp_del_nth(EXPANDED_S *, a_c_arg_t);
82 void exp_add_nth(EXPANDED_S *, a_c_arg_t);
83 int cmp_ae_by_full_lists_last(const qsort_t *,const qsort_t *);
84 int cmp_cntr_by_full_lists_last(const qsort_t *, const qsort_t *);
85 int cmp_ae_by_full(const qsort_t *, const qsort_t *);
86 int cmp_cntr_by_full(const qsort_t *, const qsort_t *);
87 int cmp_ae_by_nick_lists_last(const qsort_t *,const qsort_t *);
88 int cmp_cntr_by_nick_lists_last(const qsort_t *, const qsort_t *);
89 int cmp_ae_by_nick(const qsort_t *, const qsort_t *);
90 int cmp_cntr_by_nick(const qsort_t *, const qsort_t *);
91 int cmp_addr(const qsort_t *, const qsort_t *);
92 void sort_addr_list(char **);
93 int build_abook_datastruct(AdrBk *, char *, size_t);
94 AdrBk_Entry *init_ae(AdrBk *, AdrBk_Entry *, char *);
95 adrbk_cntr_t count_abook_entries_on_disk(AdrBk *, a_c_arg_t *);
96 AdrBk_Entry *adrbk_get_delae(AdrBk *, a_c_arg_t);
97 void free_ae_parts(AdrBk_Entry *);
98 void add_entry_to_trie(AdrBk_Trie **, char *, a_c_arg_t);
99 int build_abook_tries(AdrBk *, char *);
100 void repair_abook_tries(AdrBk *);
101 adrbk_cntr_t lookup_nickname_in_trie(AdrBk *, char *);
102 adrbk_cntr_t lookup_address_in_trie(AdrBk *, char *);
103 adrbk_cntr_t lookup_in_abook_trie(AdrBk_Trie *, char *);
104 void free_abook_trie(AdrBk_Trie **);
105 adrbk_cntr_t re_sort_particular_entry(AdrBk *, a_c_arg_t);
106 void move_ab_entry(AdrBk *, a_c_arg_t, a_c_arg_t);
107 void insert_ab_entry(AdrBk *, a_c_arg_t, AdrBk_Entry *, int);
108 void delete_ab_entry(AdrBk *, a_c_arg_t, int);
109 void defvalue_ae(AdrBk_Entry *);
113 * Open, read, and parse an address book.
115 * Args: pab -- the PerAddrBook structure
116 * homedir -- the user's home directory if specified
117 * warning -- put "why failed" message to user here
118 * (provide space of at least 201 chars)
120 * If filename is NULL, the default will be used in the homedir
121 * passed in. If homedir is NULL, the current dir will be used.
122 * If filename is not NULL and is an absolute path, just the filename
123 * will be used. Otherwise, it will be used relative to the homedir, or
124 * to the current dir depending on whether or not homedir is NULL.
126 * Expected addressbook file format is:
127 * <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
129 * The last two fields (\t<fcc>\t<comment>) are optional.
131 * Lines that start with SPACE are continuation lines. Ends of lines are
132 * treated as if they were spaces. The address field is either a single
133 * address or a list of comma-separated addresses inside parentheses.
135 * Fields missing from the end of an entry are considered blank.
137 * Commas in the address field will cause problems, as will tabs in any
138 * field.
140 * There may be some deleted entries in the addressbook that don't get
141 * used. They look like regular entries except their nicknames start
142 * with the string "#DELETED-YY/MM/DD#".
144 AdrBk *
145 adrbk_open(PerAddrBook *pab, char *homedir, char *warning, size_t warninglen, int sort_rule)
147 char path[MAXPATH], *filename;
148 AdrBk *ab;
149 int abook_validity_was_minusone = 0;
152 filename = pab ? pab->filename : NULL;
154 dprint((2, "- adrbk_open(%s) -\n", filename ? filename : ""));
156 ab = (AdrBk *)fs_get(sizeof(AdrBk));
157 memset(ab, 0, sizeof(*ab));
159 ab->orig_filename = filename ? cpystr(filename) : NULL;
161 if(pab->type & REMOTE_VIA_IMAP){
162 int try_cache;
164 ab->type = Imap;
166 if(!ab->orig_filename || *(ab->orig_filename) != '{'){
167 dprint((1, "adrbk_open: remote: filename=%s\n",
168 ab->orig_filename ? ab->orig_filename : "NULL"));
169 goto bail_out;
172 if(!(ab->rd = rd_new_remdata(RemImap, ab->orig_filename, REMOTE_ABOOK_SUBTYPE))){
173 dprint((1,
174 "adrbk_open: remote: new_remdata failed: %s\n",
175 ab->orig_filename ? ab->orig_filename : "NULL"));
176 goto bail_out;
179 /* Transfer responsibility for the storage object */
180 ab->rd->so = pab->so;
181 pab->so = NULL;
183 try_cache = rd_read_metadata(ab->rd);
185 if(ab->rd->lf)
186 ab->filename = cpystr(ab->rd->lf);
188 /* Transfer responsibility for removal of temp file */
189 if(ab->rd->flags & DEL_FILE){
190 ab->flags |= DEL_FILE;
191 ab->rd->flags &= ~DEL_FILE;
194 if(pab->access == MaybeRorW){
195 if(ab->rd->read_status == 'R')
196 pab->access = ab->rd->access = ReadOnly;
197 else
198 pab->access = ab->rd->access = ReadWrite;
200 else if(pab->access == ReadOnly){
202 * Pass on readonly-ness from being a global addrbook.
203 * This should cause us to open the remote folder readonly,
204 * avoiding error messages about readonly-ness.
206 ab->rd->access = ReadOnly;
210 * The plan is to fetch addrbook data and copy into local file.
211 * Then we open the local copy for reading. We use the IMAP STATUS
212 * command to tell us if we need to update from the remote addrbook.
214 * If access is NoExists, that probably means we had trouble
215 * opening the remote folder in the adrbk_access routine.
216 * In that case we'll use a cached copy if we have one.
218 if(pab->access != NoExists){
220 bootstrap_nocheck_policy:
221 if(try_cache && ps_global->remote_abook_validity == -1 &&
222 !abook_validity_was_minusone)
223 abook_validity_was_minusone++;
224 else{
225 rd_check_remvalid(ab->rd, 1L);
226 abook_validity_was_minusone = 0;
230 * If the cached info on this addrbook says it is readonly but
231 * it looks like it's been fixed now, change it to readwrite.
233 if(!(pab->type & GLOBAL) && ab->rd->read_status == 'R'){
235 * We go to this trouble since readonly addrbooks
236 * are likely a mistake. They are usually supposed to be
237 * readwrite. So we open it and check if it's been fixed.
239 rd_check_readonly_access(ab->rd);
240 if(ab->rd->read_status == 'W'){
241 pab->access = ab->rd->access = ReadWrite;
242 ab->rd->flags |= REM_OUTOFDATE;
244 else
245 pab->access = ab->rd->access = ReadOnly;
248 if(ab->rd->flags & REM_OUTOFDATE){
249 if(rd_update_local(ab->rd) != 0){
250 dprint((1,
251 "adrbk_open: remote: rd_update_local failed\n"));
253 * Don't give up altogether. We still may be
254 * able to use a cached copy.
257 else{
258 dprint((7,
259 "%s: copied remote to local (%ld)\n",
260 ab->rd->rn ? ab->rd->rn : "?",
261 (long)ab->rd->last_use));
265 if(pab->access == ReadWrite)
266 ab->rd->flags |= DO_REMTRIM;
269 /* If we couldn't get to remote folder, try using the cached copy */
270 if(pab->access == NoExists || ab->rd->flags & REM_OUTOFDATE){
271 if(try_cache){
272 pab->access = ab->rd->access = ReadOnly;
273 ab->rd->flags |= USE_OLD_CACHE;
274 q_status_message(SM_ORDER, 3, 4,
275 _("Can't contact remote address book server, using cached copy"));
276 dprint((2,
277 "Can't open remote addrbook %s, using local cached copy %s readonly\n",
278 ab->rd->rn ? ab->rd->rn : "?",
279 ab->rd->lf ? ab->rd->lf : "?"));
281 else
282 goto bail_out;
285 else{
286 ab->type = Local;
288 /*------------ figure out and save name of file to open ---------*/
289 if(filename == NULL){
290 if(homedir != NULL){
291 build_path(path, homedir, ADRBK_NAME, sizeof(path));
292 ab->filename = cpystr(path);
294 else
295 ab->filename = cpystr(ADRBK_NAME);
297 else{
298 if(is_absolute_path(filename)){
299 ab->filename = cpystr(filename);
301 else{
302 if(homedir != NULL){
303 build_path(path, homedir, filename, sizeof(path));
304 ab->filename = cpystr(path);
306 else
307 ab->filename = cpystr(filename);
312 if(ab->filename && ab->filename[0]){
313 char buf[MAXPATH];
315 strncpy(buf, ab->filename, sizeof(buf)-4);
316 buf[sizeof(buf)-4] = '\0';
319 * Our_filecopy is used in _WINDOWS to allow
320 * multiple pines to update the address book. The problem is
321 * that if a file is open it can't be deleted, so we need to keep
322 * the main filename closed most of the time.
323 * In Unix, our_filecopy just points to filename.
326 #ifdef _WINDOWS
328 * If we can't write in the same directory as filename is in, put
329 * the copies in /tmp instead.
331 if(!(ab->our_filecopy = tempfile_in_same_dir(ab->filename, "a3", NULL)))
332 ab->our_filecopy = temp_nam(NULL, "a3");
333 #endif /* _WINDOWS */
336 * We don't need the copies on Unix because we can rename/delete
337 * open files. Turn the feature off by making the copies point to
338 * the originals.
340 if(!ab->our_filecopy)
341 ab->our_filecopy = ab->filename;
343 else{
344 dprint((1, "adrbk_open: ab->filename is NULL???\n"));
345 goto bail_out;
350 * We're going to make our own copy of the address book file so that
351 * we won't conflict with other instances of pine trying to change it.
352 * In particular, on Windows the address book file cannot be deleted
353 * or renamed into if it is open in another process.
355 ab->flags |= FILE_OUTOFDATE;
356 if(copy_abook_to_tempfile(ab, warning, warninglen) < 0){
357 dprint((1, "adrbk_open: copy_file failed\n"));
358 if(abook_validity_was_minusone){
360 * The file copy failed when it shouldn't have. If the user has
361 * remote_abook_validity == -1 then we'll go back and try to
362 * do the validity check in case that can get us the file we
363 * need to copy. Without the validity check first time we won't
364 * contact the imap server.
366 dprint((1, "adrbk_open: trying to bootstrap\n"));
368 if(ab->our_filecopy){
369 if(ab->our_filecopy != ab->filename){
370 our_unlink(ab->our_filecopy);
371 fs_give((void **)&ab->our_filecopy);
374 ab->our_filecopy = NULL;
377 goto bootstrap_nocheck_policy;
380 goto bail_out;
383 if(!ab->fp)
384 goto bail_out;
386 ab->sort_rule = sort_rule;
387 if(pab->access == ReadOnly)
388 ab->sort_rule = AB_SORT_RULE_NONE;
390 if(ab){
391 /* allocate header for expanded lists list */
392 ab->exp = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
393 /* first real element is NULL */
394 ab->exp->next = (EXPANDED_S *)NULL;
396 /* allocate header for checked entries list */
397 ab->checks = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
398 /* first real element is NULL */
399 ab->checks->next = (EXPANDED_S *)NULL;
401 /* allocate header for selected entries list */
402 ab->selects = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
403 /* first real element is NULL */
404 ab->selects->next = (EXPANDED_S *)NULL;
406 return(ab);
409 bail_out:
410 dprint((2, "adrbk_open: bailing: filenames=%s %s %s fp=%s\n",
411 ab->orig_filename ? ab->orig_filename : "NULL",
412 ab->filename ? ab->filename : "NULL",
413 ab->our_filecopy ? ab->our_filecopy : "NULL",
414 ab->fp ? "open" : "NULL"));
416 if(ab->rd){
417 ab->rd->flags &= ~DO_REMTRIM;
418 rd_close_remdata(&ab->rd);
421 if(ab->fp)
422 (void)fclose(ab->fp);
424 if(ab->orig_filename)
425 fs_give((void **) &ab->orig_filename);
427 if(ab->our_filecopy && ab->our_filecopy != ab->filename){
428 our_unlink(ab->our_filecopy);
429 fs_give((void **) &ab->our_filecopy);
432 if(ab->filename)
433 fs_give((void **) &ab->filename);
435 if(pab->so){
436 so_give(&(pab->so));
437 pab->so = NULL;
440 fs_give((void **) &ab);
442 return NULL;
447 * Copy the address book file to the temporary session copy. Also copy
448 * the hashfile. Any of these files which don't exist will be created.
450 * Returns 0 success
451 * -1 failure
454 copy_abook_to_tempfile(AdrBk *ab, char *warning, size_t warninglen)
456 int got_it, fd, c,
457 ret = -1,
458 we_cancel = 0;
459 FILE *fp_read = (FILE *)NULL,
460 *fp_write = (FILE *)NULL;
461 char *lc;
462 time_t mtime;
465 dprint((3, "copy_file(%s) -\n",
466 (ab && ab->filename) ? ab->filename : ""));
468 if(!ab || !ab->filename || !ab->filename[0])
469 goto get_out;
471 if(!(ab->flags & FILE_OUTOFDATE))
472 return(0);
474 /* open filename for reading */
475 fp_read = our_fopen(ab->filename, "rb");
476 if(fp_read == NULL){
479 * filename probably doesn't exist so we try to create it
482 /* don't want to create in these cases, should already be there */
483 if(ab->type == Imap){
484 if(warning){
485 if(ab->type == Imap){
486 snprintf(warning, warninglen,
487 /* TRANSLATORS: A temporary file for the address book can't
488 be opened. */
489 _("Temp addrbook file can't be opened: %s"),
490 ab->filename);
491 warning[warninglen-1] = '\0';
493 else{
494 strncpy(warning, _("Address book doesn't exist"), warninglen);
495 warning[warninglen-1] = '\0';
499 goto get_out;
502 q_status_message1(SM_INFO, 0, 3,
503 _("Address book %.200s doesn't exist, creating"),
504 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
505 dprint((2, "Address book %s doesn't exist, creating\n",
506 ab->filename ? ab->filename : "?"));
509 * Just use user's umask for permissions. Mode is "w" so the create
510 * will happen. We close it right after creating and open it in
511 * read mode again later.
513 fp_read = our_fopen(ab->filename, "wb"); /* create */
514 if(fp_read == NULL ||
515 fclose(fp_read) == EOF ||
516 (fp_read = our_fopen(ab->filename, "rb")) == NULL){
517 /*--- Create failed, bail out ---*/
518 if(warning){
519 strncpy(warning, error_description(errno), warninglen);
520 warning[warninglen-1] = '\0';
523 dprint((2, "create failed: %s\n",
524 error_description(errno)));
526 goto get_out;
530 /* record new change date of addrbook file */
531 ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
533 ab->last_local_valid_chk = get_adj_time();
536 /* now there is an ab->filename and we have it open for reading */
538 got_it = 0;
540 /* copy ab->filename to ab->our_filecopy, preserving mtime */
541 if(ab->filename != ab->our_filecopy){
542 struct stat sbuf;
543 struct utimbuf times;
544 int valid_stat = 0;
546 dprint((7, "Before abook copies\n"));
547 if((fd = our_open(ab->our_filecopy, OPEN_WRITE_MODE, 0600)) < 0)
548 goto get_out;
550 fp_write = fdopen(fd, "wb");
551 rewind(fp_read);
552 if(fstat(fileno(fp_read), &sbuf)){
553 q_status_message1(SM_INFO, 0, 3,
554 "Error: can't stat addrbook \"%.200s\"",
555 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
556 dprint((2, "Error: can't stat addrbook \"%s\"\n",
557 ab->filename ? ab->filename : "?"));
559 else{
560 valid_stat++;
561 times.actime = sbuf.st_atime;
562 times.modtime = sbuf.st_mtime;
565 while((c = getc(fp_read)) != EOF)
566 if(putc(c, fp_write) == EOF)
567 goto get_out;
569 (void)fclose(fp_write);
570 fp_write = (FILE *)NULL;
571 if(valid_stat && our_utime(ab->our_filecopy, &times)){
572 q_status_message1(SM_INFO, 0, 3,
573 "Error: can't set mtime for \"%.200s\"",
574 (lc=last_cmpnt(ab->filename)) ? lc : ab->our_filecopy);
575 dprint((2, "Error: can't set mtime for \"%s\"\n",
576 ab->our_filecopy ? ab->our_filecopy : "?"));
579 (void)fclose(fp_read);
580 fp_read = (FILE *)NULL;
581 if(!(ab->fp = our_fopen(ab->our_filecopy, "rb")))
582 goto get_out;
584 dprint((7, "After abook file copy\n"));
586 else{ /* already open to the right file */
587 ab->fp = fp_read;
588 fp_read = (FILE *)NULL;
592 * Now we've copied filename to our_filecopy.
593 * Operate on the copy now. Ab->fp is open readonly on
594 * our_filecopy.
597 got_it = 0;
598 mtime = get_adj_name_file_mtime(ab->our_filecopy);
599 we_cancel = busy_cue(NULL, NULL, 1);
600 if(build_abook_datastruct(ab, (warning && !*warning) ? warning : NULL, warninglen)){
601 if(we_cancel)
602 cancel_busy_cue(-1);
604 dprint((2, "failed in build_abook_datastruct\n"));
605 goto get_out;
608 if(ab->arr
609 && build_abook_tries(ab, (warning && !*warning) ? warning : NULL)){
610 if(we_cancel)
611 cancel_busy_cue(-1);
613 dprint((2, "failed in build_abook_tries\n"));
614 goto get_out;
617 if(we_cancel)
618 cancel_busy_cue(-1);
620 ab->flags &= ~FILE_OUTOFDATE; /* turn off out of date flag */
621 ret = 0;
623 get_out:
624 if(fp_read)
625 (void)fclose(fp_read);
627 if(fp_write)
628 (void)fclose(fp_write);
630 if(ret < 0 && ab){
631 if(ab->our_filecopy && ab->our_filecopy != ab->filename)
632 our_unlink(ab->our_filecopy);
635 return(ret);
640 * Returns an allocated copy of the directory which contains filename.
642 char *
643 dir_containing(char *filename)
645 char dir[MAXPATH+1];
646 char *dirp = NULL;
648 if(filename){
649 char *lc;
651 if((lc = last_cmpnt(filename)) != NULL){
652 int to_copy;
654 to_copy = (lc - filename > 1) ? (lc - filename - 1) : 1;
655 strncpy(dir, filename, MIN(to_copy, sizeof(dir)-1));
656 dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
658 else{
659 dir[0] = '.';
660 dir[1] = '\0';
663 dirp = cpystr(dir);
666 return(dirp);
671 * Checks whether or not the addrbook is sorted correctly according to
672 * the SortType. Returns 1 if is sorted correctly, 0 otherwise.
675 adrbk_is_in_sort_order(AdrBk *ab, int be_quiet)
677 adrbk_cntr_t entry;
678 AdrBk_Entry *ae, *ae_prev;
679 int (*cmp_func)();
680 int we_cancel = 0;
682 dprint((9, "- adrbk_is_in_sort_order -\n"));
684 if(!ab)
685 return 0;
687 if(ab->sort_rule == AB_SORT_RULE_NONE || ab->count < 2)
688 return 1;
690 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
691 cmp_ae_by_full_lists_last :
692 (ab->sort_rule == AB_SORT_RULE_FULL) ?
693 cmp_ae_by_full :
694 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
695 cmp_ae_by_nick_lists_last :
696 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
697 cmp_ae_by_nick;
699 ae_prev = adrbk_get_ae(ab, (a_c_arg_t) 0);
701 if(!be_quiet)
702 we_cancel = busy_cue(NULL, NULL, 1);
704 for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t) entry);
705 ae != (AdrBk_Entry *)NULL;
706 ae = adrbk_get_ae(ab, (a_c_arg_t) (++entry))){
708 if((*cmp_func)((qsort_t *)&ae_prev, (qsort_t *)&ae) > 0){
709 if(we_cancel)
710 cancel_busy_cue(-1);
712 dprint((9, "- adrbk_is_in_sort_order : no (entry %ld) -\n", (long) entry));
713 return 0;
716 ae_prev = ae;
719 if(we_cancel)
720 cancel_busy_cue(-1);
722 dprint((9, "- adrbk_is_in_sort_order : yes -\n"));
724 return 1;
729 * Look through the ondisk address book and count the number of entries.
731 * Args ab -- address book pointer
732 * deleted -- pointer to location to return number of deleted entries
733 * warning -- place to put warning
735 * Returns number of non-deleted entries.
737 adrbk_cntr_t
738 count_abook_entries_on_disk(AdrBk *ab, a_c_arg_t *deleted)
740 FILE *fp_in;
741 char *nickname;
742 adrbk_cntr_t count = 0;
743 adrbk_cntr_t deleted_count = 0;
744 int rew = 1;
745 char nickbuf[50];
747 if(!ab || !ab->fp)
748 return -1;
750 fp_in = ab->fp;
752 while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
753 rew = 0;
754 strncpy(nickbuf, nickname, sizeof(nickbuf));
755 nickbuf[sizeof(nickbuf)-1] = '\0';
756 fs_give((void **) &nickname);
757 if(strncmp(nickbuf, DELETED, DELETED_LEN) == 0
758 && isdigit((unsigned char)nickbuf[DELETED_LEN])
759 && isdigit((unsigned char)nickbuf[DELETED_LEN+1])
760 && nickbuf[DELETED_LEN+2] == '/'
761 && isdigit((unsigned char)nickbuf[DELETED_LEN+3])
762 && isdigit((unsigned char)nickbuf[DELETED_LEN+4])
763 && nickbuf[DELETED_LEN+5] == '/'
764 && isdigit((unsigned char)nickbuf[DELETED_LEN+6])
765 && isdigit((unsigned char)nickbuf[DELETED_LEN+7])
766 && nickbuf[DELETED_LEN+8] == '#'){
767 deleted_count++;
768 continue;
771 count++;
774 if(deleted)
775 *deleted = (a_c_arg_t) deleted_count;
777 return(count);
782 * Builds the data structures used with the address book which is
783 * simply an array of entries.
786 build_abook_datastruct(AdrBk *ab, char *warning, size_t warninglen)
788 FILE *fp_in;
789 char *nickname;
790 char *lc;
791 a_c_arg_t count, deleted;
792 adrbk_cntr_t used = 0, delused = 0;
793 int max_nick = 0,
794 max_full = 0, full_two = 0, full_three = 0,
795 max_fcc = 0, fcc_two = 0, fcc_three = 0,
796 max_addr = 0, addr_two = 0, addr_three = 0,
797 this_nick_width, this_full_width, this_addr_width,
798 this_fcc_width;
799 WIDTH_INFO_S *widths;
800 int rew = 1, is_deleted;
801 AdrBk_Entry *ae;
803 dprint((9, "- build_abook_datastruct -\n"));
805 if(!ab || !ab->fp)
806 return -1;
808 errno = 0;
810 fp_in = ab->fp;
813 * If we used a list instead of an array to store the entries we
814 * could avoid this pass through the file to count the entries, which
815 * we use to allocate the array.
817 * Since we use entry_nums a lot to access address book entries it is
818 * convenient to have an array for quick access, so we'll probably
819 * leave this for now.
821 count = count_abook_entries_on_disk(ab, &deleted);
823 if(count < 0)
824 return -1;
826 ab->count = (adrbk_cntr_t) count;
827 ab->del_count = (adrbk_cntr_t) deleted;
829 if(count > 0){
830 ab->arr = (AdrBk_Entry *) fs_get(count * sizeof(AdrBk_Entry));
831 memset(ab->arr, 0, count * sizeof(AdrBk_Entry));
834 if(deleted > 0){
835 ab->del = (AdrBk_Entry *) fs_get(deleted * sizeof(AdrBk_Entry));
836 memset(ab->del, 0, deleted * sizeof(AdrBk_Entry));
839 while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
842 ae = NULL;
843 rew = 0;
844 is_deleted = 0;
846 if(strncmp(nickname, DELETED, DELETED_LEN) == 0
847 && isdigit((unsigned char)nickname[DELETED_LEN])
848 && isdigit((unsigned char)nickname[DELETED_LEN+1])
849 && nickname[DELETED_LEN+2] == '/'
850 && isdigit((unsigned char)nickname[DELETED_LEN+3])
851 && isdigit((unsigned char)nickname[DELETED_LEN+4])
852 && nickname[DELETED_LEN+5] == '/'
853 && isdigit((unsigned char)nickname[DELETED_LEN+6])
854 && isdigit((unsigned char)nickname[DELETED_LEN+7])
855 && nickname[DELETED_LEN+8] == '#'){
856 is_deleted++;
859 ALARM_BLIP();
860 if(!is_deleted && (long) used > MAX_ADRBK_SIZE){
861 q_status_message2(SM_ORDER | SM_DING, 4, 5,
862 "Max addrbook size is %.200s, %.200s too large, giving up",
863 long2string(MAX_ADRBK_SIZE),
864 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
865 dprint((1, "build_ondisk: used=%ld > %s\n",
866 (long) used, long2string(MAX_ADRBK_SIZE)));
867 goto io_err;
870 if(is_deleted){
871 if(delused < ab->del_count)
872 ae = &ab->del[delused++];
874 else{
875 if(used < ab->count)
876 ae = &ab->arr[used++];
879 if(ae)
880 init_ae(ab, ae, nickname);
882 fs_give((void **) &nickname);
884 if(!ae || is_deleted)
885 continue;
888 * We're calculating the widths as we read in the data.
889 * We could just go with some default widths to save time.
891 this_nick_width = 0;
892 this_full_width = 0;
893 this_addr_width = 0;
894 this_fcc_width = 0;
896 if(ae->nickname)
897 this_nick_width = (int) utf8_width(ae->nickname);
899 if(ae->fullname)
900 this_full_width = (int) utf8_width(ae->fullname);
902 if(ae->tag == Single){
903 if(ae->addr.addr)
904 this_addr_width = (int) utf8_width(ae->addr.addr);
906 else{
907 char **a2;
909 this_addr_width = 0;
910 for(a2 = ae->addr.list; *a2 != NULL; a2++)
911 this_addr_width = MAX(this_addr_width, (int) utf8_width(*a2));
914 if(ae->fcc)
915 this_fcc_width = (int) utf8_width(ae->fcc);
917 max_nick = MAX(max_nick, this_nick_width);
919 if(this_full_width > max_full){
920 full_three = full_two;
921 full_two = max_full;
922 max_full = this_full_width;
924 else if(this_full_width > full_two){
925 full_three = full_two;
926 full_two = this_full_width;
928 else if(this_full_width > full_three){
929 full_three = this_full_width;
932 if(this_addr_width > max_addr){
933 addr_three = addr_two;
934 addr_two = max_addr;
935 max_addr = this_addr_width;
937 else if(this_addr_width > addr_two){
938 addr_three = addr_two;
939 addr_two = this_addr_width;
941 else if(this_addr_width > addr_three){
942 addr_three = this_addr_width;
945 if(this_fcc_width > max_fcc){
946 fcc_three = fcc_two;
947 fcc_two = max_fcc;
948 max_fcc = this_fcc_width;
950 else if(this_fcc_width > fcc_two){
951 fcc_three = fcc_two;
952 fcc_two = this_fcc_width;
954 else if(this_fcc_width > fcc_three){
955 fcc_three = this_fcc_width;
959 widths = &ab->widths;
960 widths->max_nickname_width = MIN(max_nick, 99);
961 widths->max_fullname_width = MIN(max_full, 99);
962 widths->max_addrfield_width = MIN(max_addr, 99);
963 widths->max_fccfield_width = MIN(max_fcc, 99);
964 widths->third_biggest_fullname_width = MIN(full_three, 99);
965 widths->third_biggest_addrfield_width = MIN(addr_three, 99);
966 widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
968 dprint((9, "- build_abook_datastruct done -\n"));
969 return 0;
971 io_err:
972 if(warning && errno != 0){
973 strncpy(warning, error_description(errno), warninglen);
974 warning[warninglen-1] = '\0';
977 dprint((1, "build_ondisk: io_err: %s\n",
978 error_description(errno)));
980 return -1;
985 * Builds the trees used for nickname and address lookups.
988 build_abook_tries(AdrBk *ab, char *warning)
990 adrbk_cntr_t entry_num;
991 AdrBk_Entry *ae;
992 int we_cancel = 0;
994 if(!ab)
995 return -1;
997 dprint((9, "- build_abook_tries(%s) -\n", ab->filename));
1000 if(ab->nick_trie)
1001 free_abook_trie(&ab->nick_trie);
1003 if(ab->addr_trie)
1004 free_abook_trie(&ab->addr_trie);
1006 if(ab->full_trie)
1007 free_abook_trie(&ab->full_trie);
1009 if(ab->revfull_trie)
1010 free_abook_trie(&ab->revfull_trie);
1013 * Go through addrbook entries and add each to the tries it
1014 * belongs in.
1016 for(entry_num = 0; entry_num < ab->count; entry_num++){
1017 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1018 if(ae){
1019 /* every nickname in the nick trie */
1020 if(ae->nickname && ae->nickname[0])
1021 add_entry_to_trie(&ab->nick_trie, ae->nickname, (a_c_arg_t) entry_num);
1023 if(ae->fullname && ae->fullname[0]){
1024 char *reverse = NULL;
1025 char *forward = NULL;
1026 char *comma = NULL;
1029 * We have some fullnames stored as Last, First. Put both in.
1031 if(ae->fullname[0] != '"'
1032 && (comma=strindex(ae->fullname, ',')) != NULL
1033 && comma - ae->fullname > 0){
1034 forward = adrbk_formatname(ae->fullname, NULL, NULL);
1035 if(forward && forward[0]){
1036 *comma = '\0';
1037 reverse = cpystr(ae->fullname);
1038 *comma = ',';
1040 else{
1041 if(forward)
1042 fs_give((void **) &forward);
1044 forward = ae->fullname;
1047 else
1048 forward = ae->fullname;
1050 if(forward){
1051 char *addthis;
1054 * Make this add not only the full name as is (forward) but
1055 * also add the middle name and last name (names after spaces).
1056 * so that they'll be found.
1058 for(addthis=forward;
1059 addthis && (*addthis);
1060 addthis = strindex(addthis, ' ')){
1061 while(*addthis == ' ')
1062 addthis++;
1064 if(*addthis)
1065 add_entry_to_trie(&ab->full_trie, addthis, (a_c_arg_t) entry_num);
1069 if(reverse)
1070 add_entry_to_trie(&ab->revfull_trie, reverse, (a_c_arg_t) entry_num);
1072 if(forward && forward != ae->fullname)
1073 fs_give((void **) &forward);
1076 if(ae->tag == Single && ae->addr.addr && ae->addr.addr[0]){
1077 char buf[1000];
1078 char *tmp_a_string, *simple_addr = NULL;
1079 ADDRESS *addr = NULL;
1080 char *fakedomain = "@";
1083 * Isolate the actual address out of ae.
1085 tmp_a_string = cpystr(ae->addr.addr);
1086 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1087 if(tmp_a_string)
1088 fs_give((void **) &tmp_a_string);
1090 if(addr){
1091 if(addr->mailbox && addr->host
1092 && !(addr->host[0] == '@' && addr->host[1] == '\0'))
1093 simple_addr = simple_addr_string(addr, buf, sizeof(buf));
1096 * If the fullname wasn't set in the addrbook entry there
1097 * may still be one that is part of the address. We need
1098 * to be careful because we may be in the middle of opening
1099 * the address book right now. Don't call something like
1100 * our_build_address because it will probably re-open this
1101 * same addrbook infinitely.
1103 if(!(ae->fullname && ae->fullname[0])
1104 && addr->personal && addr->personal[0])
1105 add_entry_to_trie(&ab->full_trie, addr->personal,
1106 (a_c_arg_t) entry_num);
1108 mail_free_address(&addr);
1111 if(simple_addr)
1112 add_entry_to_trie(&ab->addr_trie, simple_addr, (a_c_arg_t) entry_num);
1117 dprint((9, "- build_abook_tries done -\n"));
1119 if(we_cancel)
1120 cancel_busy_cue(-1);
1122 return 0;
1126 void
1127 add_entry_to_trie(AdrBk_Trie **head, char *str, a_c_arg_t entry_num)
1129 AdrBk_Trie *temp, *trail;
1130 char *addthis, *p, buf[1000];
1132 if(!head || !str || !*str)
1133 return;
1135 /* add as lower case */
1137 for(p = str; *p && ((*p & 0x80) || !isupper((unsigned char) *p)); p++)
1140 if(*p){
1141 strncpy(buf, str, sizeof(buf));
1142 buf[sizeof(buf)-1] = '\0';
1143 for(p = buf; *p; p++)
1144 if(!(*p & 0x80) && isupper((unsigned char) *p))
1145 *p = tolower(*p);
1147 addthis = buf;
1149 else
1150 addthis = str;
1152 temp = trail = (*head);
1155 * Find way down the trie, adding missing nodes as we go.
1157 for(p = addthis; *p;){
1158 if(temp == NULL){
1159 temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1160 memset(temp, 0, sizeof(*temp));
1161 temp->value = *p;
1162 temp->entrynum = NO_NEXT;
1164 if(*head == NULL)
1165 *head = temp;
1166 else
1167 trail->down = temp;
1169 else{
1170 while((temp != NULL) && (temp->value != *p)){
1171 trail = temp;
1172 temp = temp->right;
1175 /* wasn't there, add new node */
1176 if(temp == NULL){
1177 temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
1178 memset(temp, 0, sizeof(*temp));
1179 temp->value = *p;
1180 temp->entrynum = NO_NEXT;
1181 trail->right = temp;
1185 if(*(++p)){
1186 trail = temp;
1187 temp = temp->down;
1192 * If entrynum is already filled in there must be an entry with
1193 * the same nickname earlier in the abook. Use that earlier entry.
1195 if(temp != NULL && temp->entrynum == NO_NEXT)
1196 temp->entrynum = (adrbk_cntr_t) entry_num;
1201 * Returns entry_num of first entry with this nickname, else NO_NEXT.
1203 adrbk_cntr_t
1204 lookup_nickname_in_trie(AdrBk *ab, char *nickname)
1206 if(!ab || !nickname || !ab->nick_trie)
1207 return(-1L);
1209 return(lookup_in_abook_trie(ab->nick_trie, nickname));
1214 * Returns entry_num of first entry with this address, else NO_NEXT.
1216 adrbk_cntr_t
1217 lookup_address_in_trie(AdrBk *ab, char *address)
1219 dprint((9, "lookup_address_in_trie: %s\n", ab ? (ab->addr_trie ? (address ? address : "?") : "null addr_trie") : "null ab"));
1220 if(!ab || !address || !ab->addr_trie)
1221 return(-1L);
1223 return(lookup_in_abook_trie(ab->addr_trie, address));
1227 adrbk_cntr_t
1228 lookup_in_abook_trie(AdrBk_Trie *t, char *str)
1230 char *p, *lookthisup;
1231 char buf[1000];
1232 adrbk_cntr_t ret = NO_NEXT;
1234 if(!t || !str)
1235 return(ret);
1237 /* make lookup case independent */
1239 for(p = str; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
1242 if(*p){
1243 strncpy(buf, str, sizeof(buf));
1244 buf[sizeof(buf)-1] = '\0';
1245 for(p = buf; *p; p++)
1246 if(!(*p & 0x80) && isupper((unsigned char) *p))
1247 *p = tolower(*p);
1249 lookthisup = buf;
1251 else
1252 lookthisup = str;
1254 p = lookthisup;
1257 * We usually return out from inside the loop (unless str == "").
1259 while(*p){
1260 /* search for character at this level */
1261 while(t->value != *p){
1262 if(t->right == NULL)
1263 return(ret); /* no match */
1265 t = t->right;
1268 if(*++p == '\0') /* end of str, a match */
1269 return(t->entrynum);
1271 /* need to go down to match next character */
1272 if(t->down == NULL) /* no match */
1273 return(ret);
1275 t = t->down;
1278 return(ret);
1282 void
1283 free_abook_trie(AdrBk_Trie **trie)
1285 if(trie){
1286 if(*trie){
1287 free_abook_trie(&(*trie)->down);
1288 free_abook_trie(&(*trie)->right);
1289 fs_give((void **) trie);
1296 * Returns pointer to start of next address book entry from disk file.
1297 * The return will be in raw form from the file with newlines still
1298 * embedded. Or NULL at end of file.
1300 * If rew is set, rewind the file and start over at beginning.
1302 char *
1303 get_next_abook_entry(FILE *fp, int rew)
1305 char *returned_lines = NULL, *p;
1306 char line[1024];
1307 static int will_be_done_next_time = 0;
1308 static long next_nickname_offset = 0L;
1309 size_t lsize;
1310 long offset, saved_offset;
1311 long len = 0L;
1313 #define CHUNKSIZE 500
1315 lsize = sizeof(line);
1317 if(rew){
1318 will_be_done_next_time = 0;
1319 rewind(fp);
1320 /* skip leading (bogus) continuation lines */
1322 offset = ftell(fp);
1323 line[0] = '\0';
1324 line[lsize-2] = '\0';
1325 p = fgets(line, lsize, fp);
1327 if(p == NULL)
1328 return(NULL);
1330 /* line is too long to fit, read the rest and discard */
1331 while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1332 && p != NULL){
1334 /* get next lsize-1 characters, leaving line[0] */
1335 line[lsize-2] = '\0';
1336 p = fgets(line+1, lsize-1, fp);
1338 }while(line[0] == SPACE);
1340 /* offset is start of first good line now */
1341 next_nickname_offset = offset;
1344 if(will_be_done_next_time)
1345 return(NULL);
1347 /* we set this up in rew==1 case or on previous call to this routine */
1348 offset = next_nickname_offset;
1351 * The rest is working on finding the start of the next entry so
1352 * skip continuation lines
1355 next_nickname_offset = ftell(fp);
1356 line[0] = '\0';
1357 line[lsize-2] = '\0';
1358 p = fgets(line, lsize, fp);
1360 /* line is too long to fit, read the rest and discard */
1361 while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
1362 && p != NULL){
1364 /* get next lsize-1 characters, leaving line[0] */
1365 line[lsize-2] = '\0';
1366 p = fgets(line+1, lsize-1, fp);
1368 }while(line[0] == SPACE);
1370 /* next_nickname_offset is start of next entry now */
1372 if(!line[0])
1373 will_be_done_next_time = 1;
1375 len = next_nickname_offset - offset;
1377 returned_lines = (char *) fs_get((len + 1) * sizeof(char));
1379 saved_offset = ftell(fp);
1380 if(fseek(fp, offset, 0)){
1381 dprint((2, "get_next_ab_entry: trouble fseeking\n"));
1382 len = 0;
1383 fs_give((void **) &returned_lines);
1385 else{
1386 if(fread(returned_lines, sizeof(char), (unsigned) len, fp) != len){
1387 dprint((2, "get_next_ab_entry: trouble freading\n"));
1388 len = 0;
1389 fs_give((void **) &returned_lines);
1393 if(fseek(fp, saved_offset, 0)){
1394 dprint((2, "get_next_ab_entry: trouble fseeking to saved_offset\n"));
1395 len = 0;
1396 fs_give((void **) &returned_lines);
1399 if(returned_lines)
1400 returned_lines[len] = '\0';
1402 return(returned_lines);
1407 * Returns a pointer to the start of the mailbox@host part of this
1408 * address string, and a pointer to the end + 1. The caller can then
1409 * replace the end char with \0 and call the hash function, then put
1410 * back the end char. Start_addr and end_addr are assumed to be non-null.
1412 void
1413 strip_addr_string(char *addrstr, char **start_addr, char **end_addr)
1415 register char *q;
1416 int in_quotes = 0,
1417 in_comment = 0;
1418 char prev_char = '\0';
1420 if(!addrstr || !*addrstr){
1421 *start_addr = NULL;
1422 *end_addr = NULL;
1423 return;
1426 *start_addr = addrstr;
1428 for(q = addrstr; *q; q++){
1429 switch(*q){
1430 case '<':
1431 if(!in_quotes && !in_comment){
1432 if(*++q){
1433 *start_addr = q;
1434 /* skip to > */
1435 while(*q && *q != '>')
1436 q++;
1438 /* found > */
1439 if(*q){
1440 *end_addr = q;
1441 return;
1443 else
1444 q--;
1448 break;
1450 case LPAREN:
1451 if(!in_quotes && !in_comment)
1452 in_comment = 1;
1453 break;
1455 case RPAREN:
1456 if(in_comment && prev_char != BSLASH)
1457 in_comment = 0;
1458 break;
1460 case QUOTE:
1461 if(in_quotes && prev_char != BSLASH)
1462 in_quotes = 0;
1463 else if(!in_quotes && !in_comment)
1464 in_quotes = 1;
1465 break;
1467 default:
1468 break;
1471 prev_char = *q;
1474 *end_addr = q;
1479 * Fill in the passed in ae pointer by parsing the str that is passed.
1481 * Args ab --
1482 * ae -- pointer we want to fill in. The individual members of
1483 * the ae struct will be allocated here
1484 * str -- the string from the on-disk address book file for this entry
1486 * Returns a pointer to ae or NULL if there are problems.
1488 AdrBk_Entry *
1489 init_ae(AdrBk *ab, AdrBk_Entry *a, char *str)
1491 char *p;
1492 char *addrfield = (char *) NULL;
1493 char *addrfield_end;
1494 char *nickname, *fullname, *fcc, *extra;
1496 if(!ab){
1497 dprint((2, "init_ae: found trouble: NULL ab\n"));
1498 return((AdrBk_Entry *) NULL);
1501 defvalue_ae(a);
1503 p = str;
1505 REPLACE_NEWLINES_WITH_SPACE(p);
1507 nickname = p;
1508 SKIP_TO_TAB(p);
1509 if(!*p){
1510 RM_END_SPACE(nickname, p);
1511 a->nickname = cpystr(nickname);
1513 else{
1514 *p = '\0';
1515 RM_END_SPACE(nickname, p);
1516 a->nickname = cpystr(nickname);
1517 p++;
1518 SKIP_SPACE(p);
1519 fullname = p;
1520 SKIP_TO_TAB(p);
1521 if(!*p){
1522 RM_END_SPACE(fullname, p);
1523 a->fullname = cpystr(fullname);
1525 else{
1526 *p = '\0';
1527 RM_END_SPACE(fullname, p);
1528 a->fullname = cpystr(fullname);
1529 p++;
1530 SKIP_SPACE(p);
1531 addrfield = p;
1532 SKIP_TO_TAB(p);
1533 if(!*p){
1534 RM_END_SPACE(addrfield, p);
1536 else{
1537 *p = '\0';
1538 RM_END_SPACE(addrfield, p);
1539 p++;
1540 SKIP_SPACE(p);
1541 fcc = p;
1542 SKIP_TO_TAB(p);
1543 if(!*p){
1544 RM_END_SPACE(fcc, p);
1545 a->fcc = cpystr(fcc);
1547 else{
1548 char *src, *dst;
1550 *p = '\0';
1551 RM_END_SPACE(fcc, p);
1552 a->fcc = cpystr(fcc);
1553 p++;
1554 SKIP_SPACE(p);
1555 extra = p;
1556 p = extra + strlen(extra);
1557 RM_END_SPACE(extra, p);
1560 * When we wrap long comments we insert an extra colon
1561 * in the wrap so we can spot it and take it back out.
1562 * Pretty much a hack since we thought of it a long
1563 * time after designing it, but it eliminates the limit
1564 * on length of comments. Here we are looking for
1565 * <SP> <SP> : <SP> or
1566 * <SP> <SP> <SP> : <SP> and replacing it with <SP>.
1567 * There could have been a single \n or \r\n, so that
1568 * is why we check for 2 or 3 spaces before the colon.
1569 * (This was another mistake.)
1571 dst = src = extra;
1572 while(*src != '\0'){
1573 if(*src == SPACE && *(src+1) == SPACE &&
1574 *(src+2) == ':' && *(src+3) == SPACE){
1577 * If there was an extra space because of the
1578 * CRLF (instead of LF) then we already put
1579 * a SP in dst last time through the loop
1580 * and don't need to add another.
1582 if(src == extra || *(src-1) != SPACE)
1583 *dst++ = *src;
1585 src += 4;
1587 else
1588 *dst++ = *src++;
1591 *dst = '\0';
1592 a->extra = cpystr(extra);
1598 /* decode and convert to UTF-8 if we need to */
1600 convert_possibly_encoded_str_to_utf8(&a->nickname);
1601 convert_possibly_encoded_str_to_utf8(&a->fullname);
1602 convert_possibly_encoded_str_to_utf8(&a->fcc);
1603 convert_possibly_encoded_str_to_utf8(&a->extra);
1605 /* parse addrfield */
1606 if(addrfield){
1607 if(*addrfield == '('){ /* it's a list */
1608 a->tag = List;
1609 p = addrfield;
1610 addrfield_end = p + strlen(p);
1613 * Get rid of the parens.
1614 * If this isn't true the input file is messed up.
1616 if(p[strlen(p)-1] == ')'){
1617 char **ll;
1619 p[strlen(p)-1] = '\0';
1620 p++;
1621 a->addr.list = parse_addrlist(p);
1622 for(ll = a->addr.list; ll && *ll; ll++)
1623 convert_possibly_encoded_str_to_utf8(ll);
1625 else{
1626 /* put back what was there to start with */
1627 *addrfield_end = ')';
1628 a->addr.list = (char **)fs_get(sizeof(char *) * 2);
1629 a->addr.list[0] = cpystr(addrfield);
1630 a->addr.list[1] = NULL;
1631 dprint((2, "parsing error reading addressbook: missing right paren: %s\n",
1632 addrfield ? addrfield : "?"));
1635 else{ /* A plain, single address */
1637 a->tag = Single;
1638 a->addr.addr = cpystr(addrfield);
1639 convert_possibly_encoded_str_to_utf8(&a->addr.addr);
1642 else{
1644 * If no addrfield, assume an empty Single.
1646 a->addr.addr = cpystr("");
1647 a->tag = Single;
1650 return(a);
1655 * Return the size of the address book
1657 adrbk_cntr_t
1658 adrbk_count(AdrBk *ab)
1660 return(ab ? ab->count : (adrbk_cntr_t) 0);
1665 * Return a pointer to the ae that has index number "entry_num".
1667 AdrBk_Entry *
1668 adrbk_get_ae(AdrBk *ab, a_c_arg_t entry_num)
1670 if(!ab || entry_num >= (a_c_arg_t) ab->count)
1671 return((AdrBk_Entry *) NULL);
1673 return(&ab->arr[entry_num]);
1678 * Return a pointer to the deleted ae that has index number "entry_num".
1680 AdrBk_Entry *
1681 adrbk_get_delae(AdrBk *ab, a_c_arg_t entry_num)
1683 if(!ab || entry_num >= (a_c_arg_t) ab->del_count)
1684 return((AdrBk_Entry *) NULL);
1686 return(&ab->del[entry_num]);
1691 * Look up an entry in the address book given a nickname
1693 * Args: ab -- the address book
1694 * nickname -- nickname to match
1695 * entry_num -- if matched, return entry_num of match here
1697 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1699 * Lookups usually need to be recursive in case the address
1700 * book references itself. This is left to the next level up.
1701 * adrbk_clearrefs() is provided to clear all the reference tags in
1702 * the address book for loop detection.
1703 * When there are duplicates of the same nickname we return the first.
1704 * This can only happen if addrbook was edited externally.
1706 AdrBk_Entry *
1707 adrbk_lookup_by_nick(AdrBk *ab, char *nickname, adrbk_cntr_t *entry_num)
1709 adrbk_cntr_t num;
1710 AdrBk_Entry *ae;
1712 dprint((5, "- adrbk_lookup_by_nick(%s) (in %s) -\n",
1713 nickname ? nickname : "?",
1714 (ab && ab->filename) ? ab->filename : "?"));
1716 if(!ab || !nickname || !nickname[0])
1717 return NULL;
1720 num = lookup_nickname_in_trie(ab, nickname);
1722 if(num != NO_NEXT){
1723 ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1724 if(entry_num && ae)
1725 *entry_num = num;
1727 return(ae);
1729 else
1730 return((AdrBk_Entry *) NULL);
1735 * Look up an entry in the address book given an address
1737 * Args: ab -- the address book
1738 * address -- address to match
1739 * entry_num -- if matched, return entry_num of match here
1741 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1743 * Note: When there are multiple occurrences of an address in an addressbook,
1744 * which there will be if more than one nickname points to same address, then
1745 * we want this to match the first occurrence so that the fcc you get will
1746 * be predictable.
1748 AdrBk_Entry *
1749 adrbk_lookup_by_addr(AdrBk *ab, char *address, adrbk_cntr_t *entry_num)
1751 adrbk_cntr_t num;
1752 AdrBk_Entry *ae;
1754 dprint((5, "- adrbk_lookup_by_addr(%s) (in %s) -\n",
1755 address ? address : "?",
1756 (ab && ab->filename) ? ab->filename : "?"));
1758 if(!ab || !address || !address[0])
1759 return((AdrBk_Entry *)NULL);
1761 num = lookup_address_in_trie(ab, address);
1763 if(num != NO_NEXT){
1764 ae = adrbk_get_ae(ab, (a_c_arg_t) num);
1765 if(entry_num && ae)
1766 *entry_num = num;
1768 return(ae);
1770 else
1771 return((AdrBk_Entry *)NULL);
1776 * Format a full name.
1778 * Args: fullname -- full name out of address book for formatting
1779 * first -- Return a pointer to first name here.
1780 * last -- Return a pointer to last name here.
1782 * Result: Returns pointer to name formatted for a mail header. Space is
1783 * allocated here and should be freed by caller.
1785 * We need this because we store full names as Last, First.
1786 * If the name has no comma, then no change is made.
1787 * Otherwise the text before the first comma is moved to the end and
1788 * the comma is deleted.
1790 * Last and first have to be freed by caller.
1792 char *
1793 adrbk_formatname(char *fullname, char **first, char **last)
1795 char *comma;
1796 char *new_name;
1798 if(first)
1799 *first = NULL;
1800 if(last)
1801 *last = NULL;
1804 * There is an assumption that the fullname is a UTF-8 string.
1807 if(fullname[0] != '"' && (comma = strindex(fullname, ',')) != NULL){
1808 size_t l;
1809 int last_name_len = comma - fullname;
1811 comma++;
1812 while(*comma && isspace((unsigned char)*comma))
1813 comma++;
1815 if(first)
1816 *first = cpystr(comma);
1818 if(last){
1819 *last = (char *)fs_get((last_name_len + 1) * sizeof(char));
1820 strncpy(*last, fullname, last_name_len);
1821 (*last)[last_name_len] = '\0';
1824 l = strlen(comma) + 1 + last_name_len;
1825 new_name = (char *) fs_get((l+1) * sizeof(char));
1826 strncpy(new_name, comma, l);
1827 new_name[l] = '\0';
1828 strncat(new_name, " ", l+1-1-strlen(new_name));
1829 new_name[l] = '\0';
1830 strncat(new_name, fullname, MIN(last_name_len,l+1-1-strlen(new_name)));
1831 new_name[l] = '\0';
1833 else
1834 new_name = cpystr(fullname);
1836 return(new_name);
1841 * Clear reference flags in preparation for a recursive lookup.
1843 * For loop detection during address book look up. This clears all the
1844 * referenced flags, then as the lookup proceeds the referenced flags can
1845 * be checked and set.
1847 void
1848 adrbk_clearrefs(AdrBk *ab)
1850 adrbk_cntr_t entry_num;
1851 AdrBk_Entry *ae;
1853 dprint((9, "- adrbk_clearrefs -\n"));
1855 if(!ab)
1856 return;
1858 for(entry_num = 0; entry_num < ab->count; entry_num++){
1859 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
1860 ae->referenced = 0;
1866 * Allocate a new AdrBk_Entry
1868 AdrBk_Entry *
1869 adrbk_newentry(void)
1871 AdrBk_Entry *ae;
1873 ae = (AdrBk_Entry *) fs_get(sizeof(AdrBk_Entry));
1874 defvalue_ae(ae);
1876 return(ae);
1881 * Just sets ae values to default.
1882 * Parts should be freed before calling this or they will leak.
1884 void
1885 defvalue_ae(AdrBk_Entry *ae)
1887 ae->nickname = empty;
1888 ae->fullname = empty;
1889 ae->addr.addr = empty;
1890 ae->fcc = empty;
1891 ae->extra = empty;
1892 ae->tag = NotSet;
1893 ae->referenced = 0;
1897 AdrBk_Entry *
1898 copy_ae(AdrBk_Entry *src)
1900 AdrBk_Entry *a;
1902 a = adrbk_newentry();
1903 a->tag = src->tag;
1904 a->nickname = cpystr(src->nickname ? src->nickname : "");
1905 a->fullname = cpystr(src->fullname ? src->fullname : "");
1906 a->fcc = cpystr(src->fcc ? src->fcc : "");
1907 a->extra = cpystr(src->extra ? src->extra : "");
1908 if(a->tag == Single)
1909 a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
1910 else if(a->tag == List){
1911 char **p;
1912 int i, n;
1914 /* count list */
1915 for(p = src->addr.list; p && *p; p++)
1916 ;/* do nothing */
1918 if(p == NULL)
1919 n = 0;
1920 else
1921 n = p - src->addr.list;
1923 a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
1924 for(i = 0; i < n; i++)
1925 a->addr.list[i] = cpystr(src->addr.list[i]);
1927 a->addr.list[n] = NULL;
1930 return(a);
1935 * Add an entry to the address book, or modify an existing entry
1937 * Args: ab -- address book to add to
1938 * old_entry_num -- the entry we want to modify. If this is NO_NEXT, then
1939 * we look up the nickname passed in to see if that's the
1940 * entry to modify, else it is a new entry.
1941 * nickname -- the nickname for new entry
1942 * fullname -- the fullname for new entry
1943 * address -- the address for new entry
1944 * fcc -- the fcc for new entry
1945 * extra -- the extra field for new entry
1946 * tag -- the type of new entry
1947 * new_entry_num -- return entry_num of new or modified entry here
1948 * resort_happened -- means that more than just the current entry changed,
1949 * either something was added or order was changed
1950 * enable_intr -- tell adrbk_write to enable interrupt handling
1951 * be_quiet -- tell adrbk_write to not do percent done messages
1952 * write_it -- only do adrbk_write if this is set
1954 * Result: return code: 0 all went well
1955 * -2 error writing address book, check errno
1956 * -3 no modification, the tag given didn't match
1957 * existing tag
1958 * -4 tabs are in one of the fields passed in
1960 * If the nickname exists in the address book already, the operation is
1961 * considered a modification even if the case does not match exactly,
1962 * otherwise it is an add. The entry the operation occurs on is returned
1963 * in new. All fields are set to those passed in; that is, passing in NULL
1964 * even on a modification will set those fields to NULL as opposed to leaving
1965 * them unchanged. It is acceptable to pass in the current strings
1966 * in the entry in the case of modification. For address lists, the
1967 * structure passed in is what is used, so the storage has to all have
1968 * come from fs_get(). If the pointer passed in is the same as
1969 * the current field, no change is made.
1972 adrbk_add(AdrBk *ab, a_c_arg_t old_entry_num, char *nickname, char *fullname,
1973 char *address, char *fcc, char *extra, Tag tag, adrbk_cntr_t *new_entry_num,
1974 int *resort_happened, int enable_intr, int be_quiet, int write_it)
1976 AdrBk_Entry *a;
1977 AdrBk_Entry *ae;
1978 adrbk_cntr_t old_enum;
1979 adrbk_cntr_t new_enum;
1980 int (*cmp_func)();
1981 int retval = 0;
1982 int need_write = 0;
1983 int set_mangled = 0;
1985 dprint((3, "- adrbk_add(%s) -\n", nickname ? nickname : ""));
1987 if(!ab)
1988 return -2;
1990 /* ---- Make sure there are no tabs in the stuff to add ------*/
1991 if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
1992 (fullname != NULL && strindex(fullname, TAB) != NULL) ||
1993 (fcc != NULL && strindex(fcc, TAB) != NULL) ||
1994 (tag == Single && address != NULL && strindex(address, TAB) != NULL))
1995 return -4;
1998 * Are we adding or updating ?
2000 * If old_entry_num was passed in, we're updating that. If nickname
2001 * already exists, we're updating that entry. Otherwise, this is an add.
2003 if((adrbk_cntr_t)old_entry_num != NO_NEXT){
2004 ae = adrbk_get_ae(ab, old_entry_num);
2005 if(ae)
2006 old_enum = (adrbk_cntr_t)old_entry_num;
2008 else
2009 ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
2011 if(ae == NULL){ /*----- adding a new entry ----*/
2013 ae = adrbk_newentry();
2014 ae->tag = tag;
2015 if(nickname)
2016 ae->nickname = cpystr(nickname);
2017 if(fullname)
2018 ae->fullname = cpystr(fullname);
2019 if(fcc)
2020 ae->fcc = cpystr(fcc);
2021 if(extra)
2022 ae->extra = cpystr(extra);
2024 if(tag == Single)
2025 ae->addr.addr = cpystr(address);
2026 else
2027 ae->addr.list = (char **)NULL;
2029 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2030 cmp_ae_by_full_lists_last :
2031 (ab->sort_rule == AB_SORT_RULE_FULL) ?
2032 cmp_ae_by_full :
2033 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2034 cmp_ae_by_nick_lists_last :
2035 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2036 cmp_ae_by_nick;
2038 if(ab->sort_rule == AB_SORT_RULE_NONE) /* put it last */
2039 new_enum = ab->count;
2040 else /* Find slot for it */
2041 for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t) new_enum);
2042 a != (AdrBk_Entry *)NULL;
2043 a = adrbk_get_ae(ab, (a_c_arg_t) (++new_enum))){
2044 if((*cmp_func)((qsort_t *)&a, (qsort_t *)&ae) >= 0)
2045 break;
2048 /* Insert ae before entry new_enum. */
2049 insert_ab_entry(ab, (a_c_arg_t) new_enum, ae, 0);
2051 if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2052 exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
2054 exp_add_nth(ab->selects, (a_c_arg_t)new_enum);
2057 * insert_ab_entry copies the pointers of ae so the things
2058 * being pointed to (nickname, ...) are still in use.
2059 * Don't free them but free the ae struct itself.
2061 fs_give((void **) &ae);
2063 /*---- return in pointer if requested -----*/
2064 if(new_entry_num)
2065 *new_entry_num = new_enum;
2067 if(resort_happened)
2068 *resort_happened = 1;
2070 if(write_it)
2071 retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num, &set_mangled,
2072 enable_intr, be_quiet);
2074 else{
2075 /*----- Updating an existing entry ----*/
2077 int fix_tries = 0;
2079 if(ae->tag != tag)
2080 return -3;
2083 * Instead of just freeing and reallocating here we attempt to re-use
2084 * the space that was already allocated if possible.
2086 if(ae->nickname != nickname
2087 && ae->nickname != NULL
2088 && nickname != NULL
2089 && strcmp(nickname, ae->nickname) != 0){
2090 need_write++;
2091 fix_tries++;
2092 /* can use already alloc'd space */
2093 if(ae->nickname != NULL && nickname != NULL &&
2094 strlen(nickname) <= strlen(ae->nickname)){
2096 strncpy(ae->nickname, nickname, strlen(ae->nickname)+1);
2098 else{
2099 if(ae->nickname != NULL && ae->nickname != empty)
2100 fs_give((void **)&ae->nickname);
2102 ae->nickname = nickname ? cpystr(nickname) : nickname;
2106 if(ae->fullname != fullname
2107 && ae->fullname != NULL
2108 && fullname != NULL
2109 && strcmp(fullname, ae->fullname) != 0){
2110 need_write++;
2111 if(ae->fullname != NULL && fullname != NULL &&
2112 strlen(fullname) <= strlen(ae->fullname)){
2114 strncpy(ae->fullname, fullname, strlen(ae->fullname)+1);
2116 else{
2117 if(ae->fullname != NULL && ae->fullname != empty)
2118 fs_give((void **)&ae->fullname);
2120 ae->fullname = fullname ? cpystr(fullname) : fullname;
2124 if(ae->fcc != fcc
2125 && ae->fcc != NULL
2126 && fcc != NULL
2127 && strcmp(fcc, ae->fcc) != 0){
2128 need_write++;
2129 if(ae->fcc != NULL && fcc != NULL &&
2130 strlen(fcc) <= strlen(ae->fcc)){
2132 strncpy(ae->fcc, fcc, strlen(ae->fcc)+1);
2134 else{
2135 if(ae->fcc != NULL && ae->fcc != empty)
2136 fs_give((void **)&ae->fcc);
2138 ae->fcc = fcc ? cpystr(fcc) : fcc;
2142 if(ae->extra != extra
2143 && ae->extra != NULL
2144 && extra != NULL
2145 && strcmp(extra, ae->extra) != 0){
2146 need_write++;
2147 if(ae->extra != NULL && extra != NULL &&
2148 strlen(extra) <= strlen(ae->extra)){
2150 strncpy(ae->extra, extra, strlen(ae->extra)+1);
2152 else{
2153 if(ae->extra != NULL && ae->extra != empty)
2154 fs_give((void **)&ae->extra);
2156 ae->extra = extra ? cpystr(extra) : extra;
2160 if(tag == Single){
2161 /*---- Single ----*/
2162 if(ae->addr.addr != address
2163 && ae->addr.addr != NULL
2164 && address != NULL
2165 && strcmp(address, ae->addr.addr) != 0){
2166 need_write++;
2167 fix_tries++;
2168 if(ae->addr.addr != NULL && address != NULL &&
2169 strlen(address) <= strlen(ae->addr.addr)){
2171 strncpy(ae->addr.addr, address, strlen(ae->addr.addr)+1);
2173 else{
2174 if(ae->addr.addr != NULL && ae->addr.addr != empty)
2175 fs_give((void **)&ae->addr.addr);
2177 ae->addr.addr = address ? cpystr(address) : address;
2181 else{
2182 /*---- List -----*/
2184 * We don't mess with lists here.
2185 * The caller has to do it with adrbk_listadd().
2187 ;/* do nothing */
2191 /*---------- Make sure it's still in order ---------*/
2194 * old_enum is where ae is currently located
2195 * put it where it belongs
2197 if(need_write){
2198 new_enum = re_sort_particular_entry(ab, (a_c_arg_t) old_enum);
2199 if(old_enum != new_enum)
2200 fix_tries++;
2202 else
2203 new_enum = old_enum;
2205 /*---- return in pointer if requested -----*/
2206 if(new_entry_num)
2207 *new_entry_num = new_enum;
2209 if(resort_happened)
2210 *resort_happened = (old_enum != new_enum);
2212 if(fix_tries)
2213 repair_abook_tries(ab);
2215 if(write_it && need_write){
2216 int sort_happened = 0;
2218 retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num,
2219 &sort_happened, enable_intr, be_quiet);
2221 set_mangled = sort_happened;
2223 if(new_entry_num)
2224 new_enum = (*new_entry_num);
2226 if(resort_happened && (sort_happened || (old_enum != new_enum)))
2227 *resort_happened = 1;
2229 else
2230 retval = 0;
2233 if(set_mangled)
2234 ps_global->mangled_screen = 1;
2236 return(retval);
2241 * Similar to adrbk_add, but lower cost. No sorting is done, the new entry
2242 * goes on the end. This won't work if it is an edit instead of an append.
2243 * The address book is not committed to disk.
2245 * Args: ab -- address book to add to
2246 * nickname -- the nickname for new entry
2247 * fullname -- the fullname for new entry
2248 * address -- the address for new entry
2249 * fcc -- the fcc for new entry
2250 * extra -- the extra field for new entry
2251 * tag -- the type of new entry
2253 * Result: return code: 0 all went well
2254 * -2 error writing address book, check errno
2255 * -3 no modification, the tag given didn't match
2256 * existing tag
2257 * -4 tabs are in one of the fields passed in
2260 adrbk_append(AdrBk *ab, char *nickname, char *fullname, char *address, char *fcc,
2261 char *extra, Tag tag, adrbk_cntr_t *new_entry_num)
2263 AdrBk_Entry *ae;
2265 dprint((3, "- adrbk_append(%s) -\n", nickname ? nickname : ""));
2267 if(!ab)
2268 return -2;
2270 /* ---- Make sure there are no tabs in the stuff to add ------*/
2271 if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
2272 (fullname != NULL && strindex(fullname, TAB) != NULL) ||
2273 (fcc != NULL && strindex(fcc, TAB) != NULL) ||
2274 (tag == Single && address != NULL && strindex(address, TAB) != NULL))
2275 return -4;
2277 ae = adrbk_newentry();
2278 ae->tag = tag;
2279 if(nickname)
2280 ae->nickname = cpystr(nickname);
2281 if(fullname)
2282 ae->fullname = cpystr(fullname);
2283 if(fcc)
2284 ae->fcc = cpystr(fcc);
2285 if(extra)
2286 ae->extra = cpystr(extra);
2288 if(tag == Single)
2289 ae->addr.addr = cpystr(address);
2290 else
2291 ae->addr.list = (char **)NULL;
2293 if(new_entry_num)
2294 *new_entry_num = ab->count;
2296 insert_ab_entry(ab, (a_c_arg_t) ab->count, ae, 0);
2299 * insert_ab_entry copies the pointers of ae so the things
2300 * being pointed to (nickname, ...) are still in use.
2301 * Don't free them but free the ae struct itself.
2303 fs_give((void **) &ae);
2305 return(0);
2310 * The entire address book is assumed sorted correctly except perhaps for
2311 * entry number cur. Put it in the correct place. Return the new entry
2312 * number for cur.
2314 adrbk_cntr_t
2315 re_sort_particular_entry(AdrBk *ab, a_c_arg_t cur)
2317 AdrBk_Entry *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
2318 long small_enough;
2319 adrbk_cntr_t big_enough;
2320 adrbk_cntr_t new_entry_num;
2321 int (*cmp_func)(const qsort_t *, const qsort_t *);
2323 dprint((9, "- re_sort -\n"));
2325 cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
2326 cmp_ae_by_full_lists_last :
2327 (ab->sort_rule == AB_SORT_RULE_FULL) ?
2328 cmp_ae_by_full :
2329 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
2330 cmp_ae_by_nick_lists_last :
2331 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2332 cmp_ae_by_nick;
2334 new_entry_num = (adrbk_cntr_t) cur;
2336 if(ab->sort_rule == AB_SORT_RULE_NONE)
2337 return(new_entry_num);
2339 ae_cur = adrbk_get_ae(ab, cur);
2341 if(cur > 0)
2342 ae_prev = adrbk_get_ae(ab, cur - 1);
2344 if(cur < ab->count -1)
2345 ae_next = adrbk_get_ae(ab, cur + 1);
2348 * A possible optimization here would be to implement some sort of
2349 * binary search to find where it goes instead of stepping through the
2350 * entries one at a time.
2352 if(cur > 0 &&
2353 (*cmp_func)((qsort_t *)&ae_cur,(qsort_t *)&ae_prev) < 0){
2354 /*--- Out of order, needs to be moved up ----*/
2355 for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
2356 ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t) small_enough);
2357 if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_small_enough) >= 0)
2358 break;
2360 new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
2361 move_ab_entry(ab, cur, (a_c_arg_t) new_entry_num);
2363 else if(cur < ab->count - 1 &&
2364 (*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_next) > 0){
2365 /*---- Out of order needs, to be moved towards end of list ----*/
2366 for(big_enough = (adrbk_cntr_t)(cur + 2);
2367 big_enough < ab->count;
2368 big_enough++){
2369 ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t) big_enough);
2370 if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_big_enough) <= 0)
2371 break;
2373 new_entry_num = big_enough - 1;
2374 move_ab_entry(ab, cur, (a_c_arg_t) big_enough);
2377 dprint((9, "- re_sort done -\n"));
2379 return(new_entry_num);
2384 * Delete an entry from the address book
2386 * Args: ab -- the address book
2387 * entry_num -- entry to delete
2388 * save_deleted -- save deleted as a #DELETED- entry
2389 * enable_intr -- tell adrbk_write to enable interrupt handling
2390 * be_quiet -- tell adrbk_write to not do percent done messages
2391 * write_it -- only do adrbk_write if this is set
2393 * Result: returns: 0 if all went well
2394 * -1 if there is no such entry
2395 * -2 error writing address book, check errno
2398 adrbk_delete(AdrBk *ab, a_c_arg_t entry_num, int save_deleted, int enable_intr,
2399 int be_quiet, int write_it)
2401 int retval = 0;
2402 int set_mangled = 0;
2404 dprint((3, "- adrbk_delete(%ld) -\n", (long)entry_num));
2406 if(!ab)
2407 return -2;
2409 delete_ab_entry(ab, entry_num, save_deleted);
2410 if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
2411 exp_del_nth(ab->exp, entry_num);
2413 exp_del_nth(ab->selects, entry_num);
2415 if(write_it)
2416 retval = adrbk_write(ab, 0, NULL, &set_mangled, enable_intr, be_quiet);
2418 if(set_mangled)
2419 ps_global->mangled_screen = 1;
2421 return(retval);
2426 * Delete an address out of an address list
2428 * Args: ab -- the address book
2429 * entry_num -- the address list we are deleting from
2430 * addr -- address in above list to be deleted
2432 * Result: 0: Deletion complete, address book written
2433 * -1: Address for deletion not found
2434 * -2: Error writing address book. Check errno.
2436 * The address to be deleted is located by matching the string.
2439 adrbk_listdel(AdrBk *ab, a_c_arg_t entry_num, char *addr)
2441 char **p, *to_free;
2442 AdrBk_Entry *ae;
2443 int ret;
2444 int set_mangled = 0;
2446 dprint((3, "- adrbk_listdel(%ld) -\n", (long) entry_num));
2448 if(!ab || entry_num >= ab->count)
2449 return -2;
2451 if(!addr)
2452 return -1;
2454 ae = adrbk_get_ae(ab, entry_num);
2456 if(ae->tag != List)
2457 return -1;
2459 for(p = ae->addr.list; *p; p++)
2460 if(strcmp(*p, addr) == 0)
2461 break;
2463 if(*p == NULL)
2464 return -1;
2466 /* note storage to be freed */
2467 if(*p != empty)
2468 to_free = *p;
2469 else
2470 to_free = NULL;
2472 /* slide all the entries below up (including NULL) */
2473 for(; *p; p++)
2474 *p = *(p+1);
2476 if(to_free)
2477 fs_give((void **) &to_free);
2479 ret = adrbk_write(ab, 0, NULL, &set_mangled, 1, 0);
2481 if(set_mangled)
2482 ps_global->mangled_screen = 1;
2484 return(ret);
2489 * Delete all addresses out of an address list
2491 * Args: ab -- the address book
2492 * entry_num -- the address list we are deleting from
2494 * Result: 0: Deletion complete, address book written
2495 * -1: Address for deletion not found
2496 * -2: Error writing address book. Check errno.
2499 adrbk_listdel_all(AdrBk *ab, a_c_arg_t entry_num)
2501 char **p;
2502 AdrBk_Entry *ae;
2504 dprint((3, "- adrbk_listdel_all(%ld) -\n", (long) entry_num));
2506 if(!ab || entry_num >= ab->count)
2507 return -2;
2509 ae = adrbk_get_ae(ab, entry_num);
2511 if(ae->tag != List)
2512 return -1;
2514 /* free old list */
2515 for(p = ae->addr.list; p && *p; p++)
2516 if(*p != empty)
2517 fs_give((void **)p);
2519 if(ae->addr.list)
2520 fs_give((void **) &ae->addr.list);
2522 ae->addr.list = NULL;
2524 return 0;
2529 * Add a list of addresses to an already existing address list
2531 * Args: ab -- the address book
2532 * entry_num -- the address list we are adding to
2533 * addrs -- address list to be added
2534 * enable_intr -- tell adrbk_write to enable interrupt handling
2535 * be_quiet -- tell adrbk_write to not do percent done messages
2536 * write_it -- only do adrbk_write if this is set
2538 * Result: returns 0 : addition made, address book written
2539 * -1 : addition to non-list attempted
2540 * -2 : error writing address book -- check errno
2543 adrbk_nlistadd(AdrBk *ab, a_c_arg_t entry_num, adrbk_cntr_t *new_entry_num,
2544 int *resort_happened, char **addrs,
2545 int enable_intr, int be_quiet, int write_it)
2547 char **p;
2548 int cur_size, size_of_additional_list, new_size;
2549 int i, rc = 0;
2550 int set_mangled = 0;
2551 AdrBk_Entry *ae;
2553 dprint((3, "- adrbk_nlistadd(%ld) -\n", (long) entry_num));
2555 if(!ab || entry_num >= ab->count)
2556 return -2;
2558 ae = adrbk_get_ae(ab, entry_num);
2560 if(ae->tag != List)
2561 return -1;
2563 /* count up size of existing list */
2564 for(p = ae->addr.list; p != NULL && *p != NULL; p++)
2565 ;/* do nothing */
2567 cur_size = p - ae->addr.list;
2569 /* count up size of new list */
2570 for(p = addrs; p != NULL && *p != NULL; p++)
2571 ;/* do nothing */
2573 size_of_additional_list = p - addrs;
2574 new_size = cur_size + size_of_additional_list;
2576 /* make room at end of list for it */
2577 if(cur_size == 0)
2578 ae->addr.list = (char **) fs_get(sizeof(char *) * (new_size + 1));
2579 else
2580 fs_resize((void **) &ae->addr.list, sizeof(char *) * (new_size + 1));
2582 /* Put new list at the end */
2583 for(i = cur_size; i < new_size; i++)
2584 (ae->addr.list)[i] = cpystr(addrs[i - cur_size]);
2586 (ae->addr.list)[new_size] = NULL;
2588 /*---- sort it into the correct place ------*/
2589 if(ab->sort_rule != AB_SORT_RULE_NONE)
2590 sort_addr_list(ae->addr.list);
2592 if(write_it)
2593 rc = adrbk_write(ab, entry_num, new_entry_num, &set_mangled, enable_intr, be_quiet);
2595 if(set_mangled){
2596 ps_global->mangled_screen = 1;
2597 if(resort_happened)
2598 *resort_happened = 1;
2601 return(rc);
2606 * Set the valid variable if we determine that the address book has
2607 * been changed by something other than us. This means we should update
2608 * our view of the address book when next possible.
2610 * Args ab -- AdrBk handle
2611 * do_it_now -- If > 0, check now regardless
2612 * If = 0, check if time since last chk more than default
2613 * If < 0, check if time since last chk more than -do_it_now
2615 void
2616 adrbk_check_validity(AdrBk *ab, long int do_it_now)
2618 dprint((9, "- adrbk_check_validity(%s) -\n",
2619 (ab && ab->filename) ? ab->filename : ""));
2621 if(!ab || ab->flags & FILE_OUTOFDATE)
2622 return;
2624 adrbk_check_local_validity(ab, do_it_now);
2626 if(ab->type == Imap && !(ab->flags & FILE_OUTOFDATE ||
2627 ab->rd->flags & REM_OUTOFDATE))
2628 rd_check_remvalid(ab->rd, do_it_now);
2633 * Set the valid variable if we determine that the address book has
2634 * been changed by something other than us. This means we should update
2635 * our view of the address book when next possible.
2637 * Args ab -- AdrBk handle
2638 * do_it_now -- If > 0, check now regardless
2639 * If = 0, check if time since last chk more than default
2640 * If < 0, check if time since last chk more than -do_it_now
2642 void
2643 adrbk_check_local_validity(AdrBk *ab, long int do_it_now)
2645 time_t mtime, chk_interval;
2647 dprint((9, "- adrbk_check_local_validity(%s) -\n",
2648 (ab && ab->filename) ? ab->filename : ""));
2650 if(!ab)
2651 return;
2653 if(do_it_now < 0L){
2654 chk_interval = -1L * do_it_now;
2655 do_it_now = 0L;
2657 else
2658 chk_interval = FILE_VALID_CHK_INTERVAL;
2660 if(!do_it_now &&
2661 get_adj_time() <= ab->last_local_valid_chk + chk_interval)
2662 return;
2664 ab->last_local_valid_chk = get_adj_time();
2667 * Check local file for a modification time change.
2668 * If this is out of date, don't even bother checking for the remote
2669 * folder being out of date. That will get fixed when we reopen.
2671 if(!(ab->flags & FILE_OUTOFDATE) &&
2672 ab->last_change_we_know_about != (time_t)(-1) &&
2673 (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
2674 ab->last_change_we_know_about != mtime){
2676 dprint((2, "adrbk_check_local_validity: addrbook %s has changed\n",
2677 ab->filename ? ab->filename : "?"));
2678 ab->flags |= FILE_OUTOFDATE;
2684 * See if we can re-use an existing stream.
2686 * [ We don't believe we need this anymore now that the stuff in pine.c ]
2687 * [ is recycling streams for us, but we haven't thought it through all ]
2688 * [ the way enough to get rid of this. Hubert 2003-07-09 ]
2690 * Args name -- Name of folder we want a stream for.
2692 * Returns -- A mail stream suitable for status cmd or append cmd.
2693 * NULL if none were available.
2695 MAILSTREAM *
2696 adrbk_handy_stream(char *name)
2698 MAILSTREAM *stat_stream = NULL;
2699 int i;
2701 dprint((9, "- adrbk_handy_stream(%s) -\n", name ? name : "?"));
2703 stat_stream = sp_stream_get(name, SP_SAME);
2706 * Look through our imap streams to see if there is one we can use.
2708 for(i = 0; !stat_stream && i < as.n_addrbk; i++){
2709 PerAddrBook *pab;
2711 pab = &as.adrbks[i];
2713 if(pab->address_book &&
2714 pab->address_book->type == Imap &&
2715 pab->address_book->rd &&
2716 pab->address_book->rd->type == RemImap &&
2717 same_stream(name, pab->address_book->rd->t.i.stream)){
2718 stat_stream = pab->address_book->rd->t.i.stream;
2719 pab->address_book->rd->last_use = get_adj_time();
2720 dprint((7,
2721 "%s: used other abook stream for status (%ld)\n",
2722 pab->address_book->orig_filename
2723 ? pab->address_book->orig_filename : "?",
2724 (long)pab->address_book->rd->last_use));
2728 dprint((9, "adrbk_handy_stream: returning %s\n",
2729 stat_stream ? "good stream" : "NULL"));
2731 return(stat_stream);
2736 * Close address book
2738 * All that is done here is to free the storage, since the address book is
2739 * rewritten on every change.
2741 void
2742 adrbk_close(AdrBk *ab)
2744 int we_cancel = 0;
2746 dprint((4, "- adrbk_close(%s) -\n",
2747 (ab && ab->filename) ? ab->filename : ""));
2749 if(!ab)
2750 return;
2752 if(ab->rd)
2753 rd_close_remdata(&ab->rd);
2755 if(ab->fp)
2756 (void)fclose(ab->fp);
2758 if(ab->our_filecopy && ab->filename != ab->our_filecopy){
2759 our_unlink(ab->our_filecopy);
2760 fs_give((void**) &ab->our_filecopy);
2763 if(ab->exp){
2764 exp_free(ab->exp);
2765 fs_give((void **)&ab->exp); /* free head of list, too */
2768 if(ab->checks){
2769 exp_free(ab->checks);
2770 fs_give((void **)&ab->checks); /* free head of list, too */
2773 if(ab->selects){
2774 exp_free(ab->selects);
2775 fs_give((void **)&ab->selects); /* free head of list, too */
2778 if(ab->arr){
2779 adrbk_cntr_t entry_num;
2782 * ab->arr is an allocated array. Each element of the array contains
2783 * several pointers that point to other allocated stuff, so we
2784 * free_ae_parts to get those and then free the array after the loop.
2786 for(entry_num = 0; entry_num < ab->count; entry_num++)
2787 free_ae_parts(&ab->arr[entry_num]);
2789 fs_give((void **) &ab->arr);
2792 if(ab->del){
2793 adrbk_cntr_t entry_num;
2795 for(entry_num = 0; entry_num < ab->del_count; entry_num++)
2796 free_ae_parts(&ab->del[entry_num]);
2798 fs_give((void **) &ab->del);
2801 if(ab->nick_trie)
2802 free_abook_trie(&ab->nick_trie);
2804 if(ab->addr_trie)
2805 free_abook_trie(&ab->addr_trie);
2807 if(ab->full_trie)
2808 free_abook_trie(&ab->full_trie);
2810 if(ab->revfull_trie)
2811 free_abook_trie(&ab->revfull_trie);
2813 if(we_cancel)
2814 cancel_busy_cue(0);
2816 if(ab->filename){
2817 if(ab->flags & DEL_FILE)
2818 our_unlink(ab->filename);
2820 fs_give((void**)&ab->filename);
2823 if(ab->orig_filename)
2824 fs_give((void**)&ab->orig_filename);
2826 fs_give((void **) &ab);
2830 void
2831 adrbk_partial_close(AdrBk *ab)
2833 dprint((4, "- adrbk_partial_close(%s) -\n",
2834 (ab && ab->filename) ? ab->filename : ""));
2836 exp_free(ab->exp); /* leaves head of list */
2837 exp_free(ab->checks); /* leaves head of list */
2842 * It has been noticed that this stream is dead and it is about to
2843 * be removed from the stream pool. We may have a pointer to it for one
2844 * of the remote address books. We have to note that the pointer is stale.
2846 void
2847 note_closed_adrbk_stream(MAILSTREAM *stream)
2849 PerAddrBook *pab;
2850 int i;
2852 if(!stream)
2853 return;
2855 for(i = 0; i < as.n_addrbk; i++){
2856 pab = &as.adrbks[i];
2857 if(pab->address_book
2858 && pab->address_book->type == Imap
2859 && pab->address_book->rd
2860 && pab->address_book->rd->type == RemImap
2861 && (stream == pab->address_book->rd->t.i.stream)){
2862 dprint((4, "- note_closed_adrbk_stream(%s) -\n",
2863 (pab->address_book && pab->address_book->orig_filename)
2864 ? pab->address_book->orig_filename : ""));
2865 pab->address_book->rd->t.i.stream = NULL;
2871 static adrbk_cntr_t tot_for_percent;
2872 static adrbk_cntr_t entry_num_for_percent;
2875 * Write out the address book.
2877 * If be_quiet is set, don't turn on busy_cue.
2879 * If enable_intr_handling is set, turn on and off interrupt handling.
2881 * Format is as in comment in the adrbk_open routine. Lines are wrapped
2882 * to be under 80 characters. This is called on every change to the
2883 * address book. Write is first to a temporary file,
2884 * which is then renamed to be the real address book so that we won't
2885 * destroy the real address book in case of something like a full file
2886 * system.
2888 * Writing a temp file and then renaming has the bad side affect of
2889 * destroying links. It also overrides any read only permissions on
2890 * the mail file since rename ignores such permissions. However, we
2891 * handle readonly-ness in addrbook.c before we call this.
2892 * We retain the permissions by doing a stat on the old file and a
2893 * chmod on the new one (with file_attrib_copy).
2895 * In pre-alpine pine the address book entries were encoded on disk.
2896 * We would be happy with raw UTF-8 now but in order to preserve some
2897 * backwards compatibility we encode the entries before writing.
2898 * We first try to translate to the user's character set and encode
2899 * in that, else we use UTF-8 but with 1522 encoding.
2901 * Returns: 0 write was successful
2902 * -2 write failed
2903 * -5 interrupted
2906 adrbk_write(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num,
2907 int *sort_happened, int enable_intr_handling, int be_quiet)
2909 FILE *ab_stream = NULL;
2910 AdrBk_Entry *ae = NULL;
2911 adrbk_cntr_t entry_num;
2912 #ifndef DOS
2913 void (*save_sighup)();
2914 #endif
2915 int max_nick = 0,
2916 max_full = 0, full_two = 0, full_three = 0,
2917 max_fcc = 0, fcc_two = 0, fcc_three = 0,
2918 max_addr = 0, addr_two = 0, addr_three = 0,
2919 this_nick_width, this_full_width, this_addr_width,
2920 this_fcc_width, fd, i;
2921 int interrupt_happened = 0, we_cancel = 0, we_turned_on = 0;
2922 char *temp_filename = NULL;
2923 WIDTH_INFO_S *widths;
2925 if(!ab)
2926 return -2;
2928 dprint((2, "- adrbk_write(\"%s\") - writing %lu entries\n",
2929 ab->filename ? ab->filename : "", (unsigned long) ab->count));
2931 errno = 0;
2933 adrbk_check_local_validity(ab, 1L);
2934 /* verify that file has not been changed by something else */
2935 if(ab->flags & FILE_OUTOFDATE){
2936 /* It has changed! */
2937 q_status_message(SM_ORDER | SM_DING, 5, 15,
2938 /* TRANSLATORS: The address book was changed by something else so alpine
2939 is not making the change the user wanted to make to avoid damaging
2940 the address book. */
2941 _("Addrbook changed by another process, aborting our change to avoid damage..."));
2942 dprint((1, "adrbk_write: addrbook %s changed while we had it open, aborting write\n",
2943 ab->filename ? ab->filename : "?"));
2944 longjmp(addrbook_changed_unexpectedly, 1);
2945 /*NOTREACHED*/
2949 * Verify that remote folder has not been
2950 * changed by something else (not if checked in last 5 seconds).
2952 * The -5 is so we won't check every time on a series of quick writes.
2954 rd_check_remvalid(ab->rd, -5L);
2955 if(ab->type == Imap){
2956 int ro;
2959 * We'll eventually need this open to write to remote, so see if
2960 * we can open it now.
2962 rd_open_remote(ab->rd);
2965 * Did someone else change the remote copy?
2967 if((ro=rd_remote_is_readonly(ab->rd)) || ab->rd->flags & REM_OUTOFDATE){
2968 if(ro == 1){
2969 q_status_message(SM_ORDER | SM_DING, 5, 15,
2970 _("Can't access remote addrbook, aborting change..."));
2971 dprint((1,
2972 "adrbk_write: Can't write to remote addrbook %s, aborting write: open failed\n",
2973 ab->rd->rn ? ab->rd->rn : "?"));
2975 else if(ro == 2){
2976 if(!(ab->rd->flags & NO_META_UPDATE)){
2977 unsigned long save_chk_nmsgs;
2980 * Should have some non-type-specific method of doing this.
2982 switch(ab->rd->type){
2983 case RemImap:
2984 save_chk_nmsgs = ab->rd->t.i.chk_nmsgs;
2985 ab->rd->t.i.chk_nmsgs = 0;/* cause it to be OUTOFDATE */
2986 rd_write_metadata(ab->rd, 0);
2987 ab->rd->t.i.chk_nmsgs = save_chk_nmsgs;
2988 break;
2990 default:
2991 q_status_message(SM_ORDER | SM_DING, 3, 5,
2992 "Adrbk_write: Type not supported");
2993 break;
2997 q_status_message(SM_ORDER | SM_DING, 5, 15,
2998 _("No write permission for remote addrbook, aborting change..."));
3000 else{
3001 q_status_message(SM_ORDER | SM_DING, 5, 15,
3002 _("Remote addrbook changed, aborting our change to avoid damage..."));
3003 dprint((1,
3004 "adrbk_write: remote addrbook %s changed while we had it open, aborting write\n",
3005 ab->orig_filename ? ab->orig_filename : "?"));
3008 rd_close_remote(ab->rd);
3010 longjmp(addrbook_changed_unexpectedly, 1);
3011 /*NOTREACHED*/
3015 #ifndef DOS
3016 save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
3017 #endif
3020 * If we want to be able to modify the address book, we will
3021 * need a temp_filename in the same directory as our_filecopy.
3023 if(!(ab->our_filecopy && ab->our_filecopy[0])
3024 || !(temp_filename = tempfile_in_same_dir(ab->our_filecopy,"a1",NULL))
3025 || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3026 dprint((1, "adrbk_write(%s): failed opening temp file (%s)\n",
3027 ab->filename ? ab->filename : "?",
3028 temp_filename ? temp_filename : "NULL"));
3029 goto io_error;
3032 ab_stream = fdopen(fd, "wb");
3033 if(ab_stream == NULL){
3034 dprint((1, "adrbk_write(%s): fdopen failed\n", temp_filename ? temp_filename : "?"));
3035 goto io_error;
3038 if(adrbk_is_in_sort_order(ab, be_quiet)){
3039 if(sort_happened)
3040 *sort_happened = 0;
3042 if(new_entry_num)
3043 *new_entry_num = current_entry_num;
3045 else{
3046 if(sort_happened)
3047 *sort_happened = 1;
3049 (void) adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet);
3052 /* accept keyboard interrupts */
3053 if(enable_intr_handling)
3054 we_turned_on = intr_handling_on();
3056 if(!be_quiet){
3057 tot_for_percent = MAX(ab->count, 1);
3058 entry_num_for_percent = 0;
3059 we_cancel = busy_cue(_("Saving address book"), percent_abook_saved, 0);
3062 writing = 1;
3065 * If there are any old deleted entries, copy them to new file.
3067 if(ab->del_count > 0){
3068 char *nickname;
3069 long delindex;
3072 * If there are deleted entries old enough that we no longer want
3073 * to save them, remove them from the list.
3075 for(delindex = (long) ab->del_count-1;
3076 delindex >= 0L && ab->del_count > 0; delindex--){
3078 ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3079 nickname = ae->nickname;
3081 if(strncmp(nickname, DELETED, DELETED_LEN) == 0
3082 && isdigit((unsigned char)nickname[DELETED_LEN])
3083 && isdigit((unsigned char)nickname[DELETED_LEN+1])
3084 && nickname[DELETED_LEN+2] == '/'
3085 && isdigit((unsigned char)nickname[DELETED_LEN+3])
3086 && isdigit((unsigned char)nickname[DELETED_LEN+4])
3087 && nickname[DELETED_LEN+5] == '/'
3088 && isdigit((unsigned char)nickname[DELETED_LEN+6])
3089 && isdigit((unsigned char)nickname[DELETED_LEN+7])
3090 && nickname[DELETED_LEN+8] == '#'){
3091 int year, month, day;
3092 struct tm *tm_before;
3093 time_t now, before;
3095 now = time((time_t *)0);
3096 before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
3097 tm_before = localtime(&before);
3098 tm_before->tm_mon++;
3101 * Check to see if it is older than 100 days.
3103 year = atoi(&nickname[DELETED_LEN]);
3104 month = atoi(&nickname[DELETED_LEN+3]);
3105 day = atoi(&nickname[DELETED_LEN+6]);
3106 if(year < 95)
3107 year += 100; /* this breaks in year 2095 */
3110 * remove it if it is more than 100 days old
3112 if(!(year > tm_before->tm_year
3113 || (year == tm_before->tm_year
3114 && month > tm_before->tm_mon)
3115 || (year == tm_before->tm_year
3116 && month == tm_before->tm_mon
3117 && day >= tm_before->tm_mday))){
3119 /* it's old, dump it */
3120 free_ae_parts(ae);
3121 ab->del_count--;
3122 /* patch the array by moving everything below up */
3123 if(delindex < ab->del_count){
3124 memmove(&ab->del[delindex],
3125 &ab->del[delindex+1],
3126 (ab->del_count - delindex) *
3127 sizeof(AdrBk_Entry));
3133 /* write out what remains */
3134 if(ab->del_count){
3135 for(delindex = 0L; delindex < ab->del_count; delindex++){
3136 ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
3137 if(write_single_abook_entry(ae, ab_stream, NULL, NULL,
3138 NULL, NULL) == EOF){
3139 dprint((1, "adrbk_write(%s): failed writing deleted entry\n",
3140 temp_filename ? temp_filename : "?"));
3141 goto io_error;
3145 else{
3146 /* nothing left, get rid of del array */
3147 fs_give((void **) &ab->del);
3151 if(ab->del_count)
3152 dprint((4, " adrbk_write: saving %ld deleted entries\n",
3153 (long) ab->del_count));
3155 for(entry_num = 0; entry_num < ab->count; entry_num++){
3156 entry_num_for_percent++;
3158 ALARM_BLIP();
3160 ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
3162 if(ae == (AdrBk_Entry *) NULL){
3163 dprint((1, "adrbk_write(%s): can't find ae while writing addrbook, entry_num = %ld\n",
3164 ab->filename ? ab->filename : "?",
3165 (long) entry_num));
3166 goto io_error;
3169 /* write to temp file */
3170 if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
3171 &this_full_width, &this_addr_width, &this_fcc_width) == EOF){
3172 dprint((1, "adrbk_write(%s): failed writing for entry %ld\n",
3173 temp_filename ? temp_filename : "?",
3174 (long) entry_num));
3175 goto io_error;
3178 /* keep track of widths */
3179 max_nick = MAX(max_nick, this_nick_width);
3180 if(this_full_width > max_full){
3181 full_three = full_two;
3182 full_two = max_full;
3183 max_full = this_full_width;
3185 else if(this_full_width > full_two){
3186 full_three = full_two;
3187 full_two = this_full_width;
3189 else if(this_full_width > full_three){
3190 full_three = this_full_width;
3193 if(this_addr_width > max_addr){
3194 addr_three = addr_two;
3195 addr_two = max_addr;
3196 max_addr = this_addr_width;
3198 else if(this_addr_width > addr_two){
3199 addr_three = addr_two;
3200 addr_two = this_addr_width;
3202 else if(this_addr_width > addr_three){
3203 addr_three = this_addr_width;
3206 if(this_fcc_width > max_fcc){
3207 fcc_three = fcc_two;
3208 fcc_two = max_fcc;
3209 max_fcc = this_fcc_width;
3211 else if(this_fcc_width > fcc_two){
3212 fcc_three = fcc_two;
3213 fcc_two = this_fcc_width;
3215 else if(this_fcc_width > fcc_three){
3216 fcc_three = this_fcc_width;
3220 * Check to see if we've been interrupted. We check at the bottom
3221 * of the loop so that we can handle an interrupt in the last
3222 * iteration.
3224 if(enable_intr_handling && ps_global->intr_pending){
3225 interrupt_happened++;
3226 goto io_error;
3230 if(we_cancel){
3231 cancel_busy_cue(-1);
3232 we_cancel = 0;
3235 if(enable_intr_handling && we_turned_on){
3236 intr_handling_off();
3237 we_turned_on = 0;
3240 if(fclose(ab_stream) == EOF){
3241 dprint((1, "adrbk_write: fclose for %s failed\n",
3242 temp_filename ? temp_filename : "?"));
3243 goto io_error;
3246 ab_stream = (FILE *) NULL;
3248 file_attrib_copy(temp_filename, ab->our_filecopy);
3249 if(ab->fp){
3250 (void) fclose(ab->fp);
3251 ab->fp = NULL; /* in case of problems */
3254 if((i=rename_file(temp_filename, ab->our_filecopy)) < 0){
3255 dprint((1, "adrbk_write: rename(%s, %s) failed: %s\n",
3256 temp_filename ? temp_filename : "?",
3257 ab->our_filecopy ? ab->our_filecopy : "?",
3258 error_description(errno)));
3259 #ifdef _WINDOWS
3260 if(i == -5){
3261 q_status_message2(SM_ORDER | SM_DING, 5, 7,
3262 _("Can't replace address book %.200sfile \"%.200s\""),
3263 (ab->type == Imap) ? "cache " : "",
3264 ab->filename);
3265 q_status_message(SM_ORDER | SM_DING, 5, 7,
3266 _("If another Alpine is running, quit that Alpine before updating address book."));
3268 #endif /* _WINDOWS */
3269 goto io_error;
3272 /* reopen fp to new file */
3273 if(!(ab->fp = our_fopen(ab->our_filecopy, "rb"))){
3274 dprint((1, "adrbk_write: can't reopen %s\n",
3275 ab->our_filecopy ? ab->our_filecopy : "?"));
3276 goto io_error;
3279 if(temp_filename){
3280 our_unlink(temp_filename);
3281 fs_give((void **) &temp_filename);
3285 * Now copy our_filecopy back to filename.
3287 if(ab->filename != ab->our_filecopy){
3288 int c, err = 0;
3289 char *lc;
3291 if(!(ab->filename && ab->filename[0])
3292 || !(temp_filename = tempfile_in_same_dir(ab->filename,"a1",NULL))
3293 || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
3294 dprint((1,
3295 "adrbk_write(%s): failed opening temp file (%s)\n",
3296 ab->filename ? ab->filename : "?",
3297 temp_filename ? temp_filename : "NULL"));
3298 err++;
3301 if(!err)
3302 ab_stream = fdopen(fd, "wb");
3304 if(ab_stream == NULL){
3305 dprint((1, "adrbk_write(%s): fdopen failed\n",
3306 temp_filename ? temp_filename : "?"));
3307 err++;
3310 if(!err){
3311 rewind(ab->fp);
3312 while((c = getc(ab->fp)) != EOF)
3313 if(putc(c, ab_stream) == EOF){
3314 err++;
3315 break;
3319 if(!err && fclose(ab_stream) == EOF)
3320 err++;
3322 if(!err){
3323 #ifdef _WINDOWS
3324 int tries = 3;
3325 #endif
3327 file_attrib_copy(temp_filename, ab->filename);
3330 * This could fail if somebody else has it open, but they should
3331 * only have it open for a short time.
3333 while(!err && rename_file(temp_filename, ab->filename) < 0){
3334 #ifdef _WINDOWS
3335 if(i == -5){
3336 if(--tries <= 0)
3337 err++;
3339 if(!err){
3340 q_status_message2(SM_ORDER, 0, 3,
3341 _("Replace of \"%.200s\" failed, trying %.200s"),
3342 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename,
3343 (tries > 1) ? "again" : "one more time");
3344 display_message('x');
3345 sleep(3);
3348 #else /* UNIX */
3349 err++;
3350 #endif /* UNIX */
3354 if(err){
3355 q_status_message1(SM_ORDER | SM_DING, 5, 5,
3356 _("Copy of addrbook to \"%.200s\" failed, changes NOT saved!"),
3357 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
3358 dprint((2, "adrbk_write: failed copying our_filecopy (%s)back to filename (%s): %s, continuing without a net\n",
3359 ab->our_filecopy ? ab->our_filecopy : "?",
3360 ab->filename ? ab->filename : "?",
3361 error_description(errno)));
3365 widths = &ab->widths;
3366 widths->max_nickname_width = MIN(max_nick, 99);
3367 widths->max_fullname_width = MIN(max_full, 99);
3368 widths->max_addrfield_width = MIN(max_addr, 99);
3369 widths->max_fccfield_width = MIN(max_fcc, 99);
3370 widths->third_biggest_fullname_width = MIN(full_three, 99);
3371 widths->third_biggest_addrfield_width = MIN(addr_three, 99);
3372 widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
3374 /* record new change date of addrbook file */
3375 ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
3377 #ifndef DOS
3378 (void)signal(SIGHUP, save_sighup);
3379 #endif
3382 * If this is a remote addressbook, copy the file over.
3383 * If it fails we warn but continue to operate on the changed,
3384 * locally cached addressbook file.
3386 if(ab->type == Imap){
3387 int e;
3388 char datebuf[200];
3390 datebuf[0] = '\0';
3391 if(we_cancel)
3392 cancel_busy_cue(-1);
3394 we_cancel = busy_cue(_("Copying to remote addressbook"), NULL, 1);
3396 * We don't want a cookie upgrade to blast our data in rd->lf by
3397 * copying back the remote data before doing the upgrade, and then
3398 * proceeding to finish with that data. So we tell rd_upgrade_cookie
3399 * about that here.
3401 ab->rd->flags |= BELIEVE_CACHE;
3402 if((e = rd_update_remote(ab->rd, datebuf)) != 0){
3403 if(e == -1){
3404 q_status_message2(SM_ORDER | SM_DING, 3, 5,
3405 _("Error opening temporary addrbook file %.200s: %.200s"),
3406 ab->rd->lf, error_description(errno));
3407 dprint((1,
3408 "adrbk_write: error opening temp file %s\n",
3409 ab->rd->lf ? ab->rd->lf : "?"));
3411 else{
3412 q_status_message2(SM_ORDER | SM_DING, 3, 5,
3413 _("Error copying to %.200s: %.200s"),
3414 ab->rd->rn, error_description(errno));
3415 dprint((1,
3416 "adrbk_write: error copying from %s to %s\n",
3417 ab->rd->lf ? ab->rd->lf : "?",
3418 ab->rd->rn ? ab->rd->rn : "?"));
3421 q_status_message(SM_ORDER | SM_DING, 5, 5,
3422 _("Copy of addrbook to remote folder failed, changes NOT saved remotely"));
3424 else{
3425 rd_update_metadata(ab->rd, datebuf);
3426 ab->rd->read_status = 'W';
3427 dprint((7,
3428 "%s: copied local to remote in adrbk_write (%ld)\n",
3429 ab->rd->rn ? ab->rd->rn : "?",
3430 (long)ab->rd->last_use));
3433 ab->rd->flags &= ~BELIEVE_CACHE;
3436 writing = 0;
3438 if(we_cancel)
3439 cancel_busy_cue(0);
3441 if(temp_filename){
3442 our_unlink(temp_filename);
3443 fs_give((void **) &temp_filename);
3446 return 0;
3449 io_error:
3450 if(we_cancel)
3451 cancel_busy_cue(-1);
3453 if(enable_intr_handling && we_turned_on)
3454 intr_handling_off();
3456 #ifndef DOS
3457 (void)signal(SIGHUP, save_sighup);
3458 #endif
3459 if(interrupt_happened){
3460 q_status_message(0, 1, 2, _("Interrupt! Reverting to previous version"));
3461 display_message('x');
3462 dprint((1, "adrbk_write(%s): Interrupt\n",
3463 ab->filename ? ab->filename : "?"));
3465 else
3466 dprint((1, "adrbk_write(%s) (%s): some sort of io_error\n",
3467 ab->filename ? ab->filename : "?",
3468 ab->our_filecopy ? ab->our_filecopy : "?"));
3470 writing = 0;
3472 if(ab_stream != NULL){
3473 fclose(ab_stream);
3474 ab_stream = (FILE *)NULL;
3477 if(temp_filename){
3478 our_unlink(temp_filename);
3479 fs_give((void **) &temp_filename);
3482 adrbk_partial_close(ab);
3484 if(interrupt_happened){
3485 errno = EINTR; /* for nicer error message */
3486 return -5;
3488 else
3489 return -2;
3494 * Writes one addrbook entry with wrapping. Fills in widths
3495 * for display purposes. Returns 0, or EOF on error.
3497 * Continuation lines always start with spaces. Tabs are treated as
3498 * separators, never as whitespace. When we output tab separators we
3499 * always put them on the ends of lines, never on the start of a line
3500 * after a continuation. That is, there is always something printable
3501 * after continuation spaces.
3504 write_single_abook_entry(AdrBk_Entry *ae, FILE *fp, int *ret_nick_width,
3505 int *ret_full_width, int *ret_addr_width, int *ret_fcc_width)
3507 int len = 0;
3508 int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
3509 int tmplen, this_len;
3510 char *write_this = NULL;
3512 if(fp == (FILE *) NULL){
3513 dprint((1, "write_single_abook_entry: fp is NULL\n"));
3514 return(EOF);
3517 if(ae->nickname){
3518 nick_width = utf8_width(ae->nickname);
3520 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3521 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->nickname);
3523 this_len = strlen(write_this ? write_this : "");
3526 * We aren't too concerned with where it wraps.
3527 * Long lines are ok as long as they aren't super long.
3529 if(len > 100 || (len+this_len > 150 && len > 20)){
3530 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3531 dprint((1,
3532 "write_single_abook_entry: fputs ind1 failed\n"));
3533 return(EOF);
3536 len = this_len + INDENT;
3539 len += this_len;
3541 if(fputs(write_this, fp) == EOF){
3542 dprint((1,
3543 "write_single_abook_entry: fputs nick failed\n"));
3544 return(EOF);
3547 else
3548 nick_width = 0;
3550 putc(TAB, fp);
3551 len++;
3553 if(ae->fullname){
3554 full_width = utf8_width(ae->fullname);
3556 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3557 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fullname);
3559 this_len = strlen(write_this ? write_this : "");
3562 if(len > 100 || (len+this_len > 150 && len > 20)){
3563 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3564 dprint((1,
3565 "write_single_abook_entry: fputs ind2 failed\n"));
3566 return(EOF);
3569 len = this_len + INDENT;
3572 len += this_len;
3574 if(fputs(write_this, fp) == EOF){
3575 dprint((1,
3576 "write_single_abook_entry: fputs full failed\n"));
3577 return(EOF);
3580 else
3581 full_width = 0;
3583 putc(TAB, fp);
3584 len++;
3586 /* special case, make sure empty list has () */
3587 if(ae->tag == List && ae->addr.list == NULL){
3588 addr_width = 0;
3589 putc('(', fp);
3590 putc(')', fp);
3591 len += 2;
3593 else if(ae->addr.addr != NULL || ae->addr.list != NULL){
3594 if(ae->tag == Single){
3595 /*----- Single: just one address ----*/
3596 if(ae->addr.addr){
3597 addr_width = utf8_width(ae->addr.addr);
3599 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3600 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->addr.addr);
3602 this_len = strlen(write_this ? write_this : "");
3604 if(len > 100 || (len+this_len > 150 && len > 20)){
3605 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3606 dprint((1,
3607 "write_single_abook_entry: fputs ind3 failed\n"));
3608 return(EOF);
3611 len = this_len + INDENT;
3614 len += this_len;
3616 if(fputs(write_this, fp) == EOF){
3617 dprint((1,
3618 "write_single_abook_entry: fputs addr failed\n"));
3619 return(EOF);
3622 else
3623 addr_width = 0;
3625 else if(ae->tag == List){
3626 register char **a2;
3628 /*----- List: a distribution list ------*/
3629 putc('(', fp);
3630 len++;
3631 addr_width = 0;
3632 for(a2 = ae->addr.list; *a2 != NULL; a2++){
3633 if(*a2){
3634 if(a2 != ae->addr.list){
3635 putc(',', fp);
3636 len++;
3639 addr_width = MAX(addr_width, utf8_width(*a2));
3641 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3642 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, *a2);
3644 this_len = strlen(write_this ? write_this : "");
3646 if(len > 100 || (len+this_len > 150 && len > 20)){
3647 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3648 dprint((1,
3649 "write_single_abook_entry: fputs ind3 failed\n"));
3650 return(EOF);
3653 len = this_len + INDENT;
3656 len += this_len;
3658 if(fputs(write_this, fp) == EOF){
3659 dprint((1,
3660 "write_single_abook_entry: fputs addrl failed\n"));
3661 return(EOF);
3666 putc(')', fp);
3667 len++;
3671 /* If either fcc or extra exists, output both, otherwise, neither */
3672 if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
3673 putc(TAB, fp);
3674 len++;
3676 if(ae->fcc && ae->fcc[0]){
3677 fcc_width = utf8_width(ae->fcc);
3679 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3680 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fcc);
3682 this_len = strlen(write_this ? write_this : "");
3684 if(len > 100 || (len+this_len > 150 && len > 20)){
3685 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3686 dprint((1,
3687 "write_single_abook_entry: fputs ind4 failed\n"));
3688 return(EOF);
3691 len = this_len + INDENT;
3694 len += this_len;
3696 if(fputs(write_this, fp) == EOF){
3697 dprint((1,
3698 "write_single_abook_entry: fputs fcc failed\n"));
3699 return(EOF);
3702 else
3703 fcc_width = 0;
3705 putc(TAB, fp);
3706 len++;
3708 if(ae->extra && ae->extra[0]){
3709 int space;
3710 char *cur, *end;
3711 char *extra_copy;
3713 write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
3714 tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->extra);
3717 * Copy ae->extra and replace newlines with spaces.
3718 * The reason we do this is because the continuation lines
3719 * produced by rfc1522_encode may interact with the
3720 * continuation lines produced below in a bad way.
3721 * In particular, what can happen is that the 1522 continuation
3722 * newline can be followed immediately by a newline produced
3723 * below. That breaks the continuation since that is a
3724 * line with nothing on it. Just turn 1522 continuations into
3725 * spaces, which work fine with 1522_decode.
3727 extra_copy = cpystr(write_this);
3728 REPLACE_NEWLINES_WITH_SPACE(extra_copy);
3730 tmplen = strlen(extra_copy);
3732 if(len > 100 || (len+tmplen > 150 && len > 20)){
3733 if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
3734 dprint((1,
3735 "write_single_abook_entry: fprintf indent failed\n"));
3736 return(EOF);
3739 len = INDENT;
3742 space = MAX(70 - len, 5);
3743 cur = extra_copy;
3744 end = cur + tmplen;
3745 while(cur < end){
3746 if(end-cur > space){
3747 int i;
3749 /* find first space after spot we want to break */
3750 for(i = space; cur+i < end && cur[i] != SPACE; i++)
3753 cur[i] = '\0';
3754 if(fputs(cur, fp) == EOF){
3755 dprint((1,
3756 "write_single_abook_entry: fputs extra failed\n"));
3757 return(EOF);
3760 cur += (i+1);
3762 if(cur < end){
3763 if(fprintf(fp, "%s%s", NEWLINE, INDENTXTRA) == EOF){
3764 dprint((1,
3765 "write_single_abook_entry: fprintf indent failed\n"));
3766 return(EOF);
3769 space = 70 - INDENT;
3772 else{
3773 if(fputs(cur, fp) == EOF){
3774 dprint((1,
3775 "write_single_abook_entry: fputs extra failed\n"));
3776 return(EOF);
3779 cur = end;
3783 if(extra_copy)
3784 fs_give((void **)&extra_copy);
3787 else
3788 fcc_width = 0;
3790 fprintf(fp, "%s", NEWLINE);
3792 if(ret_nick_width)
3793 *ret_nick_width = nick_width;
3794 if(ret_full_width)
3795 *ret_full_width = full_width;
3796 if(ret_addr_width)
3797 *ret_addr_width = addr_width;
3798 if(ret_fcc_width)
3799 *ret_fcc_width = fcc_width;
3801 return(0);
3805 char *
3806 backcompat_encoding_for_abook(char *buf1, size_t buf1len, char *buf2,
3807 size_t buf2len, char *srcstr)
3809 char *encoded = NULL;
3810 char *p;
3811 int its_ascii = 1;
3814 for(p = srcstr; *p && its_ascii; p++)
3815 if(*p & 0x80)
3816 its_ascii = 0;
3818 /* if it is ascii, go with that */
3819 if(its_ascii)
3820 encoded = srcstr;
3821 else{
3822 char *trythischarset = NULL;
3825 * If it is possible to translate the UTF-8
3826 * string into the user's character set then
3827 * do that. For backwards compatibility with
3828 * old pines.
3830 if(ps_global->keyboard_charmap && ps_global->keyboard_charmap[0])
3831 trythischarset = ps_global->keyboard_charmap;
3832 else if(ps_global->display_charmap && ps_global->display_charmap[0])
3833 trythischarset = ps_global->display_charmap;
3835 if(trythischarset){
3836 SIZEDTEXT src, dst;
3838 src.data = (unsigned char *) srcstr;
3839 src.size = strlen(srcstr);
3840 memset(&dst, 0, sizeof(dst));
3841 if(utf8_cstext(&src, trythischarset, &dst, 0)){
3842 if(dst.data){
3843 strncpy(buf1, (char *) dst.data, buf1len);
3844 buf1[buf1len-1] = '\0';
3845 fs_give((void **) &dst.data);
3846 encoded = rfc1522_encode(buf2, buf2len,
3847 (unsigned char *) buf1, trythischarset);
3848 if(encoded)
3849 REPLACE_NEWLINES_WITH_SPACE(encoded);
3854 if(!encoded){
3855 encoded = rfc1522_encode(buf1, buf1len, (unsigned char *) srcstr, "UTF-8");
3856 if(encoded)
3857 REPLACE_NEWLINES_WITH_SPACE(encoded);
3861 return(encoded);
3866 percent_abook_saved(void)
3868 return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
3869 (unsigned long)tot_for_percent));
3874 * Free memory associated with entry ae.
3876 * Args: ae -- Address book entry to be freed.
3878 void
3879 free_ae(AdrBk_Entry **ae)
3881 if(!ae || !(*ae))
3882 return;
3884 free_ae_parts(*ae);
3885 fs_give((void **) ae);
3890 * Free memory associated with entry ae but not ae itself.
3892 void
3893 free_ae_parts(AdrBk_Entry *ae)
3895 char **p;
3897 if(!ae)
3898 return;
3900 if(ae->nickname && ae->nickname != empty)
3901 fs_give((void **) &ae->nickname);
3903 if(ae->fullname && ae->fullname != empty)
3904 fs_give((void **) &ae->fullname);
3906 if(ae->tag == Single){
3907 if(ae->addr.addr && ae->addr.addr != empty)
3908 fs_give((void **) &ae->addr.addr);
3910 else if(ae->tag == List){
3911 if(ae->addr.list){
3912 for(p = ae->addr.list; *p; p++)
3913 if(*p != empty)
3914 fs_give((void **) p);
3916 fs_give((void **) &ae->addr.list);
3920 if(ae->fcc && ae->fcc != empty)
3921 fs_give((void **) &ae->fcc);
3923 if(ae->extra && ae->extra != empty)
3924 fs_give((void **) &ae->extra);
3926 defvalue_ae(ae);
3931 * Inserts element new_ae before element put_it_before_this.
3933 void
3934 insert_ab_entry(AdrBk *ab, a_c_arg_t put_it_before_this, AdrBk_Entry *new_ae,
3935 int use_deleted_list)
3937 adrbk_cntr_t before, old_count, new_count;
3938 AdrBk_Entry *ae_before, *ae_before_next;
3940 dprint((7, "- insert_ab_entry(before_this=%ld) -\n", (long) put_it_before_this));
3942 before = (adrbk_cntr_t) put_it_before_this;
3944 if(!ab || before == NO_NEXT || (!use_deleted_list && before > ab->count)
3945 || (use_deleted_list && before > ab->del_count)){
3948 else{
3950 * add space for new entry to array
3951 * slide entries [before ... old_count-1] down one
3952 * put new_ae into opened up slot
3954 old_count = use_deleted_list ? ab->del_count : ab->count;
3955 new_count = old_count + 1;
3956 if(old_count == 0){
3957 if(use_deleted_list){
3958 if(ab->del) /* shouldn't happen */
3959 fs_give((void **) &ab->del);
3961 /* first entry in new array */
3962 ab->del = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3963 defvalue_ae(ab->del);
3965 else{
3966 if(ab->arr) /* shouldn't happen */
3967 fs_give((void **) &ab->arr);
3969 /* first entry in new array */
3970 ab->arr = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
3971 defvalue_ae(ab->arr);
3974 else{
3975 if(use_deleted_list){
3976 fs_resize((void **) &ab->del, new_count * sizeof(AdrBk_Entry));
3977 defvalue_ae(&ab->del[new_count-1]);
3979 else{
3980 fs_resize((void **) &ab->arr, new_count * sizeof(AdrBk_Entry));
3981 defvalue_ae(&ab->arr[new_count-1]);
3985 if(use_deleted_list){
3986 ab->del_count = new_count;
3988 ae_before = adrbk_get_delae(ab, (a_c_arg_t) before);
3989 if(ae_before){
3990 if(before < old_count){
3991 ae_before_next = adrbk_get_delae(ab, (a_c_arg_t) (before+1));
3992 memmove(ae_before_next, ae_before,
3993 (old_count-before) * sizeof(AdrBk_Entry));
3996 memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
3999 else{
4000 ab->count = new_count;
4002 ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4003 if(ae_before){
4004 if(before < old_count){
4005 ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4006 memmove(ae_before_next, ae_before,
4007 (old_count-before) * sizeof(AdrBk_Entry));
4010 memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
4016 if(!use_deleted_list)
4017 repair_abook_tries(ab);
4022 * Moves element move_this_one before element put_it_before_this.
4024 void
4025 move_ab_entry(AdrBk *ab, a_c_arg_t move_this_one, a_c_arg_t put_it_before_this)
4027 adrbk_cntr_t m, before;
4028 AdrBk_Entry ae_tmp;
4029 AdrBk_Entry *ae_m, *ae_m_next, *ae_before, *ae_before_prev, *ae_before_next;
4031 dprint((7, "- move_ab_entry(move_this=%ld,before_this=%ld) -\n", (long) move_this_one, (long) put_it_before_this));
4033 m = (adrbk_cntr_t) move_this_one;
4034 before = (adrbk_cntr_t) put_it_before_this;
4036 if(!ab || m == NO_NEXT || before == NO_NEXT
4037 || m == before || m+1 == before || before > ab->count){
4040 else if(m+1 < before){
4042 * copy m
4043 * slide entries [m+1 ... before-1] up one ("up" means smaller indices)
4044 * put m into opened up slot
4046 ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
4047 ae_m_next = adrbk_get_ae(ab, (a_c_arg_t) (m+1));
4048 ae_before_prev = adrbk_get_ae(ab, (a_c_arg_t) (before-1));
4049 if(ae_m && ae_m_next && ae_before_prev){
4050 memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4051 memmove(ae_m, ae_m_next, (before-m-1) * sizeof(ae_tmp));
4052 memcpy(ae_before_prev, &ae_tmp, sizeof(ae_tmp));
4055 else if(m > before){
4057 * copy m
4058 * slide entries [before ... m-1] down one
4059 * put m into opened up slot
4061 ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
4062 ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
4063 ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
4064 if(ae_m && ae_before && ae_before_next){
4065 memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
4066 memmove(ae_before_next, ae_before, (m-before) * sizeof(ae_tmp));
4067 memcpy(ae_before, &ae_tmp, sizeof(ae_tmp));
4071 dprint((9, "- move_ab_entry: done -\n"));
4073 repair_abook_tries(ab);
4078 * Deletes element delete_this_one from in-core data structure.
4079 * If save_it is set the deleted entry is moved to the deleted_list.
4081 void
4082 delete_ab_entry(AdrBk *ab, a_c_arg_t delete_this_one, int save_it)
4084 adrbk_cntr_t d;
4085 AdrBk_Entry *ae_deleted, *ae_deleted_next;
4087 dprint((7, "- delete_ab_entry(delete_this=%ld,save_it=%d) -\n", (long) delete_this_one, save_it));
4089 d = (adrbk_cntr_t) delete_this_one;
4091 if(!ab || d == NO_NEXT || d >= ab->count){
4094 else{
4096 * Move the entry to the deleted_list if asked to.
4098 ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
4099 if(ae_deleted){
4100 if(save_it){
4101 char *oldnick, *newnick;
4102 size_t len;
4103 struct tm *tm_now;
4104 time_t now;
4107 * First prepend the prefix
4108 * #DELETED-YY/MM/DD#
4109 * to the nickname.
4111 now = time((time_t *) NULL);
4112 tm_now = localtime(&now);
4114 oldnick = ae_deleted->nickname;
4115 len = strlen(oldnick) + DELETED_LEN + strlen("YY/MM/DD#");
4117 newnick = (char *) fs_get((len+1) * sizeof(char));
4118 snprintf(newnick, len+1, "%s%02d/%02d/%02d#%s",
4119 DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1,
4120 tm_now->tm_mday, oldnick ? oldnick : "");
4121 newnick[len] = '\0';
4123 if(ae_deleted->nickname && ae_deleted->nickname != empty)
4124 fs_give((void **) &ae_deleted->nickname);
4126 ae_deleted->nickname = newnick;
4129 * Now insert this entry in the deleted_list.
4131 insert_ab_entry(ab, (a_c_arg_t) ab->del_count, ae_deleted, 1);
4133 else
4134 free_ae_parts(ae_deleted);
4138 * slide entries [deleted+1 ... count-1] up one
4140 if(d+1 < ab->count){
4141 ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
4142 ae_deleted_next = adrbk_get_ae(ab, (a_c_arg_t) (d+1));
4143 if(ae_deleted && ae_deleted_next)
4144 memmove(ae_deleted, ae_deleted_next, (ab->count-d-1) * sizeof(AdrBk_Entry));
4147 ab->count--;
4149 if(ab->count > 0)
4150 fs_resize((void **) &ab->arr, ab->count * sizeof(AdrBk_Entry));
4151 else
4152 fs_give((void **) &ab->arr);
4155 repair_abook_tries(ab);
4160 * We may want to be smarter about this and repair instead of
4161 * rebuild these.
4163 void
4164 repair_abook_tries(AdrBk *ab)
4166 if(ab->arr){
4167 AdrBk_Trie *save_nick_trie, *save_addr_trie,
4168 *save_full_trie, *save_revfull_trie;
4170 save_nick_trie = ab->nick_trie;
4171 ab->nick_trie = NULL;
4172 save_addr_trie = ab->addr_trie;
4173 ab->addr_trie = NULL;
4174 save_full_trie = ab->full_trie;
4175 ab->full_trie = NULL;
4176 save_revfull_trie = ab->revfull_trie;
4177 ab->revfull_trie = NULL;
4178 if(build_abook_tries(ab, NULL)){
4179 dprint((2, "trouble rebuilding tries, restoring\n"));
4180 if(ab->nick_trie)
4181 free_abook_trie(&ab->nick_trie);
4183 /* better than nothing */
4184 ab->nick_trie = save_nick_trie;
4186 if(ab->addr_trie)
4187 free_abook_trie(&ab->addr_trie);
4189 ab->addr_trie = save_addr_trie;
4191 if(ab->full_trie)
4192 free_abook_trie(&ab->full_trie);
4194 ab->full_trie = save_full_trie;
4196 if(ab->revfull_trie)
4197 free_abook_trie(&ab->revfull_trie);
4199 ab->revfull_trie = save_revfull_trie;
4201 else{
4202 if(save_nick_trie)
4203 free_abook_trie(&save_nick_trie);
4205 if(save_addr_trie)
4206 free_abook_trie(&save_addr_trie);
4208 if(save_full_trie)
4209 free_abook_trie(&save_full_trie);
4211 if(save_revfull_trie)
4212 free_abook_trie(&save_revfull_trie);
4219 * Free the list of distribution lists which have been expanded.
4220 * Leaves the head of the list alone.
4222 * Args: exp_head -- Head of the expanded list.
4224 void
4225 exp_free(EXPANDED_S *exp_head)
4227 EXPANDED_S *e, *the_next_one;
4229 e = exp_head ? exp_head->next : NULL;
4231 if(!e)
4232 return;
4234 while(e){
4235 the_next_one = e->next;
4236 fs_give((void **)&e);
4237 e = the_next_one;
4240 exp_head->next = (EXPANDED_S *)NULL;
4245 * Is entry n expanded?
4247 * Args: exp_head -- Head of the expanded list.
4248 * n -- The entry num to check
4251 exp_is_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4253 register EXPANDED_S *e;
4254 adrbk_cntr_t nn;
4256 nn = (adrbk_cntr_t)n;
4258 e = exp_head ? exp_head->next : NULL;
4261 * The list is kept ordered, so we search until we find it or are
4262 * past it.
4264 while(e){
4265 if(e->ent >= nn)
4266 break;
4268 e = e->next;
4271 return(e && e->ent == nn);
4276 * How many entries expanded in this addrbook.
4278 * Args: exp_head -- Head of the expanded list.
4281 exp_howmany_expanded(EXPANDED_S *exp_head)
4283 register EXPANDED_S *e;
4284 int cnt = 0;
4286 e = exp_head ? exp_head->next : NULL;
4288 while(e){
4289 cnt++;
4290 e = e->next;
4293 return(cnt);
4298 * Are any entries expanded?
4300 * Args: exp_head -- Head of the expanded list.
4303 exp_any_expanded(EXPANDED_S *exp_head)
4305 return(exp_head && exp_head->next != NULL);
4310 * Return next entry num in list.
4312 * Args: cur -- Current position in the list.
4314 * Result: Returns the number of the next entry, or NO_NEXT if there is
4315 * no next entry. As a side effect, the cur pointer is incremented.
4317 adrbk_cntr_t
4318 exp_get_next(EXPANDED_S **cur)
4320 adrbk_cntr_t ret = NO_NEXT;
4322 if(cur && *cur && (*cur)->next){
4323 ret = (*cur)->next->ent;
4324 *cur = (*cur)->next;
4327 return(ret);
4332 * Mark entry n as being expanded.
4334 * Args: exp_head -- Head of the expanded list.
4335 * n -- The entry num to mark
4337 void
4338 exp_set_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4340 register EXPANDED_S *e;
4341 EXPANDED_S *new;
4342 adrbk_cntr_t nn;
4344 nn = (adrbk_cntr_t)n;
4345 if(!exp_head)
4346 alpine_panic("exp_head not set in exp_set_expanded");
4348 for(e = exp_head; e->next; e = e->next)
4349 if(e->next->ent >= nn)
4350 break;
4352 if(e->next && e->next->ent == nn) /* already there */
4353 return;
4355 /* add new after e */
4356 new = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
4357 new->ent = nn;
4358 new->next = e->next;
4359 e->next = new;
4364 * Mark entry n as being *not* expanded.
4366 * Args: exp_head -- Head of the expanded list.
4367 * n -- The entry num to mark
4369 void
4370 exp_unset_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
4372 register EXPANDED_S *e;
4373 EXPANDED_S *delete_this_one = NULL;
4374 adrbk_cntr_t nn;
4376 nn = (adrbk_cntr_t)n;
4377 if(!exp_head)
4378 alpine_panic("exp_head not set in exp_unset_expanded");
4380 for(e = exp_head; e->next; e = e->next)
4381 if(e->next->ent >= nn)
4382 break;
4384 if(e->next && e->next->ent == nn){
4385 delete_this_one = e->next;
4386 e->next = e->next->next;
4389 if(delete_this_one)
4390 fs_give((void **)&delete_this_one);
4395 * Adjust the "expanded" list to correspond to addrbook entry n being
4396 * deleted.
4398 * Args: exp_head -- Head of the expanded list.
4399 * n -- The entry num being deleted
4401 void
4402 exp_del_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4404 register EXPANDED_S *e;
4405 int delete_when_done = 0;
4406 adrbk_cntr_t nn;
4408 nn = (adrbk_cntr_t)n;
4409 if(!exp_head)
4410 alpine_panic("exp_head not set in exp_del_nth");
4412 e = exp_head->next;
4413 while(e && e->ent < nn)
4414 e = e->next;
4416 if(e){
4417 if(e->ent == nn){
4418 delete_when_done++;
4419 e = e->next;
4422 while(e){
4423 e->ent--; /* adjust entry nums */
4424 e = e->next;
4427 if(delete_when_done)
4428 exp_unset_expanded(exp_head, n);
4434 * Adjust the "expanded" list to correspond to a new addrbook entry being
4435 * added between current entries n-1 and n.
4437 * Args: exp_head -- Head of the expanded list.
4438 * n -- The entry num being added
4440 * The new entry is not marked expanded.
4442 void
4443 exp_add_nth(EXPANDED_S *exp_head, a_c_arg_t n)
4445 register EXPANDED_S *e;
4446 adrbk_cntr_t nn;
4448 nn = (adrbk_cntr_t)n;
4449 if(!exp_head)
4450 alpine_panic("exp_head not set in exp_add_nth");
4452 e = exp_head->next;
4453 while(e && e->ent < nn)
4454 e = e->next;
4456 while(e){
4457 e->ent++; /* adjust entry nums */
4458 e = e->next;
4463 static AdrBk *ab_for_sort;
4466 * Compare two address book entries. Args are AdrBk_Entry **'s.
4467 * Sorts lists after simple addresses and then sorts on Fullname field.
4470 cmp_ae_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4472 AdrBk_Entry **x = (AdrBk_Entry **)a,
4473 **y = (AdrBk_Entry **)b;
4474 int result;
4476 if((*x)->tag == List && (*y)->tag == Single)
4477 result = 1;
4478 else if((*x)->tag == Single && (*y)->tag == List)
4479 result = -1;
4480 else{
4481 register char *p, *q, *r, *s;
4483 p = (*x)->fullname;
4484 if(*p == '"' && *(p+1))
4485 p++;
4487 q = (*y)->fullname;
4488 if(*q == '"' && *(q+1))
4489 q++;
4491 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4492 SIZEOF_20KBUF, p);
4494 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4495 SIZEOF_20KBUF-10000, q);
4497 result = (*pcollator)(r, s);
4498 if(result == 0)
4499 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4502 return(result);
4507 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4508 * Sorts lists after simple addresses and then sorts on Fullname field.
4511 cmp_cntr_by_full_lists_last(const qsort_t *a, const qsort_t *b)
4513 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4514 *y = (adrbk_cntr_t *)b;
4515 AdrBk_Entry *x_ae,
4516 *y_ae;
4518 if(ps_global->intr_pending)
4519 longjmp(jump_over_qsort, 1);
4521 ALARM_BLIP();
4523 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4524 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4526 return(cmp_ae_by_full_lists_last((const qsort_t *) &x_ae,
4527 (const qsort_t *) &y_ae));
4532 * Compare two address book entries. Args are AdrBk_Entry **'s.
4533 * Sorts on Fullname field.
4536 cmp_ae_by_full(const qsort_t *a, const qsort_t *b)
4538 AdrBk_Entry **x = (AdrBk_Entry **)a,
4539 **y = (AdrBk_Entry **)b;
4540 int result;
4541 register char *p, *q, *r, *s;
4543 p = (*x)->fullname;
4544 if(*p == '"' && *(p+1))
4545 p++;
4547 q = (*y)->fullname;
4548 if(*q == '"' && *(q+1))
4549 q++;
4551 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4552 SIZEOF_20KBUF, p);
4553 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4554 SIZEOF_20KBUF-10000, q);
4555 result = (*pcollator)(r, s);
4556 if(result == 0)
4557 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4559 return(result);
4564 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4565 * Sorts on Fullname field.
4568 cmp_cntr_by_full(const qsort_t *a, const qsort_t *b)
4570 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4571 *y = (adrbk_cntr_t *)b;
4572 AdrBk_Entry *x_ae,
4573 *y_ae;
4575 if(ps_global->intr_pending)
4576 longjmp(jump_over_qsort, 1);
4578 ALARM_BLIP();
4580 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4581 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4583 return(cmp_ae_by_full((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4588 * Compare two address book entries. Args are AdrBk_Entry **'s.
4589 * Sorts lists after simple addresses and then sorts on Nickname field.
4592 cmp_ae_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4594 AdrBk_Entry **x = (AdrBk_Entry **)a,
4595 **y = (AdrBk_Entry **)b;
4596 int result;
4598 if((*x)->tag == List && (*y)->tag == Single)
4599 result = 1;
4600 else if((*x)->tag == Single && (*y)->tag == List)
4601 result = -1;
4602 else
4603 result = (*pcollator)((*x)->nickname, (*y)->nickname);
4605 return(result);
4610 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4611 * Sorts lists after simple addresses and then sorts on Nickname field.
4614 cmp_cntr_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
4616 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4617 *y = (adrbk_cntr_t *)b;
4618 AdrBk_Entry *x_ae,
4619 *y_ae;
4621 if(ps_global->intr_pending)
4622 longjmp(jump_over_qsort, 1);
4624 ALARM_BLIP();
4626 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4627 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4629 return(cmp_ae_by_nick_lists_last((const qsort_t *) &x_ae,
4630 (const qsort_t *) &y_ae));
4635 * Compare two address book entries. Args are AdrBk_Entry **'s.
4636 * Sorts on Nickname field.
4639 cmp_ae_by_nick(const qsort_t *a, const qsort_t *b)
4641 AdrBk_Entry **x = (AdrBk_Entry **)a,
4642 **y = (AdrBk_Entry **)b;
4644 return((*pcollator)((*x)->nickname, (*y)->nickname));
4649 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4650 * Sorts on Nickname field.
4653 cmp_cntr_by_nick(const qsort_t *a, const qsort_t *b)
4655 adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
4656 *y = (adrbk_cntr_t *)b;
4657 AdrBk_Entry *x_ae,
4658 *y_ae;
4660 if(ps_global->intr_pending)
4661 longjmp(jump_over_qsort, 1);
4663 ALARM_BLIP();
4665 x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
4666 y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
4668 return(cmp_ae_by_nick((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
4673 * For sorting a simple list of pointers to addresses (skip initial quotes)
4676 cmp_addr(const qsort_t *a1, const qsort_t *a2)
4678 char *x = *(char **)a1, *y = *(char **)a2;
4679 char *r, *s;
4681 if(x && *x == '"')
4682 x++;
4684 if(y && *y == '"')
4685 y++;
4687 r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
4688 SIZEOF_20KBUF, x);
4689 s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
4690 SIZEOF_20KBUF-10000, y);
4691 return((*pcollator)(r, s));
4696 * Sort an array of strings, except skip initial quotes.
4698 void
4699 sort_addr_list(char **list)
4701 register char **p;
4703 /* find size of list */
4704 for(p = list; *p != NULL; p++)
4705 ;/* do nothing */
4707 qsort((qsort_t *)list, (size_t)(p - list), sizeof(char *), cmp_addr);
4712 * Sort this address book.
4714 * Args: ab -- address book to sort
4715 * current_entry_num -- see next description
4716 * new_entry_num -- return new entry_num of current_entry_num here
4718 * Result: return code: 0 all went well
4719 * -2 error writing address book, check errno
4721 * The sorting strategy is to allocate an array of length ab->count which
4722 * contains the element numbers 0, 1, ..., ab->count - 1, representing the
4723 * entries in the addrbook, of course. Sort the array, then use that
4724 * result to swap ae's in the in-core addrbook array before writing it out.
4727 adrbk_sort(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num, int be_quiet)
4729 adrbk_cntr_t *sort_array, *inv, tmp;
4730 long i, j, hi, count;
4731 int skip_the_sort = 0, we_cancel = 0, we_turned_on = 0;
4732 AdrBk_Entry ae_tmp, *ae_i, *ae_hi;
4733 EXPANDED_S *e, *e2, *smallest;
4735 dprint((5, "- adrbk_sort -\n"));
4737 count = (long) (ab->count);
4739 if(!ab)
4740 return -2;
4742 if(ab->sort_rule == AB_SORT_RULE_NONE)
4743 return 0;
4745 if(count < 2)
4746 return 0;
4748 sort_array = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4749 inv = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
4751 for(i = 0L; i < count; i++)
4752 sort_array[i] = (adrbk_cntr_t) i;
4754 ab_for_sort = ab;
4756 if(setjmp(jump_over_qsort))
4757 skip_the_sort = 1;
4759 if(!skip_the_sort){
4760 we_turned_on = intr_handling_on();
4761 if(!be_quiet)
4762 we_cancel = busy_cue(_("Sorting address book"), NULL, 0);
4764 qsort((qsort_t *)sort_array,
4765 (size_t)count,
4766 sizeof(adrbk_cntr_t),
4767 (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
4768 cmp_cntr_by_full_lists_last :
4769 (ab->sort_rule == AB_SORT_RULE_FULL) ?
4770 cmp_cntr_by_full :
4771 (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
4772 cmp_cntr_by_nick_lists_last :
4773 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
4774 cmp_cntr_by_nick);
4777 dprint((9, "- adrbk_sort: done with first sort -\n"));
4779 if(we_turned_on)
4780 intr_handling_off();
4782 if(skip_the_sort){
4783 q_status_message(SM_ORDER, 3, 3,
4784 _("Address book sort cancelled, using old order for now"));
4785 goto skip_the_write_too;
4788 dprint((5, "- adrbk_sort (%s)\n",
4789 ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
4790 ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
4791 ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
4792 ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
4795 * Rearrange the in-core array of ae's to be in the new order.
4796 * We can do that by sorting the inverse into the correct order in parallel.
4798 for(i = 0L; i < count; i++)
4799 inv[sort_array[i]] = i;
4801 if(new_entry_num && (adrbk_cntr_t) current_entry_num >= 0
4802 && (adrbk_cntr_t) current_entry_num < count){
4803 *new_entry_num = inv[(adrbk_cntr_t) current_entry_num];
4807 * The expanded and selected lists will be wrong now. Correct them.
4808 * First the expanded list.
4810 e = ab->exp ? ab->exp->next : NULL;
4811 while(e){
4812 if(e->ent >= 0 && e->ent < count)
4813 e->ent = inv[e->ent];
4815 e = e->next;
4819 * And sort into ascending order as expected by the exp_ routines.
4821 e = ab->exp ? ab->exp->next : NULL;
4822 while(e){
4823 /* move smallest to e */
4824 e2 = e;
4825 smallest = e;
4826 while(e2){
4827 if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4828 smallest = e2;
4830 e2 = e2->next;
4833 /* swap values in e and smallest */
4834 if(e != smallest){
4835 tmp = e->ent;
4836 e->ent = smallest->ent;
4837 smallest->ent = tmp;
4840 e = e->next;
4844 * Same thing for the selected list.
4846 e = ab->selects ? ab->selects->next : NULL;
4847 while(e){
4848 if(e->ent >= 0 && e->ent < count)
4849 e->ent = inv[e->ent];
4851 e = e->next;
4854 e = ab->selects ? ab->selects->next : NULL;
4855 while(e){
4856 /* move smallest to e */
4857 e2 = e;
4858 smallest = e;
4859 while(e2){
4860 if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
4861 smallest = e2;
4863 e2 = e2->next;
4866 /* swap values in e and smallest */
4867 if(e != smallest){
4868 tmp = e->ent;
4869 e->ent = smallest->ent;
4870 smallest->ent = tmp;
4873 e = e->next;
4876 for(i = 0L; i < count; i++){
4877 if(i != inv[i]){
4878 /* find inv[j] which = i */
4879 for(j = i+1; j < count; j++){
4880 if(i == inv[j]){
4881 hi = j;
4882 break;
4886 /* swap i and hi */
4887 ae_i = adrbk_get_ae(ab, (a_c_arg_t) i);
4888 ae_hi = adrbk_get_ae(ab, (a_c_arg_t) hi);
4889 if(ae_i && ae_hi){
4890 memcpy(&ae_tmp, ae_i, sizeof(ae_tmp));
4891 memcpy(ae_i, ae_hi, sizeof(ae_tmp));
4892 memcpy(ae_hi, &ae_tmp, sizeof(ae_tmp));
4894 /* else can't happen */
4896 inv[hi] = inv[i];
4900 if(we_cancel)
4901 cancel_busy_cue(0);
4903 repair_abook_tries(ab);
4905 dprint((9, "- adrbk_sort: done with rearranging -\n"));
4907 skip_the_write_too:
4908 if(we_cancel)
4909 cancel_busy_cue(0);
4911 if(sort_array)
4912 fs_give((void **) &sort_array);
4914 if(inv)
4915 fs_give((void **) &inv);
4917 return 0;
4922 * Returns 1 if any addrbooks are in Open state, 0 otherwise.
4924 * This is a test for ostatus == Open, not for whether or not the address book
4925 * FILE is opened.
4928 any_ab_open(void)
4930 int i, ret = 0;
4932 if(as.initialized)
4933 for(i = 0; ret == 0 && i < as.n_addrbk; i++)
4934 if(as.adrbks[i].ostatus == Open)
4935 ret++;
4937 return(ret);
4942 * Make sure addrbooks are minimally initialized.
4944 void
4945 init_ab_if_needed(void)
4947 dprint((9, "- init_ab_if_needed -\n"));
4949 if(!as.initialized)
4950 (void)init_addrbooks(Closed, 0, 0, 1);
4955 * Sets everything up to get started.
4957 * Args: want_status -- The desired OpenStatus for all addrbooks.
4958 * reset_to_top -- Forget about the old location and put cursor
4959 * at top.
4960 * open_if_only_one -- If want_status is HalfOpen and there is only
4961 * section to look at, then promote want_status
4962 * to Open.
4963 * ro_warning -- Set ReadOnly warning global
4965 * Return: 1 if ok, 0 if problem
4968 init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, int ro_warning)
4970 register PerAddrBook *pab;
4971 char *q, **t;
4972 long line;
4974 dprint((4, "-- init_addrbooks(%s, %d, %d, %d) --\n",
4975 want_status==Open ?
4976 "Open" :
4977 want_status==HalfOpen ?
4978 "HalfOpen" :
4979 want_status==ThreeQuartOpen ?
4980 "ThreeQuartOpen" :
4981 want_status==NoDisplay ?
4982 "NoDisplay" :
4983 "Closed",
4984 reset_to_top, open_if_only_one, ro_warning));
4986 as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
4987 - HEADER_ROWS(ps_global);
4988 if(as.l_p_page <= 0)
4989 as.no_op_possbl++;
4990 else
4991 as.no_op_possbl = 0;
4993 as.ro_warning = ro_warning;
4995 /* already been initialized */
4996 if(as.initialized){
4997 int i;
5000 * Special case. If there is only one addressbook we start the
5001 * user out with that open.
5003 if(want_status == HalfOpen &&
5004 ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5005 (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5006 F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5007 want_status = Open;
5009 /* open to correct state */
5010 for(i = 0; i < as.n_addrbk; i++)
5011 init_abook(&as.adrbks[i], want_status);
5013 if(reset_to_top){
5014 warp_to_beginning();
5015 as.top_ent = 0L;
5016 line = first_selectable_line(0L);
5017 if(line == NO_LINE)
5018 as.cur_row = 0L;
5019 else
5020 as.cur_row = line;
5022 if(as.cur_row >= as.l_p_page)
5023 as.top_ent += (as.cur_row - as.l_p_page + 1);
5025 as.old_cur_row = as.cur_row;
5028 dprint((4, "init_addrbooks: already initialized: %d books\n",
5029 as.n_addrbk));
5030 return((as.n_addrbk + as.n_serv) ? 1 : 0);
5033 as.initialized = 1;
5035 /* count directory servers */
5036 as.n_serv = 0;
5037 as.n_impl = 0;
5038 #ifdef ENABLE_LDAP
5039 if(ps_global->VAR_LDAP_SERVERS &&
5040 ps_global->VAR_LDAP_SERVERS[0] &&
5041 ps_global->VAR_LDAP_SERVERS[0][0])
5042 for(t = ps_global->VAR_LDAP_SERVERS; t[0] && t[0][0]; t++){
5043 LDAP_SERV_S *info;
5045 as.n_serv++;
5046 info = break_up_ldap_server(*t);
5047 as.n_impl += (info && info->impl) ? 1 : 0;
5048 if(info)
5049 free_ldap_server_info(&info);
5051 #endif /* ENABLE_LDAP */
5053 /* count addressbooks */
5054 as.how_many_personals = 0;
5055 if(ps_global->VAR_ADDRESSBOOK &&
5056 ps_global->VAR_ADDRESSBOOK[0] &&
5057 ps_global->VAR_ADDRESSBOOK[0][0])
5058 for(t = ps_global->VAR_ADDRESSBOOK; t[0] && t[0][0]; t++)
5059 as.how_many_personals++;
5061 as.n_addrbk = as.how_many_personals;
5062 if(ps_global->VAR_GLOB_ADDRBOOK &&
5063 ps_global->VAR_GLOB_ADDRBOOK[0] &&
5064 ps_global->VAR_GLOB_ADDRBOOK[0][0])
5065 for(t = ps_global->VAR_GLOB_ADDRBOOK; t[0] && t[0][0]; t++)
5066 as.n_addrbk++;
5068 if(want_status == HalfOpen &&
5069 ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
5070 (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
5071 F_ON(F_CMBND_ABOOK_DISP, ps_global))))
5072 want_status = Open;
5076 * allocate array of PerAddrBooks
5077 * (we don't give this up until we exit Pine, but it's small)
5079 if(as.n_addrbk){
5080 as.adrbks = (PerAddrBook *)fs_get(as.n_addrbk * sizeof(PerAddrBook));
5081 memset((void *)as.adrbks, 0, as.n_addrbk * sizeof(PerAddrBook));
5083 /* init PerAddrBook data */
5084 for(as.cur = 0; as.cur < as.n_addrbk; as.cur++){
5085 char *nickname = NULL,
5086 *filename = NULL;
5088 if(as.cur < as.how_many_personals)
5089 q = ps_global->VAR_ADDRESSBOOK[as.cur];
5090 else
5091 q = ps_global->VAR_GLOB_ADDRBOOK[as.cur - as.how_many_personals];
5093 pab = &as.adrbks[as.cur];
5095 /* Parse entry for optional nickname and filename */
5096 get_pair(q, &nickname, &filename, 0, 0);
5098 if(nickname && !*nickname)
5099 fs_give((void **)&nickname);
5101 strncpy(tmp_20k_buf, filename, SIZEOF_20KBUF);
5102 fs_give((void **)&filename);
5104 filename = tmp_20k_buf;
5105 if(nickname == NULL)
5106 pab->abnick = cpystr(filename);
5107 else
5108 pab->abnick = nickname;
5110 if(*filename == '~')
5111 fnexpand(filename, SIZEOF_20KBUF);
5113 if(*filename == '{' || is_absolute_path(filename)){
5114 pab->filename = cpystr(filename); /* fully qualified */
5116 else{
5117 char book_path[MAXPATH+1];
5118 char *lc = last_cmpnt(ps_global->pinerc);
5120 book_path[0] = '\0';
5121 if(lc != NULL){
5122 strncpy(book_path, ps_global->pinerc,
5123 MIN(lc - ps_global->pinerc, sizeof(book_path)-1));
5124 book_path[MIN(lc - ps_global->pinerc,
5125 sizeof(book_path)-1)] = '\0';
5128 strncat(book_path, filename,
5129 sizeof(book_path)-1-strlen(book_path));
5130 pab->filename = cpystr(book_path);
5133 if(*pab->filename == '{')
5134 pab->type |= REMOTE_VIA_IMAP;
5136 if(as.cur >= as.how_many_personals)
5137 pab->type |= GLOBAL;
5139 pab->access = adrbk_access(pab);
5141 /* global address books are forced readonly */
5142 if(pab->type & GLOBAL && pab->access != NoAccess)
5143 pab->access = ReadOnly;
5145 pab->ostatus = TotallyClosed;
5148 * and remember that the memset above initializes everything
5149 * else to 0
5152 init_abook(pab, want_status);
5157 * Have to reset_to_top in this case since this is the first open,
5158 * regardless of the value of the argument, since these values haven't been
5159 * set before here.
5161 as.cur = 0;
5162 as.top_ent = 0L;
5163 warp_to_beginning();
5164 line = first_selectable_line(0L);
5166 if(line == NO_LINE)
5167 as.cur_row = 0L;
5168 else
5169 as.cur_row = line;
5171 if(as.cur_row >= as.l_p_page){
5172 as.top_ent += (as.cur_row - as.l_p_page + 1);
5173 as.cur_row = as.l_p_page - 1;
5176 as.old_cur_row = as.cur_row;
5178 return((as.n_addrbk + as.n_serv) ? 1 : 0);
5183 * Something was changed in options screen, so need to start over.
5185 void
5186 addrbook_reset(void)
5188 dprint((4, "- addrbook_reset -\n"));
5189 completely_done_with_adrbks();
5194 * Sort was changed in options screen. Since we only sort normally
5195 * when we actually make a change to the address book, we need to
5196 * go out of our way to sort here.
5198 void
5199 addrbook_redo_sorts(void)
5201 int i;
5202 PerAddrBook *pab;
5203 AdrBk *ab;
5205 dprint((4, "- addrbook_redo_sorts -\n"));
5207 addrbook_reset();
5208 init_ab_if_needed();
5210 for(i = 0; i < as.n_addrbk; i++){
5211 pab = &as.adrbks[i];
5212 init_abook(pab, NoDisplay);
5213 ab = pab->address_book;
5215 if(!adrbk_is_in_sort_order(ab, 0))
5216 adrbk_write(ab, 0, NULL, NULL, 1, 0);
5219 addrbook_reset();
5224 * Returns type of access allowed on this addrbook.
5226 AccessType
5227 adrbk_access(PerAddrBook *pab)
5229 char fbuf[MAXPATH+1];
5230 AccessType access = NoExists;
5231 CONTEXT_S *dummy_cntxt = NULL;
5233 dprint((9, "- addrbook_access -\n"));
5235 if(pab && pab->type & REMOTE_VIA_IMAP){
5237 * Open_fcc creates the folder if it didn't already exist.
5239 if((pab->so = open_fcc(pab->filename, &dummy_cntxt, 1,
5240 "Error: ",
5241 " Can't fetch remote addrbook.")) != NULL){
5243 * We know the folder is there but don't know what access
5244 * rights we have until we try to select it, which we don't
5245 * want to do unless we have to. So delay evaluating.
5247 access = MaybeRorW;
5250 else if(pab){ /* local file */
5251 #if defined(NO_LOCAL_ADDRBOOKS)
5252 /* don't allow any access to local addrbooks */
5253 access = NoAccess;
5254 #else /* !NO_LOCAL_ADDRBOOKS) */
5255 build_path(fbuf, is_absolute_path(pab->filename) ? NULL
5256 : ps_global->home_dir,
5257 pab->filename, sizeof(fbuf));
5259 #if defined(DOS) || defined(OS2)
5261 * Microsoft networking causes some access calls to do a DNS query (!!)
5262 * when it is turned on. In particular, if there is a / in the filename
5263 * this seems to happen. So, just don't allow it.
5265 if(strindex(fbuf, '/') != NULL){
5266 dprint((2, "\"/\" not allowed in addrbook name\n"));
5267 return NoAccess;
5269 #else /* !DOS */
5270 /* also prevent backslash in non-DOS addrbook names */
5271 if(strindex(fbuf, '\\') != NULL){
5272 dprint((2, "\"\\\" not allowed in addrbook name\n"));
5273 return NoAccess;
5275 #endif /* !DOS */
5277 if(can_access(fbuf, ACCESS_EXISTS) == 0){
5278 if(can_access(fbuf, EDIT_ACCESS) == 0){
5279 char *dir, *p;
5281 dir = ".";
5282 if((p = last_cmpnt(fbuf)) != NULL){
5283 *--p = '\0';
5284 dir = *fbuf ? fbuf : "/";
5287 #if defined(DOS) || defined(OS2)
5289 * If the dir has become a drive letter and : (e.g. "c:")
5290 * then append a "\". The library function access() in the
5291 * win 16 version of MSC seems to require this.
5293 if(isalpha((unsigned char) *dir)
5294 && *(dir+1) == ':' && *(dir+2) == '\0'){
5295 *(dir+2) = '\\';
5296 *(dir+3) = '\0';
5298 #endif /* DOS || OS2 */
5301 * Even if we can edit the address book file itself, we aren't
5302 * going to be able to change it unless we can also write in
5303 * the directory that contains it (because we write into a
5304 * temp file and then rename).
5306 if(can_access(dir, EDIT_ACCESS) == 0)
5307 access = ReadWrite;
5308 else{
5309 access = ReadOnly;
5310 q_status_message1(SM_ORDER, 2, 2,
5311 "Address book directory (%.200s) is ReadOnly",
5312 dir);
5315 else if(can_access(fbuf, READ_ACCESS) == 0)
5316 access = ReadOnly;
5317 else
5318 access = NoAccess;
5320 #endif /* !NO_LOCAL_ADDRBOOKS) */
5323 return(access);
5328 * Trim back remote address books if necessary.
5330 void
5331 trim_remote_adrbks(void)
5333 register PerAddrBook *pab;
5334 int i;
5336 dprint((2, "- trim_remote_adrbks -\n"));
5338 if(!as.initialized)
5339 return;
5341 for(i = 0; i < as.n_addrbk; i++){
5342 pab = &as.adrbks[i];
5343 if(pab->ostatus != TotallyClosed && pab->address_book
5344 && pab->address_book->rd)
5345 rd_trim_remdata(&pab->address_book->rd);
5351 * Free and close everything.
5353 void
5354 completely_done_with_adrbks(void)
5356 register PerAddrBook *pab;
5357 int i;
5359 dprint((2, "- completely_done_with_adrbks -\n"));
5361 ab_nesting_level = 0;
5363 if(!as.initialized)
5364 return;
5366 for(i = 0; i < as.n_addrbk; i++)
5367 init_abook(&as.adrbks[i], TotallyClosed);
5369 for(i = 0; i < as.n_addrbk; i++){
5370 pab = &as.adrbks[i];
5372 if(pab->filename)
5373 fs_give((void **)&pab->filename);
5375 if(pab->abnick)
5376 fs_give((void **)&pab->abnick);
5379 done_with_dlc_cache();
5381 if(as.adrbks)
5382 fs_give((void **)&as.adrbks);
5384 as.n_addrbk = 0;
5385 as.initialized = 0;
5390 * Initialize or re-initialize this address book.
5392 * Args: pab -- the PerAddrBook ptr
5393 * want_status -- desired OpenStatus for this address book
5395 void
5396 init_abook(PerAddrBook *pab, OpenStatus want_status)
5398 register OpenStatus new_status;
5400 dprint((4, "- init_abook -\n"));
5401 dprint((7, " addrbook nickname = %s filename = %s",
5402 pab->abnick ? pab->abnick : "<null>",
5403 pab->filename ? pab->filename : "<null>"));
5404 dprint((7, " ostatus was %s, want %s\n",
5405 pab->ostatus==Open ? "Open" :
5406 pab->ostatus==HalfOpen ? "HalfOpen" :
5407 pab->ostatus==ThreeQuartOpen ? "ThreeQuartOpen" :
5408 pab->ostatus==NoDisplay ? "NoDisplay" :
5409 pab->ostatus==Closed ? "Closed" : "TotallyClosed",
5410 want_status==Open ? "Open" :
5411 want_status==HalfOpen ? "HalfOpen" :
5412 want_status==ThreeQuartOpen ? "ThreeQuartOpen" :
5413 want_status==NoDisplay ? "NoDisplay" :
5414 want_status==Closed ? "Closed" : "TotallyClosed"));
5416 new_status = want_status; /* optimistic default */
5418 if(want_status == TotallyClosed){
5419 if(pab->address_book != NULL){
5420 adrbk_close(pab->address_book);
5421 pab->address_book = NULL;
5424 if(pab->so != NULL){
5425 so_give(&pab->so);
5426 pab->so = NULL;
5430 * If we don't need it, release some addrbook memory by calling
5431 * adrbk_partial_close().
5433 else if((want_status == Closed || want_status == HalfOpen) &&
5434 pab->address_book != NULL){
5435 adrbk_partial_close(pab->address_book);
5437 /* If we want the addrbook read in and it hasn't been, do so */
5438 else if(want_status == Open || want_status == NoDisplay){
5439 if(pab->address_book == NULL){ /* abook handle is not currently active */
5440 if(pab->access != NoAccess){
5441 char warning[800]; /* place to put a warning */
5442 int sort_rule;
5444 warning[0] = '\0';
5445 if(pab->access == ReadOnly)
5446 sort_rule = AB_SORT_RULE_NONE;
5447 else
5448 sort_rule = ps_global->ab_sort_rule;
5450 pab->address_book = adrbk_open(pab,
5451 ps_global->home_dir, warning, sizeof(warning), sort_rule);
5453 if(pab->address_book == NULL){
5454 pab->access = NoAccess;
5455 if(want_status == Open){
5456 new_status = HalfOpen; /* best we can do */
5457 q_status_message1(SM_ORDER | SM_DING, *warning?1:3, 4,
5458 _("Error opening/creating address book %.200s"),
5459 pab->abnick);
5460 if(*warning)
5461 q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5462 as.n_addrbk > 1 ? pab->abnick : "addressbook",
5463 warning);
5465 else
5466 new_status = Closed;
5468 dprint((1, "Error opening address book %s: %s\n",
5469 pab->abnick ? pab->abnick : "?",
5470 error_description(errno)));
5472 else{
5473 if(pab->access == NoExists)
5474 pab->access = ReadWrite;
5476 if(pab->access == ReadWrite){
5478 * Add forced entries if there are any. These are
5479 * entries that are always supposed to show up in
5480 * personal address books. They're specified in the
5481 * global config file.
5483 add_forced_entries(pab->address_book);
5486 new_status = want_status;
5487 dprint((2, "Address book %s (%s) opened with %ld items\n",
5488 pab->abnick ? pab->abnick : "?",
5489 pab->filename ? pab->filename : "?",
5490 (long)adrbk_count(pab->address_book)));
5491 if(*warning){
5492 dprint((1,
5493 "Addressbook parse error in %s (%s): %s\n",
5494 pab->abnick ? pab->abnick : "?",
5495 pab->filename ? pab->filename : "?",
5496 warning));
5497 if(!pab->gave_parse_warnings && want_status == Open){
5498 pab->gave_parse_warnings++;
5499 q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
5500 as.n_addrbk > 1 ? pab->abnick : "addressbook",
5501 warning);
5506 else{
5507 if(want_status == Open){
5508 new_status = HalfOpen; /* best we can do */
5509 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5510 "Insufficient permissions for opening address book %.200s",
5511 pab->abnick);
5513 else
5514 new_status = Closed;
5518 * File handle was already open but we were in Closed or HalfOpen
5519 * state. Check to see if someone else has changed the addrbook
5520 * since we first opened it.
5522 else if((pab->ostatus == Closed || pab->ostatus == HalfOpen) &&
5523 ps_global->remote_abook_validity > 0)
5524 (void)adrbk_check_and_fix(pab, 1, 0, 0);
5527 pab->ostatus = new_status;
5532 * Does a validity check on all the open address books.
5534 * Return -- number of address books with invalid data
5537 adrbk_check_all_validity_now(void)
5539 int i;
5540 int something_out_of_date = 0;
5541 PerAddrBook *pab;
5543 dprint((7, "- adrbk_check_all_validity_now -\n"));
5545 if(as.initialized){
5546 for(i = 0; i < as.n_addrbk; i++){
5547 pab = &as.adrbks[i];
5548 if(pab->address_book){
5549 adrbk_check_validity(pab->address_book, 1L);
5550 if(pab->address_book->flags & FILE_OUTOFDATE ||
5551 (pab->address_book->rd &&
5552 pab->address_book->rd->flags & REM_OUTOFDATE))
5553 something_out_of_date++;
5558 return(something_out_of_date);
5563 * Fix out-of-date address book. This only fixes the AdrBk part of the
5564 * problem, not the pab and display. It closes and reopens the address_book
5565 * file, clears the cache, reads new data.
5567 * Arg pab -- Pointer to the PerAddrBook data for this addrbook
5568 * safe -- It is safe to apply the fix
5569 * low_freq -- This is a low frequency check (longer between checks)
5571 * Returns non-zero if addrbook was fixed
5574 adrbk_check_and_fix(PerAddrBook *pab, int safe, int low_freq, int check_now)
5576 int ret = 0;
5577 long chk_interval;
5579 if(!as.initialized || !pab)
5580 return(ret);
5582 dprint((7, "- adrbk_check_and_fix(%s) -\n",
5583 pab->filename ? pab->filename : "?"));
5585 if(pab->address_book){
5586 if(check_now)
5587 chk_interval = 1L;
5588 else if(low_freq)
5589 chk_interval = -60L * MAX(LOW_FREQ_CHK_INTERVAL,
5590 ps_global->remote_abook_validity + 5);
5591 else
5592 chk_interval = 0L;
5594 adrbk_check_validity(pab->address_book, chk_interval);
5596 if(pab->address_book->flags & FILE_OUTOFDATE ||
5597 (pab->address_book->rd &&
5598 pab->address_book->rd->flags & REM_OUTOFDATE &&
5599 !(pab->address_book->rd->flags & USER_SAID_NO))){
5600 if(safe){
5601 OpenStatus save_status;
5602 int save_rem_abook_valid = 0;
5604 dprint((2, "adrbk_check_and_fix %s: fixing %s\n",
5605 debug_time(0,0,ps_global->signal_in_progress),
5606 pab->filename ? pab->filename : "?"));
5607 if(ab_nesting_level > 0){
5608 q_status_message3(SM_ORDER, 0, 2,
5609 "Resyncing address book%.200s%.200s%.200s",
5610 as.n_addrbk > 1 ? " \"" : "",
5611 as.n_addrbk > 1 ? pab->abnick : "",
5612 as.n_addrbk > 1 ? "\"" : "");
5613 display_message('x');
5614 fflush(stdout);
5617 ret++;
5618 save_status = pab->ostatus;
5620 /* don't do the trim right now */
5621 if(pab->address_book->rd)
5622 pab->address_book->rd->flags &= ~DO_REMTRIM;
5625 * Have to change this from -1 or we won't actually do
5626 * the resync.
5628 if((pab->address_book->rd &&
5629 pab->address_book->rd->flags & REM_OUTOFDATE) &&
5630 ps_global->remote_abook_validity == -1){
5631 save_rem_abook_valid = -1;
5632 ps_global->remote_abook_validity = 0;
5635 init_abook(pab, TotallyClosed);
5637 pab->so = NULL;
5638 /* this sets up pab->so, so is important */
5639 pab->access = adrbk_access(pab);
5642 * If we just re-init to HalfOpen... we won't actually
5643 * open the address book, which was open before. That
5644 * would be fine but it's a little nicer if we can open
5645 * it now so that we don't defer the resync until
5646 * the next open, which would be a user action for sure.
5647 * Right now we may be here during a newmail check
5648 * timeout, so this is a good time to do the resync.
5650 if(save_status == HalfOpen ||
5651 save_status == ThreeQuartOpen ||
5652 save_status == Closed)
5653 init_abook(pab, NoDisplay);
5655 init_abook(pab, save_status);
5657 if(save_rem_abook_valid)
5658 ps_global->remote_abook_validity = save_rem_abook_valid;
5660 if(ab_nesting_level > 0 && pab->ostatus == save_status)
5661 q_status_message3(SM_ORDER, 0, 2,
5662 "Resynced address book%.200s%.200s%.200s",
5663 as.n_addrbk > 1 ? " \"" : "",
5664 as.n_addrbk > 1 ? pab->abnick : "",
5665 as.n_addrbk > 1 ? "\"" : "");
5667 else
5668 dprint((2,
5669 "adrbk_check_and_fix: not safe to fix %s\n",
5670 pab->filename ? pab->filename : "?"));
5674 return(ret);
5678 static time_t last_check_and_fix_all;
5680 * Fix out of date address books. This only fixes the AdrBk part of the
5681 * problem, not the pab and display. It closes and reopens the address_book
5682 * files, clears the caches, reads new data.
5684 * Args safe -- It is safe to apply the fix
5685 * low_freq -- This is a low frequency check (longer between checks)
5687 * Returns non-zero if an addrbook was fixed
5690 adrbk_check_and_fix_all(int safe, int low_freq, int check_now)
5692 int i, ret = 0;
5693 PerAddrBook *pab;
5695 if(!as.initialized)
5696 return(ret);
5698 dprint((7, "- adrbk_check_and_fix_all -\n"));
5700 last_check_and_fix_all = get_adj_time();
5702 for(i = 0; i < as.n_addrbk; i++){
5703 pab = &as.adrbks[i];
5704 if(pab->address_book)
5705 ret += adrbk_check_and_fix(pab, safe, low_freq, check_now);
5708 return(ret);
5715 void
5716 adrbk_maintenance(void)
5718 static time_t last_time_here = 0;
5719 time_t now;
5720 int i;
5721 long low_freq_interval;
5723 dprint((9, "- adrbk_maintenance -\n"));
5725 if(!as.initialized)
5726 return;
5728 now = get_adj_time();
5730 if(now < last_time_here + 120)
5731 return;
5733 last_time_here = now;
5735 low_freq_interval = MAX(LOW_FREQ_CHK_INTERVAL,
5736 ps_global->remote_abook_validity + 5);
5738 /* check the time here to make it cheap */
5739 if(ab_nesting_level == 0 &&
5740 ps_global->remote_abook_validity > 0 &&
5741 now > last_check_and_fix_all + low_freq_interval * 60L)
5742 (void)adrbk_check_and_fix_all(1, 1, 0);
5744 /* close down idle connections */
5745 for(i = 0; i < as.n_addrbk; i++){
5746 PerAddrBook *pab;
5748 pab = &as.adrbks[i];
5750 if(pab->address_book &&
5751 pab->address_book->type == Imap &&
5752 pab->address_book->rd &&
5753 rd_stream_exists(pab->address_book->rd)){
5754 dprint((7,
5755 "adrbk_maint: %s: idle cntr %ld (%ld)\n",
5756 pab->address_book->orig_filename
5757 ? pab->address_book->orig_filename : "?",
5758 (long)(now - pab->address_book->rd->last_use),
5759 (long)pab->address_book->rd->last_use));
5761 if(now > pab->address_book->rd->last_use + IMAP_IDLE_TIMEOUT){
5762 dprint((2,
5763 "adrbk_maint %s: closing idle (%ld secs) connection: %s\n",
5764 debug_time(0,0,ps_global->signal_in_progress),
5765 (long)(now - pab->address_book->rd->last_use),
5766 pab->address_book->orig_filename
5767 ? pab->address_book->orig_filename : "?"));
5768 rd_close_remote(pab->address_book->rd);
5770 else{
5772 * If we aren't going to close it, we ping it instead to
5773 * make sure it stays open and doesn't timeout on us.
5774 * This shouldn't be necessary unless the server has a
5775 * really short timeout. If we got killed for some reason
5776 * we set imap.stream to NULL.
5777 * Instead of just pinging, we may as well check for
5778 * updates, too.
5780 if(ab_nesting_level == 0 &&
5781 ps_global->remote_abook_validity > 0){
5782 time_t save_last_use;
5785 * We shouldn't count this as a real last_use.
5787 save_last_use = pab->address_book->rd->last_use;
5788 (void)adrbk_check_and_fix(pab, 1, 0, 1);
5789 pab->address_book->rd->last_use = save_last_use;
5791 /* just ping it if not safe to fix it */
5792 else if(!rd_ping_stream(pab->address_book->rd)){
5793 dprint((2,
5794 "adrbk_maint: %s: abook stream closed unexpectedly: %s\n",
5795 debug_time(0,0,ps_global->signal_in_progress),
5796 pab->address_book->orig_filename
5797 ? pab->address_book->orig_filename : "?"));
5806 * Parses a string of comma-separated addresses or nicknames into an
5807 * array.
5809 * Returns an allocated, null-terminated list, or NULL.
5811 char **
5812 parse_addrlist(char *addrfield)
5814 #define LISTCHUNK 500 /* Alloc this many addresses for list at a time */
5815 char **al, **ad;
5816 char *next_addr, *cur_addr, *p, *q;
5817 int slots = LISTCHUNK;
5819 if(!addrfield)
5820 return((char **)NULL);
5822 /* allocate first chunk */
5823 slots = LISTCHUNK;
5824 al = (char **)fs_get(sizeof(char *) * (slots+1));
5825 ad = al;
5827 p = addrfield;
5829 /* skip any leading whitespace */
5830 for(q = p; *q && *q == SPACE; q++)
5831 ;/* do nothing */
5833 next_addr = (*q) ? q : NULL;
5835 /* Loop adding each address in list to array al */
5836 for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
5838 next_addr = skip_to_next_addr(cur_addr);
5840 q = cur_addr;
5841 SKIP_SPACE(q);
5843 /* allocate more space */
5844 if((ad-al) >= slots){
5845 slots += LISTCHUNK;
5846 fs_resize((void **)&al, sizeof(char *) * (slots+1));
5847 ad = al + slots - LISTCHUNK;
5850 if(*q)
5851 *ad++ = cpystr(q);
5854 *ad++ = NULL;
5856 /* free up any excess we've allocated */
5857 fs_resize((void **)&al, sizeof(char *) * (ad - al));
5858 return(al);
5863 * Args cur -- pointer to the start of the current addr in list.
5865 * Returns a pointer to the start of the next addr or NULL if there are
5866 * no more addrs.
5868 * Side effect: current addr has trailing white space removed
5869 * and is null terminated.
5871 char *
5872 skip_to_next_addr(char *cur)
5874 register char *p,
5876 char *ret_pointer;
5877 int in_quotes = 0,
5878 in_comment = 0;
5879 char prev_char = '\0';
5882 * Find delimiting comma or end.
5883 * Quoted commas and commented commas don't count.
5885 for(q = cur; *q; q++){
5886 switch(*q){
5887 case COMMA:
5888 if(!in_quotes && !in_comment)
5889 goto found_comma;
5890 break;
5892 case LPAREN:
5893 if(!in_quotes && !in_comment)
5894 in_comment = 1;
5895 break;
5897 case RPAREN:
5898 if(in_comment && prev_char != BSLASH)
5899 in_comment = 0;
5900 break;
5902 case QUOTE:
5903 if(in_quotes && prev_char != BSLASH)
5904 in_quotes = 0;
5905 else if(!in_quotes && !in_comment)
5906 in_quotes = 1;
5907 break;
5909 default:
5910 break;
5913 prev_char = *q;
5916 found_comma:
5917 if(*q){ /* trailing comma case */
5918 *q = '\0';
5919 ret_pointer = q + 1;
5921 else
5922 ret_pointer = NULL; /* no more addrs after cur */
5924 /* remove trailing white space from cur */
5925 for(p = q - 1; p >= cur && isspace((unsigned char)*p); p--)
5926 *p = '\0';
5928 return(ret_pointer);
5933 * Add entries specified by system administrator. If the nickname already
5934 * exists, it is not touched.
5936 void
5937 add_forced_entries(AdrBk *abook)
5939 AdrBk_Entry *abe;
5940 char *nickname, *fullname, *address;
5941 char *end_of_nick, *end_of_full, **t;
5944 if(!ps_global->VAR_FORCED_ABOOK_ENTRY ||
5945 !ps_global->VAR_FORCED_ABOOK_ENTRY[0] ||
5946 !ps_global->VAR_FORCED_ABOOK_ENTRY[0][0])
5947 return;
5949 for(t = ps_global->VAR_FORCED_ABOOK_ENTRY; t[0] && t[0][0]; t++){
5950 nickname = *t;
5953 * syntax for each element is
5954 * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
5957 /* find end of nickname */
5958 end_of_nick = nickname;
5959 while(*end_of_nick
5960 && !isspace((unsigned char)*end_of_nick)
5961 && *end_of_nick != '|')
5962 end_of_nick++;
5964 /* find the pipe character between nickname and fullname */
5965 fullname = end_of_nick;
5966 while(*fullname && *fullname != '|')
5967 fullname++;
5969 if(*fullname)
5970 fullname++;
5972 *end_of_nick = '\0';
5973 abe = adrbk_lookup_by_nick(abook, nickname, NULL);
5975 if(!abe){ /* If it isn't there, add it */
5977 /* skip whitespace before fullname */
5978 fullname = skip_white_space(fullname);
5980 /* find the pipe character between fullname and address */
5981 end_of_full = fullname;
5982 while(*end_of_full && *end_of_full != '|')
5983 end_of_full++;
5985 if(!*end_of_full){
5986 dprint((2,
5987 "missing | in forced-abook-entry \"%s\"\n",
5988 nickname ? nickname : "?"));
5989 continue;
5992 address = end_of_full + 1;
5994 /* skip whitespace before address */
5995 address = skip_white_space(address);
5997 if(*address == '('){
5998 dprint((2,
5999 "no lists allowed in forced-abook-entry \"%s\"\n",
6000 address ? address : "?"));
6001 continue;
6004 /* go back and remove trailing white space from fullname */
6005 while(*end_of_full == '|' || isspace((unsigned char)*end_of_full)){
6006 *end_of_full = '\0';
6007 end_of_full--;
6010 dprint((2,
6011 "Adding forced abook entry \"%s\"\n", nickname ? nickname : ""));
6013 (void)adrbk_add(abook,
6014 NO_NEXT,
6015 nickname,
6016 fullname,
6017 address,
6018 NULL,
6019 NULL,
6020 Single,
6021 (adrbk_cntr_t *)NULL,
6022 (int *)NULL,
6030 /* Go through the list of addressbooks and check if any
6031 * of them point to the given stream.
6034 any_addressbook_in_remote_stream(MAILSTREAM *stream)
6036 int rv = 0;
6037 int i = 0, num = 0;
6038 char *nickname = NULL;
6039 char *filename = NULL;
6040 char *q = NULL;
6043 if(ps_global->VAR_ADDRESSBOOK &&
6044 ps_global->VAR_ADDRESSBOOK[num] &&
6045 ps_global->VAR_ADDRESSBOOK[num][0]){
6046 q = ps_global->VAR_ADDRESSBOOK[num++];
6047 i = num;
6049 else if(ps_global->VAR_GLOB_ADDRBOOK &&
6050 ps_global->VAR_GLOB_ADDRBOOK[i-num] &&
6051 ps_global->VAR_GLOB_ADDRBOOK[i-num][0]){
6052 q = ps_global->VAR_GLOB_ADDRBOOK[i - num];
6053 i++;
6054 } else q = NULL;
6055 if(q != NULL){
6056 get_pair(q, &nickname, &filename, 0, 0);
6058 if(nickname) fs_give((void **)&nickname);
6060 if(filename){
6061 if(*filename == '{'
6062 && same_stream(filename, stream) != NULL)
6063 rv = 1;
6064 fs_give((void **)&filename);
6067 } while (rv == 0 && q != NULL);
6069 return rv;