1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: adrbklib.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
5 /* ========================================================================
6 * Copyright 2006-2009 University of Washington
7 * Copyright 2013-2020 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"
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"
45 #define ADRBK_NAME ".addressbook"
48 #define OPEN_WRITE_MODE (O_TRUNC|O_WRONLY|O_CREAT|O_BINARY)
52 #define MAXPATH 1000 /* Longest file path we can deal with */
57 #define INDENTXTRA " : "
58 #define INDENT 3 /* length of INDENTSTR */
63 static int writing
; /* so we can give understandable error message */
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
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#".
145 adrbk_open(PerAddrBook
*pab
, char *homedir
, char *warning
, size_t warninglen
, int sort_rule
)
147 char path
[MAXPATH
], *filename
;
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
){
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"));
172 if(!(ab
->rd
= rd_new_remdata(RemImap
, ab
->orig_filename
, REMOTE_ABOOK_SUBTYPE
))){
174 "adrbk_open: remote: new_remdata failed: %s\n",
175 ab
->orig_filename
? ab
->orig_filename
: "NULL"));
179 /* Transfer responsibility for the storage object */
180 ab
->rd
->so
= pab
->so
;
183 try_cache
= rd_read_metadata(ab
->rd
);
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
;
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
++;
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
;
245 pab
->access
= ab
->rd
->access
= ReadOnly
;
248 if(ab
->rd
->flags
& REM_OUTOFDATE
){
249 if(rd_update_local(ab
->rd
) != 0){
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.
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
){
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"));
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
: "?"));
288 /*------------ figure out and save name of file to open ---------*/
289 if(filename
== NULL
){
291 build_path(path
, homedir
, ADRBK_NAME
, sizeof(path
));
292 ab
->filename
= cpystr(path
);
295 ab
->filename
= cpystr(ADRBK_NAME
);
298 if(is_absolute_path(filename
)){
299 ab
->filename
= cpystr(filename
);
303 build_path(path
, homedir
, filename
, sizeof(path
));
304 ab
->filename
= cpystr(path
);
307 ab
->filename
= cpystr(filename
);
312 if(ab
->filename
&& ab
->filename
[0]){
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.
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
340 if(!ab
->our_filecopy
)
341 ab
->our_filecopy
= ab
->filename
;
344 dprint((1, "adrbk_open: ab->filename is NULL???\n"));
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
;
386 ab
->sort_rule
= sort_rule
;
387 if(pab
->access
== ReadOnly
)
388 ab
->sort_rule
= AB_SORT_RULE_NONE
;
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
;
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"));
417 ab
->rd
->flags
&= ~DO_REMTRIM
;
418 rd_close_remdata(&ab
->rd
);
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
);
433 fs_give((void **) &ab
->filename
);
440 fs_give((void **) &ab
);
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.
454 copy_abook_to_tempfile(AdrBk
*ab
, char *warning
, size_t warninglen
)
459 FILE *fp_read
= (FILE *)NULL
,
460 *fp_write
= (FILE *)NULL
;
465 dprint((3, "copy_file(%s) -\n",
466 (ab
&& ab
->filename
) ? ab
->filename
: ""));
468 if(!ab
|| !ab
->filename
|| !ab
->filename
[0])
471 if(!(ab
->flags
& FILE_OUTOFDATE
))
474 /* open filename for reading */
475 fp_read
= our_fopen(ab
->filename
, "rb");
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
){
485 if(ab
->type
== Imap
){
486 snprintf(warning
, warninglen
,
487 /* TRANSLATORS: A temporary file for the address book can't
489 _("Temp addrbook file can't be opened: %s"),
491 warning
[warninglen
-1] = '\0';
494 strncpy(warning
, _("Address book doesn't exist"), warninglen
);
495 warning
[warninglen
-1] = '\0';
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 ---*/
519 strncpy(warning
, error_description(errno
), warninglen
);
520 warning
[warninglen
-1] = '\0';
523 dprint((2, "create failed: %s\n",
524 error_description(errno
)));
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 */
540 /* copy ab->filename to ab->our_filecopy, preserving mtime */
541 if(ab
->filename
!= ab
->our_filecopy
){
543 struct utimbuf times
;
546 dprint((7, "Before abook copies\n"));
547 if((fd
= our_open(ab
->our_filecopy
, OPEN_WRITE_MODE
, 0600)) < 0)
550 fp_write
= fdopen(fd
, "wb");
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
: "?"));
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
)
569 (void)fclose(fp_write
);
570 fp_write
= (FILE *)NULL
;
571 if(valid_stat
&& our_utime(ab
->our_filecopy
, ×
)){
572 q_status_message1(SM_INFO
, 0, 3,
573 "Error: can't set mtime for \"%.200s\"",
574 (lc
=last_cmpnt(ab
->filename
)) ? lc
: ab
->our_filecopy
);
575 dprint((2, "Error: can't set mtime for \"%s\"\n",
576 ab
->our_filecopy
? ab
->our_filecopy
: "?"));
579 (void)fclose(fp_read
);
580 fp_read
= (FILE *)NULL
;
581 if(!(ab
->fp
= our_fopen(ab
->our_filecopy
, "rb")))
584 dprint((7, "After abook file copy\n"));
586 else{ /* already open to the right file */
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
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
)){
604 dprint((2, "failed in build_abook_datastruct\n"));
609 && build_abook_tries(ab
, (warning
&& !*warning
) ? warning
: NULL
)){
613 dprint((2, "failed in build_abook_tries\n"));
620 ab
->flags
&= ~FILE_OUTOFDATE
; /* turn off out of date flag */
625 (void)fclose(fp_read
);
628 (void)fclose(fp_write
);
631 if(ab
->our_filecopy
&& ab
->our_filecopy
!= ab
->filename
)
632 our_unlink(ab
->our_filecopy
);
640 * Returns an allocated copy of the directory which contains filename.
643 dir_containing(char *filename
)
651 if((lc
= last_cmpnt(filename
)) != NULL
){
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';
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
)
678 AdrBk_Entry
*ae
, *ae_prev
;
682 dprint((9, "- adrbk_is_in_sort_order -\n"));
687 if(ab
->sort_rule
== AB_SORT_RULE_NONE
|| ab
->count
< 2)
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
) ?
694 (ab
->sort_rule
== AB_SORT_RULE_NICK_LISTS
) ?
695 cmp_ae_by_nick_lists_last
:
696 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
699 ae_prev
= adrbk_get_ae(ab
, (a_c_arg_t
) 0);
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){
712 dprint((9, "- adrbk_is_in_sort_order : no (entry %ld) -\n", (long) entry
));
722 dprint((9, "- adrbk_is_in_sort_order : yes -\n"));
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.
738 count_abook_entries_on_disk(AdrBk
*ab
, a_c_arg_t
*deleted
)
742 adrbk_cntr_t count
= 0;
743 adrbk_cntr_t deleted_count
= 0;
752 while((nickname
= get_next_abook_entry(fp_in
, rew
)) != NULL
){
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] == '#'){
775 *deleted
= (a_c_arg_t
) deleted_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
)
791 a_c_arg_t count
, deleted
;
792 adrbk_cntr_t used
= 0, delused
= 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
,
799 WIDTH_INFO_S
*widths
;
800 int rew
= 1, is_deleted
;
803 dprint((9, "- build_abook_datastruct -\n"));
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
);
826 ab
->count
= (adrbk_cntr_t
) count
;
827 ab
->del_count
= (adrbk_cntr_t
) deleted
;
830 ab
->arr
= (AdrBk_Entry
*) fs_get(count
* sizeof(AdrBk_Entry
));
831 memset(ab
->arr
, 0, count
* sizeof(AdrBk_Entry
));
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
){
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] == '#'){
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
)));
871 if(delused
< ab
->del_count
)
872 ae
= &ab
->del
[delused
++];
876 ae
= &ab
->arr
[used
++];
880 init_ae(ab
, ae
, nickname
);
882 fs_give((void **) &nickname
);
884 if(!ae
|| is_deleted
)
888 * We're calculating the widths as we read in the data.
889 * We could just go with some default widths to save time.
897 this_nick_width
= (int) utf8_width(ae
->nickname
);
900 this_full_width
= (int) utf8_width(ae
->fullname
);
902 if(ae
->tag
== Single
){
904 this_addr_width
= (int) utf8_width(ae
->addr
.addr
);
910 for(a2
= ae
->addr
.list
; *a2
!= NULL
; a2
++)
911 this_addr_width
= MAX(this_addr_width
, (int) utf8_width(*a2
));
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
;
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
;
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
){
948 max_fcc
= this_fcc_width
;
950 else if(this_fcc_width
> 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"));
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
)));
985 * Builds the trees used for nickname and address lookups.
988 build_abook_tries(AdrBk
*ab
, char *warning
)
990 adrbk_cntr_t entry_num
;
997 dprint((9, "- build_abook_tries(%s) -\n", ab
->filename
));
1001 free_abook_trie(&ab
->nick_trie
);
1004 free_abook_trie(&ab
->addr_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
1016 for(entry_num
= 0; entry_num
< ab
->count
; entry_num
++){
1017 ae
= adrbk_get_ae(ab
, (a_c_arg_t
) entry_num
);
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
;
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]){
1037 reverse
= cpystr(ae
->fullname
);
1042 fs_give((void **) &forward
);
1044 forward
= ae
->fullname
;
1048 forward
= ae
->fullname
;
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
== ' ')
1065 add_entry_to_trie(&ab
->full_trie
, addthis
, (a_c_arg_t
) entry_num
);
1070 add_entry_to_trie(&ab
->revfull_trie
, reverse
, (a_c_arg_t
) entry_num
);
1071 fs_give((void **) &reverse
);
1074 if(forward
&& forward
!= ae
->fullname
)
1075 fs_give((void **) &forward
);
1078 if(ae
->tag
== Single
&& ae
->addr
.addr
&& ae
->addr
.addr
[0]){
1080 char *tmp_a_string
, *simple_addr
= NULL
;
1081 ADDRESS
*addr
= NULL
;
1082 char *fakedomain
= "@";
1085 * Isolate the actual address out of ae.
1087 tmp_a_string
= cpystr(ae
->addr
.addr
);
1088 rfc822_parse_adrlist(&addr
, tmp_a_string
, fakedomain
);
1090 fs_give((void **) &tmp_a_string
);
1093 if(addr
->mailbox
&& addr
->host
1094 && !(addr
->host
[0] == '@' && addr
->host
[1] == '\0'))
1095 simple_addr
= simple_addr_string(addr
, buf
, sizeof(buf
));
1098 * If the fullname wasn't set in the addrbook entry there
1099 * may still be one that is part of the address. We need
1100 * to be careful because we may be in the middle of opening
1101 * the address book right now. Don't call something like
1102 * our_build_address because it will probably re-open this
1103 * same addrbook infinitely.
1105 if(!(ae
->fullname
&& ae
->fullname
[0])
1106 && addr
->personal
&& addr
->personal
[0])
1107 add_entry_to_trie(&ab
->full_trie
, addr
->personal
,
1108 (a_c_arg_t
) entry_num
);
1110 mail_free_address(&addr
);
1114 add_entry_to_trie(&ab
->addr_trie
, simple_addr
, (a_c_arg_t
) entry_num
);
1119 dprint((9, "- build_abook_tries done -\n"));
1122 cancel_busy_cue(-1);
1129 add_entry_to_trie(AdrBk_Trie
**head
, char *str
, a_c_arg_t entry_num
)
1131 AdrBk_Trie
*temp
, *trail
;
1132 char *addthis
, *p
, buf
[1000];
1134 if(!head
|| !str
|| !*str
)
1137 /* add as lower case */
1139 for(p
= str
; *p
&& ((*p
& 0x80) || !isupper((unsigned char) *p
)); p
++)
1143 strncpy(buf
, str
, sizeof(buf
));
1144 buf
[sizeof(buf
)-1] = '\0';
1145 for(p
= buf
; *p
; p
++)
1146 if(!(*p
& 0x80) && isupper((unsigned char) *p
))
1154 temp
= trail
= (*head
);
1157 * Find way down the trie, adding missing nodes as we go.
1159 for(p
= addthis
; *p
;){
1161 temp
= (AdrBk_Trie
*) fs_get(sizeof(*temp
));
1162 memset(temp
, 0, sizeof(*temp
));
1164 temp
->entrynum
= NO_NEXT
;
1172 while((temp
!= NULL
) && (temp
->value
!= *p
)){
1177 /* wasn't there, add new node */
1179 temp
= (AdrBk_Trie
*) fs_get(sizeof(*temp
));
1180 memset(temp
, 0, sizeof(*temp
));
1182 temp
->entrynum
= NO_NEXT
;
1183 trail
->right
= temp
;
1194 * If entrynum is already filled in there must be an entry with
1195 * the same nickname earlier in the abook. Use that earlier entry.
1197 if(temp
!= NULL
&& temp
->entrynum
== NO_NEXT
)
1198 temp
->entrynum
= (adrbk_cntr_t
) entry_num
;
1203 * Returns entry_num of first entry with this nickname, else NO_NEXT.
1206 lookup_nickname_in_trie(AdrBk
*ab
, char *nickname
)
1208 if(!ab
|| !nickname
|| !ab
->nick_trie
)
1211 return(lookup_in_abook_trie(ab
->nick_trie
, nickname
));
1216 * Returns entry_num of first entry with this address, else NO_NEXT.
1219 lookup_address_in_trie(AdrBk
*ab
, char *address
)
1221 dprint((9, "lookup_address_in_trie: %s\n", ab
? (ab
->addr_trie
? (address
? address
: "?") : "null addr_trie") : "null ab"));
1222 if(!ab
|| !address
|| !ab
->addr_trie
)
1225 return(lookup_in_abook_trie(ab
->addr_trie
, address
));
1230 lookup_in_abook_trie(AdrBk_Trie
*t
, char *str
)
1232 char *p
, *lookthisup
;
1234 adrbk_cntr_t ret
= NO_NEXT
;
1239 /* make lookup case independent */
1241 for(p
= str
; *p
&& !(*p
& 0x80) && islower((unsigned char) *p
); p
++)
1245 strncpy(buf
, str
, sizeof(buf
));
1246 buf
[sizeof(buf
)-1] = '\0';
1247 for(p
= buf
; *p
; p
++)
1248 if(!(*p
& 0x80) && isupper((unsigned char) *p
))
1259 * We usually return out from inside the loop (unless str == "").
1262 /* search for character at this level */
1263 while(t
->value
!= *p
){
1264 if(t
->right
== NULL
)
1265 return(ret
); /* no match */
1270 if(*++p
== '\0') /* end of str, a match */
1271 return(t
->entrynum
);
1273 /* need to go down to match next character */
1274 if(t
->down
== NULL
) /* no match */
1285 free_abook_trie(AdrBk_Trie
**trie
)
1289 free_abook_trie(&(*trie
)->down
);
1290 free_abook_trie(&(*trie
)->right
);
1291 fs_give((void **) trie
);
1298 * Returns pointer to start of next address book entry from disk file.
1299 * The return will be in raw form from the file with newlines still
1300 * embedded. Or NULL at end of file.
1302 * If rew is set, rewind the file and start over at beginning.
1305 get_next_abook_entry(FILE *fp
, int rew
)
1307 char *returned_lines
= NULL
, *p
;
1309 static int will_be_done_next_time
= 0;
1310 static long next_nickname_offset
= 0L;
1312 long offset
, saved_offset
;
1315 #define CHUNKSIZE 500
1317 lsize
= sizeof(line
);
1320 will_be_done_next_time
= 0;
1322 /* skip leading (bogus) continuation lines */
1326 line
[lsize
-2] = '\0';
1327 p
= fgets(line
, lsize
, fp
);
1332 /* line is too long to fit, read the rest and discard */
1333 while(line
[lsize
-2] != '\0' && line
[lsize
-2] != '\n'
1336 /* get next lsize-1 characters, leaving line[0] */
1337 line
[lsize
-2] = '\0';
1338 p
= fgets(line
+1, lsize
-1, fp
);
1340 }while(line
[0] == SPACE
);
1342 /* offset is start of first good line now */
1343 next_nickname_offset
= offset
;
1346 if(will_be_done_next_time
)
1349 /* we set this up in rew==1 case or on previous call to this routine */
1350 offset
= next_nickname_offset
;
1353 * The rest is working on finding the start of the next entry so
1354 * skip continuation lines
1357 next_nickname_offset
= ftell(fp
);
1359 line
[lsize
-2] = '\0';
1360 p
= fgets(line
, lsize
, fp
);
1362 /* line is too long to fit, read the rest and discard */
1363 while(line
[lsize
-2] != '\0' && line
[lsize
-2] != '\n'
1366 /* get next lsize-1 characters, leaving line[0] */
1367 line
[lsize
-2] = '\0';
1368 p
= fgets(line
+1, lsize
-1, fp
);
1370 }while(line
[0] == SPACE
);
1372 /* next_nickname_offset is start of next entry now */
1375 will_be_done_next_time
= 1;
1377 len
= next_nickname_offset
- offset
;
1379 returned_lines
= (char *) fs_get((len
+ 1) * sizeof(char));
1381 saved_offset
= ftell(fp
);
1382 if(fseek(fp
, offset
, 0)){
1383 dprint((2, "get_next_ab_entry: trouble fseeking\n"));
1385 fs_give((void **) &returned_lines
);
1388 if(fread(returned_lines
, sizeof(char), (unsigned) len
, fp
) != len
){
1389 dprint((2, "get_next_ab_entry: trouble freading\n"));
1391 fs_give((void **) &returned_lines
);
1395 if(fseek(fp
, saved_offset
, 0)){
1396 dprint((2, "get_next_ab_entry: trouble fseeking to saved_offset\n"));
1398 fs_give((void **) &returned_lines
);
1402 returned_lines
[len
] = '\0';
1404 return(returned_lines
);
1409 * Returns a pointer to the start of the mailbox@host part of this
1410 * address string, and a pointer to the end + 1. The caller can then
1411 * replace the end char with \0 and call the hash function, then put
1412 * back the end char. Start_addr and end_addr are assumed to be non-null.
1415 strip_addr_string(char *addrstr
, char **start_addr
, char **end_addr
)
1420 char prev_char
= '\0';
1422 if(!addrstr
|| !*addrstr
){
1428 *start_addr
= addrstr
;
1430 for(q
= addrstr
; *q
; q
++){
1433 if(!in_quotes
&& !in_comment
){
1437 while(*q
&& *q
!= '>')
1453 if(!in_quotes
&& !in_comment
)
1458 if(in_comment
&& prev_char
!= BSLASH
)
1463 if(in_quotes
&& prev_char
!= BSLASH
)
1465 else if(!in_quotes
&& !in_comment
)
1481 * Fill in the passed in ae pointer by parsing the str that is passed.
1484 * ae -- pointer we want to fill in. The individual members of
1485 * the ae struct will be allocated here
1486 * str -- the string from the on-disk address book file for this entry
1488 * Returns a pointer to ae or NULL if there are problems.
1491 init_ae(AdrBk
*ab
, AdrBk_Entry
*a
, char *str
)
1494 char *addrfield
= (char *) NULL
;
1495 char *addrfield_end
;
1496 char *nickname
, *fullname
, *fcc
, *extra
;
1499 dprint((2, "init_ae: found trouble: NULL ab\n"));
1500 return((AdrBk_Entry
*) NULL
);
1507 REPLACE_NEWLINES_WITH_SPACE(p
);
1512 RM_END_SPACE(nickname
, p
);
1513 a
->nickname
= cpystr(nickname
);
1517 RM_END_SPACE(nickname
, p
);
1518 a
->nickname
= cpystr(nickname
);
1524 RM_END_SPACE(fullname
, p
);
1525 a
->fullname
= cpystr(fullname
);
1529 RM_END_SPACE(fullname
, p
);
1530 a
->fullname
= cpystr(fullname
);
1536 RM_END_SPACE(addrfield
, p
);
1540 RM_END_SPACE(addrfield
, p
);
1546 RM_END_SPACE(fcc
, p
);
1547 a
->fcc
= cpystr(fcc
);
1553 RM_END_SPACE(fcc
, p
);
1554 a
->fcc
= cpystr(fcc
);
1558 p
= extra
+ strlen(extra
);
1559 RM_END_SPACE(extra
, p
);
1562 * When we wrap long comments we insert an extra colon
1563 * in the wrap so we can spot it and take it back out.
1564 * Pretty much a hack since we thought of it a long
1565 * time after designing it, but it eliminates the limit
1566 * on length of comments. Here we are looking for
1567 * <SP> <SP> : <SP> or
1568 * <SP> <SP> <SP> : <SP> and replacing it with <SP>.
1569 * There could have been a single \n or \r\n, so that
1570 * is why we check for 2 or 3 spaces before the colon.
1571 * (This was another mistake.)
1574 while(*src
!= '\0'){
1575 if(*src
== SPACE
&& *(src
+1) == SPACE
&&
1576 *(src
+2) == ':' && *(src
+3) == SPACE
){
1579 * If there was an extra space because of the
1580 * CRLF (instead of LF) then we already put
1581 * a SP in dst last time through the loop
1582 * and don't need to add another.
1584 if(src
== extra
|| *(src
-1) != SPACE
)
1594 a
->extra
= cpystr(extra
);
1600 /* decode and convert to UTF-8 if we need to */
1602 convert_possibly_encoded_str_to_utf8(&a
->nickname
);
1603 convert_possibly_encoded_str_to_utf8(&a
->fullname
);
1604 convert_possibly_encoded_str_to_utf8(&a
->fcc
);
1605 convert_possibly_encoded_str_to_utf8(&a
->extra
);
1607 /* parse addrfield */
1609 if(*addrfield
== '('){ /* it's a list */
1612 addrfield_end
= p
+ strlen(p
);
1615 * Get rid of the parens.
1616 * If this isn't true the input file is messed up.
1618 if(p
[strlen(p
)-1] == ')'){
1621 p
[strlen(p
)-1] = '\0';
1623 a
->addr
.list
= parse_addrlist(p
);
1624 for(ll
= a
->addr
.list
; ll
&& *ll
; ll
++)
1625 convert_possibly_encoded_str_to_utf8(ll
);
1628 /* put back what was there to start with */
1629 *addrfield_end
= ')';
1630 a
->addr
.list
= (char **)fs_get(sizeof(char *) * 2);
1631 a
->addr
.list
[0] = cpystr(addrfield
);
1632 a
->addr
.list
[1] = NULL
;
1633 dprint((2, "parsing error reading addressbook: missing right paren: %s\n",
1634 addrfield
? addrfield
: "?"));
1637 else{ /* A plain, single address */
1640 a
->addr
.addr
= cpystr(addrfield
);
1641 convert_possibly_encoded_str_to_utf8(&a
->addr
.addr
);
1646 * If no addrfield, assume an empty Single.
1648 a
->addr
.addr
= cpystr("");
1657 * Return the size of the address book
1660 adrbk_count(AdrBk
*ab
)
1662 return(ab
? ab
->count
: (adrbk_cntr_t
) 0);
1667 * Return a pointer to the ae that has index number "entry_num".
1670 adrbk_get_ae(AdrBk
*ab
, a_c_arg_t entry_num
)
1672 if(!ab
|| entry_num
>= (a_c_arg_t
) ab
->count
)
1673 return((AdrBk_Entry
*) NULL
);
1675 return(&ab
->arr
[entry_num
]);
1680 * Return a pointer to the deleted ae that has index number "entry_num".
1683 adrbk_get_delae(AdrBk
*ab
, a_c_arg_t entry_num
)
1685 if(!ab
|| entry_num
>= (a_c_arg_t
) ab
->del_count
)
1686 return((AdrBk_Entry
*) NULL
);
1688 return(&ab
->del
[entry_num
]);
1693 * Look up an entry in the address book given a nickname
1695 * Args: ab -- the address book
1696 * nickname -- nickname to match
1697 * entry_num -- if matched, return entry_num of match here
1699 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1701 * Lookups usually need to be recursive in case the address
1702 * book references itself. This is left to the next level up.
1703 * adrbk_clearrefs() is provided to clear all the reference tags in
1704 * the address book for loop detection.
1705 * When there are duplicates of the same nickname we return the first.
1706 * This can only happen if addrbook was edited externally.
1709 adrbk_lookup_by_nick(AdrBk
*ab
, char *nickname
, adrbk_cntr_t
*entry_num
)
1714 dprint((5, "- adrbk_lookup_by_nick(%s) (in %s) -\n",
1715 nickname
? nickname
: "?",
1716 (ab
&& ab
->filename
) ? ab
->filename
: "?"));
1718 if(!ab
|| !nickname
|| !nickname
[0])
1722 num
= lookup_nickname_in_trie(ab
, nickname
);
1725 ae
= adrbk_get_ae(ab
, (a_c_arg_t
) num
);
1732 return((AdrBk_Entry
*) NULL
);
1737 * Look up an entry in the address book given an address
1739 * Args: ab -- the address book
1740 * address -- address to match
1741 * entry_num -- if matched, return entry_num of match here
1743 * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
1745 * Note: When there are multiple occurrences of an address in an addressbook,
1746 * which there will be if more than one nickname points to same address, then
1747 * we want this to match the first occurrence so that the fcc you get will
1751 adrbk_lookup_by_addr(AdrBk
*ab
, char *address
, adrbk_cntr_t
*entry_num
)
1756 dprint((5, "- adrbk_lookup_by_addr(%s) (in %s) -\n",
1757 address
? address
: "?",
1758 (ab
&& ab
->filename
) ? ab
->filename
: "?"));
1760 if(!ab
|| !address
|| !address
[0])
1761 return((AdrBk_Entry
*)NULL
);
1763 num
= lookup_address_in_trie(ab
, address
);
1766 ae
= adrbk_get_ae(ab
, (a_c_arg_t
) num
);
1773 return((AdrBk_Entry
*)NULL
);
1778 * Format a full name.
1780 * Args: fullname -- full name out of address book for formatting
1781 * first -- Return a pointer to first name here.
1782 * last -- Return a pointer to last name here.
1784 * Result: Returns pointer to name formatted for a mail header. Space is
1785 * allocated here and should be freed by caller.
1787 * We need this because we store full names as Last, First.
1788 * If the name has no comma, then no change is made.
1789 * Otherwise the text before the first comma is moved to the end and
1790 * the comma is deleted.
1792 * Last and first have to be freed by caller.
1795 adrbk_formatname(char *fullname
, char **first
, char **last
)
1806 * There is an assumption that the fullname is a UTF-8 string.
1809 if(fullname
[0] != '"' && (comma
= strindex(fullname
, ',')) != NULL
){
1811 int last_name_len
= comma
- fullname
;
1814 while(*comma
&& isspace((unsigned char)*comma
))
1818 *first
= cpystr(comma
);
1821 *last
= (char *)fs_get((last_name_len
+ 1) * sizeof(char));
1822 strncpy(*last
, fullname
, last_name_len
);
1823 (*last
)[last_name_len
] = '\0';
1826 l
= strlen(comma
) + 1 + last_name_len
;
1827 new_name
= (char *) fs_get((l
+1) * sizeof(char));
1828 strncpy(new_name
, comma
, l
);
1830 strncat(new_name
, " ", l
+1-1-strlen(new_name
));
1832 strncat(new_name
, fullname
, MIN(last_name_len
,l
+1-1-strlen(new_name
)));
1836 new_name
= cpystr(fullname
);
1843 * Clear reference flags in preparation for a recursive lookup.
1845 * For loop detection during address book look up. This clears all the
1846 * referenced flags, then as the lookup proceeds the referenced flags can
1847 * be checked and set.
1850 adrbk_clearrefs(AdrBk
*ab
)
1852 adrbk_cntr_t entry_num
;
1855 dprint((9, "- adrbk_clearrefs -\n"));
1860 for(entry_num
= 0; entry_num
< ab
->count
; entry_num
++){
1861 ae
= adrbk_get_ae(ab
, (a_c_arg_t
) entry_num
);
1868 * Allocate a new AdrBk_Entry
1871 adrbk_newentry(void)
1875 ae
= (AdrBk_Entry
*) fs_get(sizeof(AdrBk_Entry
));
1883 * Just sets ae values to default.
1884 * Parts should be freed before calling this or they will leak.
1887 defvalue_ae(AdrBk_Entry
*ae
)
1889 ae
->nickname
= empty
;
1890 ae
->fullname
= empty
;
1891 ae
->addr
.addr
= empty
;
1900 copy_ae(AdrBk_Entry
*src
)
1904 a
= adrbk_newentry();
1906 a
->nickname
= cpystr(src
->nickname
? src
->nickname
: "");
1907 a
->fullname
= cpystr(src
->fullname
? src
->fullname
: "");
1908 a
->fcc
= cpystr(src
->fcc
? src
->fcc
: "");
1909 a
->extra
= cpystr(src
->extra
? src
->extra
: "");
1910 if(a
->tag
== Single
)
1911 a
->addr
.addr
= cpystr(src
->addr
.addr
? src
->addr
.addr
: "");
1912 else if(a
->tag
== List
){
1917 for(p
= src
->addr
.list
; p
&& *p
; p
++)
1923 n
= p
- src
->addr
.list
;
1925 a
->addr
.list
= (char **)fs_get((n
+1) * sizeof(char *));
1926 for(i
= 0; i
< n
; i
++)
1927 a
->addr
.list
[i
] = cpystr(src
->addr
.list
[i
]);
1929 a
->addr
.list
[n
] = NULL
;
1937 * Add an entry to the address book, or modify an existing entry
1939 * Args: ab -- address book to add to
1940 * old_entry_num -- the entry we want to modify. If this is NO_NEXT, then
1941 * we look up the nickname passed in to see if that's the
1942 * entry to modify, else it is a new entry.
1943 * nickname -- the nickname for new entry
1944 * fullname -- the fullname for new entry
1945 * address -- the address for new entry
1946 * fcc -- the fcc for new entry
1947 * extra -- the extra field for new entry
1948 * tag -- the type of new entry
1949 * new_entry_num -- return entry_num of new or modified entry here
1950 * resort_happened -- means that more than just the current entry changed,
1951 * either something was added or order was changed
1952 * enable_intr -- tell adrbk_write to enable interrupt handling
1953 * be_quiet -- tell adrbk_write to not do percent done messages
1954 * write_it -- only do adrbk_write if this is set
1956 * Result: return code: 0 all went well
1957 * -2 error writing address book, check errno
1958 * -3 no modification, the tag given didn't match
1960 * -4 tabs are in one of the fields passed in
1962 * If the nickname exists in the address book already, the operation is
1963 * considered a modification even if the case does not match exactly,
1964 * otherwise it is an add. The entry the operation occurs on is returned
1965 * in new. All fields are set to those passed in; that is, passing in NULL
1966 * even on a modification will set those fields to NULL as opposed to leaving
1967 * them unchanged. It is acceptable to pass in the current strings
1968 * in the entry in the case of modification. For address lists, the
1969 * structure passed in is what is used, so the storage has to all have
1970 * come from fs_get(). If the pointer passed in is the same as
1971 * the current field, no change is made.
1974 adrbk_add(AdrBk
*ab
, a_c_arg_t old_entry_num
, char *nickname
, char *fullname
,
1975 char *address
, char *fcc
, char *extra
, Tag tag
, adrbk_cntr_t
*new_entry_num
,
1976 int *resort_happened
, int enable_intr
, int be_quiet
, int write_it
)
1980 adrbk_cntr_t old_enum
;
1981 adrbk_cntr_t new_enum
;
1985 int set_mangled
= 0;
1987 dprint((3, "- adrbk_add(%s) -\n", nickname
? nickname
: ""));
1992 /* ---- Make sure there are no tabs in the stuff to add ------*/
1993 if((nickname
!= NULL
&& strindex(nickname
, TAB
) != NULL
) ||
1994 (fullname
!= NULL
&& strindex(fullname
, TAB
) != NULL
) ||
1995 (fcc
!= NULL
&& strindex(fcc
, TAB
) != NULL
) ||
1996 (tag
== Single
&& address
!= NULL
&& strindex(address
, TAB
) != NULL
))
2000 * Are we adding or updating ?
2002 * If old_entry_num was passed in, we're updating that. If nickname
2003 * already exists, we're updating that entry. Otherwise, this is an add.
2005 if((adrbk_cntr_t
)old_entry_num
!= NO_NEXT
){
2006 ae
= adrbk_get_ae(ab
, old_entry_num
);
2008 old_enum
= (adrbk_cntr_t
)old_entry_num
;
2011 ae
= adrbk_lookup_by_nick(ab
, nickname
, &old_enum
);
2013 if(ae
== NULL
){ /*----- adding a new entry ----*/
2015 ae
= adrbk_newentry();
2018 ae
->nickname
= cpystr(nickname
);
2020 ae
->fullname
= cpystr(fullname
);
2022 ae
->fcc
= cpystr(fcc
);
2024 ae
->extra
= cpystr(extra
);
2027 ae
->addr
.addr
= cpystr(address
);
2029 ae
->addr
.list
= (char **)NULL
;
2031 cmp_func
= (ab
->sort_rule
== AB_SORT_RULE_FULL_LISTS
) ?
2032 cmp_ae_by_full_lists_last
:
2033 (ab
->sort_rule
== AB_SORT_RULE_FULL
) ?
2035 (ab
->sort_rule
== AB_SORT_RULE_NICK_LISTS
) ?
2036 cmp_ae_by_nick_lists_last
:
2037 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2040 if(ab
->sort_rule
== AB_SORT_RULE_NONE
) /* put it last */
2041 new_enum
= ab
->count
;
2042 else /* Find slot for it */
2043 for(new_enum
= 0, a
= adrbk_get_ae(ab
, (a_c_arg_t
) new_enum
);
2044 a
!= (AdrBk_Entry
*)NULL
;
2045 a
= adrbk_get_ae(ab
, (a_c_arg_t
) (++new_enum
))){
2046 if((*cmp_func
)((qsort_t
*)&a
, (qsort_t
*)&ae
) >= 0)
2050 /* Insert ae before entry new_enum. */
2051 insert_ab_entry(ab
, (a_c_arg_t
) new_enum
, ae
, 0);
2053 if(F_OFF(F_EXPANDED_DISTLISTS
,ps_global
))
2054 exp_add_nth(ab
->exp
, (a_c_arg_t
)new_enum
);
2056 exp_add_nth(ab
->selects
, (a_c_arg_t
)new_enum
);
2059 * insert_ab_entry copies the pointers of ae so the things
2060 * being pointed to (nickname, ...) are still in use.
2061 * Don't free them but free the ae struct itself.
2063 fs_give((void **) &ae
);
2065 /*---- return in pointer if requested -----*/
2067 *new_entry_num
= new_enum
;
2070 *resort_happened
= 1;
2073 retval
= adrbk_write(ab
, (a_c_arg_t
) new_enum
, new_entry_num
, &set_mangled
,
2074 enable_intr
, be_quiet
);
2077 /*----- Updating an existing entry ----*/
2085 * Instead of just freeing and reallocating here we attempt to re-use
2086 * the space that was already allocated if possible.
2088 if(ae
->nickname
!= nickname
2089 && ae
->nickname
!= NULL
2091 && strcmp(nickname
, ae
->nickname
) != 0){
2094 /* can use already alloc'd space */
2095 if(ae
->nickname
!= NULL
&& nickname
!= NULL
&&
2096 strlen(nickname
) <= strlen(ae
->nickname
)){
2098 strncpy(ae
->nickname
, nickname
, strlen(ae
->nickname
)+1);
2101 if(ae
->nickname
!= NULL
&& ae
->nickname
!= empty
)
2102 fs_give((void **)&ae
->nickname
);
2104 ae
->nickname
= nickname
? cpystr(nickname
) : nickname
;
2108 if(ae
->fullname
!= fullname
2109 && ae
->fullname
!= NULL
2111 && strcmp(fullname
, ae
->fullname
) != 0){
2113 if(ae
->fullname
!= NULL
&& fullname
!= NULL
&&
2114 strlen(fullname
) <= strlen(ae
->fullname
)){
2116 strncpy(ae
->fullname
, fullname
, strlen(ae
->fullname
)+1);
2119 if(ae
->fullname
!= NULL
&& ae
->fullname
!= empty
)
2120 fs_give((void **)&ae
->fullname
);
2122 ae
->fullname
= fullname
? cpystr(fullname
) : fullname
;
2129 && strcmp(fcc
, ae
->fcc
) != 0){
2131 if(ae
->fcc
!= NULL
&& fcc
!= NULL
&&
2132 strlen(fcc
) <= strlen(ae
->fcc
)){
2134 strncpy(ae
->fcc
, fcc
, strlen(ae
->fcc
)+1);
2137 if(ae
->fcc
!= NULL
&& ae
->fcc
!= empty
)
2138 fs_give((void **)&ae
->fcc
);
2140 ae
->fcc
= fcc
? cpystr(fcc
) : fcc
;
2144 if(ae
->extra
!= extra
2145 && ae
->extra
!= NULL
2147 && strcmp(extra
, ae
->extra
) != 0){
2149 if(ae
->extra
!= NULL
&& extra
!= NULL
&&
2150 strlen(extra
) <= strlen(ae
->extra
)){
2152 strncpy(ae
->extra
, extra
, strlen(ae
->extra
)+1);
2155 if(ae
->extra
!= NULL
&& ae
->extra
!= empty
)
2156 fs_give((void **)&ae
->extra
);
2158 ae
->extra
= extra
? cpystr(extra
) : extra
;
2163 /*---- Single ----*/
2164 if(ae
->addr
.addr
!= address
2165 && ae
->addr
.addr
!= NULL
2167 && strcmp(address
, ae
->addr
.addr
) != 0){
2170 if(ae
->addr
.addr
!= NULL
&& address
!= NULL
&&
2171 strlen(address
) <= strlen(ae
->addr
.addr
)){
2173 strncpy(ae
->addr
.addr
, address
, strlen(ae
->addr
.addr
)+1);
2176 if(ae
->addr
.addr
!= NULL
&& ae
->addr
.addr
!= empty
)
2177 fs_give((void **)&ae
->addr
.addr
);
2179 ae
->addr
.addr
= address
? cpystr(address
) : address
;
2186 * We don't mess with lists here.
2187 * The caller has to do it with adrbk_listadd().
2193 /*---------- Make sure it's still in order ---------*/
2196 * old_enum is where ae is currently located
2197 * put it where it belongs
2200 new_enum
= re_sort_particular_entry(ab
, (a_c_arg_t
) old_enum
);
2201 if(old_enum
!= new_enum
)
2205 new_enum
= old_enum
;
2207 /*---- return in pointer if requested -----*/
2209 *new_entry_num
= new_enum
;
2212 *resort_happened
= (old_enum
!= new_enum
);
2215 repair_abook_tries(ab
);
2217 if(write_it
&& need_write
){
2218 int sort_happened
= 0;
2220 retval
= adrbk_write(ab
, (a_c_arg_t
) new_enum
, new_entry_num
,
2221 &sort_happened
, enable_intr
, be_quiet
);
2223 set_mangled
= sort_happened
;
2226 new_enum
= (*new_entry_num
);
2228 if(resort_happened
&& (sort_happened
|| (old_enum
!= new_enum
)))
2229 *resort_happened
= 1;
2236 ps_global
->mangled_screen
= 1;
2243 * Similar to adrbk_add, but lower cost. No sorting is done, the new entry
2244 * goes on the end. This won't work if it is an edit instead of an append.
2245 * The address book is not committed to disk.
2247 * Args: ab -- address book to add to
2248 * nickname -- the nickname for new entry
2249 * fullname -- the fullname for new entry
2250 * address -- the address for new entry
2251 * fcc -- the fcc for new entry
2252 * extra -- the extra field for new entry
2253 * tag -- the type of new entry
2255 * Result: return code: 0 all went well
2256 * -2 error writing address book, check errno
2257 * -3 no modification, the tag given didn't match
2259 * -4 tabs are in one of the fields passed in
2262 adrbk_append(AdrBk
*ab
, char *nickname
, char *fullname
, char *address
, char *fcc
,
2263 char *extra
, Tag tag
, adrbk_cntr_t
*new_entry_num
)
2267 dprint((3, "- adrbk_append(%s) -\n", nickname
? nickname
: ""));
2272 /* ---- Make sure there are no tabs in the stuff to add ------*/
2273 if((nickname
!= NULL
&& strindex(nickname
, TAB
) != NULL
) ||
2274 (fullname
!= NULL
&& strindex(fullname
, TAB
) != NULL
) ||
2275 (fcc
!= NULL
&& strindex(fcc
, TAB
) != NULL
) ||
2276 (tag
== Single
&& address
!= NULL
&& strindex(address
, TAB
) != NULL
))
2279 ae
= adrbk_newentry();
2282 ae
->nickname
= cpystr(nickname
);
2284 ae
->fullname
= cpystr(fullname
);
2286 ae
->fcc
= cpystr(fcc
);
2288 ae
->extra
= cpystr(extra
);
2291 ae
->addr
.addr
= cpystr(address
);
2293 ae
->addr
.list
= (char **)NULL
;
2296 *new_entry_num
= ab
->count
;
2298 insert_ab_entry(ab
, (a_c_arg_t
) ab
->count
, ae
, 0);
2301 * insert_ab_entry copies the pointers of ae so the things
2302 * being pointed to (nickname, ...) are still in use.
2303 * Don't free them but free the ae struct itself.
2305 fs_give((void **) &ae
);
2312 * The entire address book is assumed sorted correctly except perhaps for
2313 * entry number cur. Put it in the correct place. Return the new entry
2317 re_sort_particular_entry(AdrBk
*ab
, a_c_arg_t cur
)
2319 AdrBk_Entry
*ae_cur
, *ae_prev
, *ae_next
, *ae_small_enough
, *ae_big_enough
;
2321 adrbk_cntr_t big_enough
;
2322 adrbk_cntr_t new_entry_num
;
2323 int (*cmp_func
)(const qsort_t
*, const qsort_t
*);
2325 dprint((9, "- re_sort -\n"));
2327 cmp_func
= (ab
->sort_rule
== AB_SORT_RULE_FULL_LISTS
) ?
2328 cmp_ae_by_full_lists_last
:
2329 (ab
->sort_rule
== AB_SORT_RULE_FULL
) ?
2331 (ab
->sort_rule
== AB_SORT_RULE_NICK_LISTS
) ?
2332 cmp_ae_by_nick_lists_last
:
2333 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
2336 new_entry_num
= (adrbk_cntr_t
) cur
;
2338 if(ab
->sort_rule
== AB_SORT_RULE_NONE
)
2339 return(new_entry_num
);
2341 ae_cur
= adrbk_get_ae(ab
, cur
);
2344 ae_prev
= adrbk_get_ae(ab
, cur
- 1);
2346 if(cur
< ab
->count
-1)
2347 ae_next
= adrbk_get_ae(ab
, cur
+ 1);
2350 * A possible optimization here would be to implement some sort of
2351 * binary search to find where it goes instead of stepping through the
2352 * entries one at a time.
2355 (*cmp_func
)((qsort_t
*)&ae_cur
,(qsort_t
*)&ae_prev
) < 0){
2356 /*--- Out of order, needs to be moved up ----*/
2357 for(small_enough
= (long)cur
- 2; small_enough
>= 0L; small_enough
--){
2358 ae_small_enough
= adrbk_get_ae(ab
,(a_c_arg_t
) small_enough
);
2359 if((*cmp_func
)((qsort_t
*)&ae_cur
, (qsort_t
*)&ae_small_enough
) >= 0)
2362 new_entry_num
= (adrbk_cntr_t
)(small_enough
+ 1L);
2363 move_ab_entry(ab
, cur
, (a_c_arg_t
) new_entry_num
);
2365 else if(cur
< ab
->count
- 1 &&
2366 (*cmp_func
)((qsort_t
*)&ae_cur
, (qsort_t
*)&ae_next
) > 0){
2367 /*---- Out of order needs, to be moved towards end of list ----*/
2368 for(big_enough
= (adrbk_cntr_t
)(cur
+ 2);
2369 big_enough
< ab
->count
;
2371 ae_big_enough
= adrbk_get_ae(ab
, (a_c_arg_t
) big_enough
);
2372 if((*cmp_func
)((qsort_t
*)&ae_cur
, (qsort_t
*)&ae_big_enough
) <= 0)
2375 new_entry_num
= big_enough
- 1;
2376 move_ab_entry(ab
, cur
, (a_c_arg_t
) big_enough
);
2379 dprint((9, "- re_sort done -\n"));
2381 return(new_entry_num
);
2386 * Delete an entry from the address book
2388 * Args: ab -- the address book
2389 * entry_num -- entry to delete
2390 * save_deleted -- save deleted as a #DELETED- entry
2391 * enable_intr -- tell adrbk_write to enable interrupt handling
2392 * be_quiet -- tell adrbk_write to not do percent done messages
2393 * write_it -- only do adrbk_write if this is set
2395 * Result: returns: 0 if all went well
2396 * -1 if there is no such entry
2397 * -2 error writing address book, check errno
2400 adrbk_delete(AdrBk
*ab
, a_c_arg_t entry_num
, int save_deleted
, int enable_intr
,
2401 int be_quiet
, int write_it
)
2404 int set_mangled
= 0;
2406 dprint((3, "- adrbk_delete(%ld) -\n", (long)entry_num
));
2411 delete_ab_entry(ab
, entry_num
, save_deleted
);
2412 if(F_OFF(F_EXPANDED_DISTLISTS
,ps_global
))
2413 exp_del_nth(ab
->exp
, entry_num
);
2415 exp_del_nth(ab
->selects
, entry_num
);
2418 retval
= adrbk_write(ab
, 0, NULL
, &set_mangled
, enable_intr
, be_quiet
);
2421 ps_global
->mangled_screen
= 1;
2428 * Delete an address out of an address list
2430 * Args: ab -- the address book
2431 * entry_num -- the address list we are deleting from
2432 * addr -- address in above list to be deleted
2434 * Result: 0: Deletion complete, address book written
2435 * -1: Address for deletion not found
2436 * -2: Error writing address book. Check errno.
2438 * The address to be deleted is located by matching the string.
2441 adrbk_listdel(AdrBk
*ab
, a_c_arg_t entry_num
, char *addr
)
2446 int set_mangled
= 0;
2448 dprint((3, "- adrbk_listdel(%ld) -\n", (long) entry_num
));
2450 if(!ab
|| entry_num
>= ab
->count
)
2456 ae
= adrbk_get_ae(ab
, entry_num
);
2461 for(p
= ae
->addr
.list
; *p
; p
++)
2462 if(strcmp(*p
, addr
) == 0)
2468 /* note storage to be freed */
2474 /* slide all the entries below up (including NULL) */
2479 fs_give((void **) &to_free
);
2481 ret
= adrbk_write(ab
, 0, NULL
, &set_mangled
, 1, 0);
2484 ps_global
->mangled_screen
= 1;
2491 * Delete all addresses out of an address list
2493 * Args: ab -- the address book
2494 * entry_num -- the address list we are deleting from
2496 * Result: 0: Deletion complete, address book written
2497 * -1: Address for deletion not found
2498 * -2: Error writing address book. Check errno.
2501 adrbk_listdel_all(AdrBk
*ab
, a_c_arg_t entry_num
)
2506 dprint((3, "- adrbk_listdel_all(%ld) -\n", (long) entry_num
));
2508 if(!ab
|| entry_num
>= ab
->count
)
2511 ae
= adrbk_get_ae(ab
, entry_num
);
2517 for(p
= ae
->addr
.list
; p
&& *p
; p
++)
2519 fs_give((void **)p
);
2522 fs_give((void **) &ae
->addr
.list
);
2524 ae
->addr
.list
= NULL
;
2531 * Add a list of addresses to an already existing address list
2533 * Args: ab -- the address book
2534 * entry_num -- the address list we are adding to
2535 * addrs -- address list to be added
2536 * enable_intr -- tell adrbk_write to enable interrupt handling
2537 * be_quiet -- tell adrbk_write to not do percent done messages
2538 * write_it -- only do adrbk_write if this is set
2540 * Result: returns 0 : addition made, address book written
2541 * -1 : addition to non-list attempted
2542 * -2 : error writing address book -- check errno
2545 adrbk_nlistadd(AdrBk
*ab
, a_c_arg_t entry_num
, adrbk_cntr_t
*new_entry_num
,
2546 int *resort_happened
, char **addrs
,
2547 int enable_intr
, int be_quiet
, int write_it
)
2550 int cur_size
, size_of_additional_list
, new_size
;
2552 int set_mangled
= 0;
2555 dprint((3, "- adrbk_nlistadd(%ld) -\n", (long) entry_num
));
2557 if(!ab
|| entry_num
>= ab
->count
)
2560 ae
= adrbk_get_ae(ab
, entry_num
);
2565 /* count up size of existing list */
2566 for(p
= ae
->addr
.list
; p
!= NULL
&& *p
!= NULL
; p
++)
2569 cur_size
= p
- ae
->addr
.list
;
2571 /* count up size of new list */
2572 for(p
= addrs
; p
!= NULL
&& *p
!= NULL
; p
++)
2575 size_of_additional_list
= p
- addrs
;
2576 new_size
= cur_size
+ size_of_additional_list
;
2578 /* make room at end of list for it */
2580 ae
->addr
.list
= (char **) fs_get(sizeof(char *) * (new_size
+ 1));
2582 fs_resize((void **) &ae
->addr
.list
, sizeof(char *) * (new_size
+ 1));
2584 /* Put new list at the end */
2585 for(i
= cur_size
; i
< new_size
; i
++)
2586 (ae
->addr
.list
)[i
] = cpystr(addrs
[i
- cur_size
]);
2588 (ae
->addr
.list
)[new_size
] = NULL
;
2590 /*---- sort it into the correct place ------*/
2591 if(ab
->sort_rule
!= AB_SORT_RULE_NONE
)
2592 sort_addr_list(ae
->addr
.list
);
2595 rc
= adrbk_write(ab
, entry_num
, new_entry_num
, &set_mangled
, enable_intr
, be_quiet
);
2598 ps_global
->mangled_screen
= 1;
2600 *resort_happened
= 1;
2608 * Set the valid variable if we determine that the address book has
2609 * been changed by something other than us. This means we should update
2610 * our view of the address book when next possible.
2612 * Args ab -- AdrBk handle
2613 * do_it_now -- If > 0, check now regardless
2614 * If = 0, check if time since last chk more than default
2615 * If < 0, check if time since last chk more than -do_it_now
2618 adrbk_check_validity(AdrBk
*ab
, long int do_it_now
)
2620 dprint((9, "- adrbk_check_validity(%s) -\n",
2621 (ab
&& ab
->filename
) ? ab
->filename
: ""));
2623 if(!ab
|| ab
->flags
& FILE_OUTOFDATE
)
2626 adrbk_check_local_validity(ab
, do_it_now
);
2628 if(ab
->type
== Imap
&& !(ab
->flags
& FILE_OUTOFDATE
||
2629 ab
->rd
->flags
& REM_OUTOFDATE
))
2630 rd_check_remvalid(ab
->rd
, do_it_now
);
2635 * Set the valid variable if we determine that the address book has
2636 * been changed by something other than us. This means we should update
2637 * our view of the address book when next possible.
2639 * Args ab -- AdrBk handle
2640 * do_it_now -- If > 0, check now regardless
2641 * If = 0, check if time since last chk more than default
2642 * If < 0, check if time since last chk more than -do_it_now
2645 adrbk_check_local_validity(AdrBk
*ab
, long int do_it_now
)
2647 time_t mtime
, chk_interval
;
2649 dprint((9, "- adrbk_check_local_validity(%s) -\n",
2650 (ab
&& ab
->filename
) ? ab
->filename
: ""));
2656 chk_interval
= -1L * do_it_now
;
2660 chk_interval
= FILE_VALID_CHK_INTERVAL
;
2663 get_adj_time() <= ab
->last_local_valid_chk
+ chk_interval
)
2666 ab
->last_local_valid_chk
= get_adj_time();
2669 * Check local file for a modification time change.
2670 * If this is out of date, don't even bother checking for the remote
2671 * folder being out of date. That will get fixed when we reopen.
2673 if(!(ab
->flags
& FILE_OUTOFDATE
) &&
2674 ab
->last_change_we_know_about
!= (time_t)(-1) &&
2675 (mtime
=get_adj_name_file_mtime(ab
->filename
)) != (time_t)(-1) &&
2676 ab
->last_change_we_know_about
!= mtime
){
2678 dprint((2, "adrbk_check_local_validity: addrbook %s has changed\n",
2679 ab
->filename
? ab
->filename
: "?"));
2680 ab
->flags
|= FILE_OUTOFDATE
;
2686 * See if we can re-use an existing stream.
2688 * [ We don't believe we need this anymore now that the stuff in pine.c ]
2689 * [ is recycling streams for us, but we haven't thought it through all ]
2690 * [ the way enough to get rid of this. Hubert 2003-07-09 ]
2692 * Args name -- Name of folder we want a stream for.
2694 * Returns -- A mail stream suitable for status cmd or append cmd.
2695 * NULL if none were available.
2698 adrbk_handy_stream(char *name
)
2700 MAILSTREAM
*stat_stream
= NULL
;
2703 dprint((9, "- adrbk_handy_stream(%s) -\n", name
? name
: "?"));
2705 stat_stream
= sp_stream_get(name
, SP_SAME
);
2708 * Look through our imap streams to see if there is one we can use.
2710 for(i
= 0; !stat_stream
&& i
< as
.n_addrbk
; i
++){
2713 pab
= &as
.adrbks
[i
];
2715 if(pab
->address_book
&&
2716 pab
->address_book
->type
== Imap
&&
2717 pab
->address_book
->rd
&&
2718 pab
->address_book
->rd
->type
== RemImap
&&
2719 same_stream(name
, pab
->address_book
->rd
->t
.i
.stream
)){
2720 stat_stream
= pab
->address_book
->rd
->t
.i
.stream
;
2721 pab
->address_book
->rd
->last_use
= get_adj_time();
2723 "%s: used other abook stream for status (%ld)\n",
2724 pab
->address_book
->orig_filename
2725 ? pab
->address_book
->orig_filename
: "?",
2726 (long)pab
->address_book
->rd
->last_use
));
2730 dprint((9, "adrbk_handy_stream: returning %s\n",
2731 stat_stream
? "good stream" : "NULL"));
2733 return(stat_stream
);
2738 * Close address book
2740 * All that is done here is to free the storage, since the address book is
2741 * rewritten on every change.
2744 adrbk_close(AdrBk
*ab
)
2748 dprint((4, "- adrbk_close(%s) -\n",
2749 (ab
&& ab
->filename
) ? ab
->filename
: ""));
2755 rd_close_remdata(&ab
->rd
);
2758 (void)fclose(ab
->fp
);
2760 if(ab
->our_filecopy
&& ab
->filename
!= ab
->our_filecopy
){
2761 our_unlink(ab
->our_filecopy
);
2762 fs_give((void**) &ab
->our_filecopy
);
2767 fs_give((void **)&ab
->exp
); /* free head of list, too */
2771 exp_free(ab
->checks
);
2772 fs_give((void **)&ab
->checks
); /* free head of list, too */
2776 exp_free(ab
->selects
);
2777 fs_give((void **)&ab
->selects
); /* free head of list, too */
2781 adrbk_cntr_t entry_num
;
2784 * ab->arr is an allocated array. Each element of the array contains
2785 * several pointers that point to other allocated stuff, so we
2786 * free_ae_parts to get those and then free the array after the loop.
2788 for(entry_num
= 0; entry_num
< ab
->count
; entry_num
++)
2789 free_ae_parts(&ab
->arr
[entry_num
]);
2791 fs_give((void **) &ab
->arr
);
2795 adrbk_cntr_t entry_num
;
2797 for(entry_num
= 0; entry_num
< ab
->del_count
; entry_num
++)
2798 free_ae_parts(&ab
->del
[entry_num
]);
2800 fs_give((void **) &ab
->del
);
2804 free_abook_trie(&ab
->nick_trie
);
2807 free_abook_trie(&ab
->addr_trie
);
2810 free_abook_trie(&ab
->full_trie
);
2812 if(ab
->revfull_trie
)
2813 free_abook_trie(&ab
->revfull_trie
);
2819 if(ab
->flags
& DEL_FILE
)
2820 our_unlink(ab
->filename
);
2822 fs_give((void**)&ab
->filename
);
2825 if(ab
->orig_filename
)
2826 fs_give((void**)&ab
->orig_filename
);
2828 fs_give((void **) &ab
);
2833 adrbk_partial_close(AdrBk
*ab
)
2835 dprint((4, "- adrbk_partial_close(%s) -\n",
2836 (ab
&& ab
->filename
) ? ab
->filename
: ""));
2838 exp_free(ab
->exp
); /* leaves head of list */
2839 exp_free(ab
->checks
); /* leaves head of list */
2844 * It has been noticed that this stream is dead and it is about to
2845 * be removed from the stream pool. We may have a pointer to it for one
2846 * of the remote address books. We have to note that the pointer is stale.
2849 note_closed_adrbk_stream(MAILSTREAM
*stream
)
2857 for(i
= 0; i
< as
.n_addrbk
; i
++){
2858 pab
= &as
.adrbks
[i
];
2859 if(pab
->address_book
2860 && pab
->address_book
->type
== Imap
2861 && pab
->address_book
->rd
2862 && pab
->address_book
->rd
->type
== RemImap
2863 && (stream
== pab
->address_book
->rd
->t
.i
.stream
)){
2864 dprint((4, "- note_closed_adrbk_stream(%s) -\n",
2865 (pab
->address_book
&& pab
->address_book
->orig_filename
)
2866 ? pab
->address_book
->orig_filename
: ""));
2867 pab
->address_book
->rd
->t
.i
.stream
= NULL
;
2873 static adrbk_cntr_t tot_for_percent
;
2874 static adrbk_cntr_t entry_num_for_percent
;
2877 * Write out the address book.
2879 * If be_quiet is set, don't turn on busy_cue.
2881 * If enable_intr_handling is set, turn on and off interrupt handling.
2883 * Format is as in comment in the adrbk_open routine. Lines are wrapped
2884 * to be under 80 characters. This is called on every change to the
2885 * address book. Write is first to a temporary file,
2886 * which is then renamed to be the real address book so that we won't
2887 * destroy the real address book in case of something like a full file
2890 * Writing a temp file and then renaming has the bad side affect of
2891 * destroying links. It also overrides any read only permissions on
2892 * the mail file since rename ignores such permissions. However, we
2893 * handle readonly-ness in addrbook.c before we call this.
2894 * We retain the permissions by doing a stat on the old file and a
2895 * chmod on the new one (with file_attrib_copy).
2897 * In pre-alpine pine the address book entries were encoded on disk.
2898 * We would be happy with raw UTF-8 now but in order to preserve some
2899 * backwards compatibility we encode the entries before writing.
2900 * We first try to translate to the user's character set and encode
2901 * in that, else we use UTF-8 but with 1522 encoding.
2903 * Returns: 0 write was successful
2908 adrbk_write(AdrBk
*ab
, a_c_arg_t current_entry_num
, adrbk_cntr_t
*new_entry_num
,
2909 int *sort_happened
, int enable_intr_handling
, int be_quiet
)
2911 FILE *ab_stream
= NULL
;
2912 AdrBk_Entry
*ae
= NULL
;
2913 adrbk_cntr_t entry_num
;
2915 void (*save_sighup
)();
2918 max_full
= 0, full_two
= 0, full_three
= 0,
2919 max_fcc
= 0, fcc_two
= 0, fcc_three
= 0,
2920 max_addr
= 0, addr_two
= 0, addr_three
= 0,
2921 this_nick_width
, this_full_width
, this_addr_width
,
2922 this_fcc_width
, fd
, i
;
2923 int interrupt_happened
= 0, we_cancel
= 0, we_turned_on
= 0;
2924 char *temp_filename
= NULL
;
2925 WIDTH_INFO_S
*widths
;
2930 dprint((2, "- adrbk_write(\"%s\") - writing %lu entries\n",
2931 ab
->filename
? ab
->filename
: "", (unsigned long) ab
->count
));
2935 adrbk_check_local_validity(ab
, 1L);
2936 /* verify that file has not been changed by something else */
2937 if(ab
->flags
& FILE_OUTOFDATE
){
2938 /* It has changed! */
2939 q_status_message(SM_ORDER
| SM_DING
, 5, 15,
2940 /* TRANSLATORS: The address book was changed by something else so alpine
2941 is not making the change the user wanted to make to avoid damaging
2942 the address book. */
2943 _("Addrbook changed by another process, aborting our change to avoid damage..."));
2944 dprint((1, "adrbk_write: addrbook %s changed while we had it open, aborting write\n",
2945 ab
->filename
? ab
->filename
: "?"));
2946 longjmp(addrbook_changed_unexpectedly
, 1);
2951 * Verify that remote folder has not been
2952 * changed by something else (not if checked in last 5 seconds).
2954 * The -5 is so we won't check every time on a series of quick writes.
2956 rd_check_remvalid(ab
->rd
, -5L);
2957 if(ab
->type
== Imap
){
2961 * We'll eventually need this open to write to remote, so see if
2962 * we can open it now.
2964 rd_open_remote(ab
->rd
);
2967 * Did someone else change the remote copy?
2969 if((ro
=rd_remote_is_readonly(ab
->rd
)) || ab
->rd
->flags
& REM_OUTOFDATE
){
2971 q_status_message(SM_ORDER
| SM_DING
, 5, 15,
2972 _("Can't access remote addrbook, aborting change..."));
2974 "adrbk_write: Can't write to remote addrbook %s, aborting write: open failed\n",
2975 ab
->rd
->rn
? ab
->rd
->rn
: "?"));
2978 if(!(ab
->rd
->flags
& NO_META_UPDATE
)){
2979 unsigned long save_chk_nmsgs
;
2982 * Should have some non-type-specific method of doing this.
2984 switch(ab
->rd
->type
){
2986 save_chk_nmsgs
= ab
->rd
->t
.i
.chk_nmsgs
;
2987 ab
->rd
->t
.i
.chk_nmsgs
= 0;/* cause it to be OUTOFDATE */
2988 rd_write_metadata(ab
->rd
, 0);
2989 ab
->rd
->t
.i
.chk_nmsgs
= save_chk_nmsgs
;
2993 q_status_message(SM_ORDER
| SM_DING
, 3, 5,
2994 "Adrbk_write: Type not supported");
2999 q_status_message(SM_ORDER
| SM_DING
, 5, 15,
3000 _("No write permission for remote addrbook, aborting change..."));
3003 q_status_message(SM_ORDER
| SM_DING
, 5, 15,
3004 _("Remote addrbook changed, aborting our change to avoid damage..."));
3006 "adrbk_write: remote addrbook %s changed while we had it open, aborting write\n",
3007 ab
->orig_filename
? ab
->orig_filename
: "?"));
3010 rd_close_remote(ab
->rd
);
3012 longjmp(addrbook_changed_unexpectedly
, 1);
3018 save_sighup
= (void (*)())signal(SIGHUP
, SIG_IGN
);
3022 * If we want to be able to modify the address book, we will
3023 * need a temp_filename in the same directory as our_filecopy.
3025 if(!(ab
->our_filecopy
&& ab
->our_filecopy
[0])
3026 || !(temp_filename
= tempfile_in_same_dir(ab
->our_filecopy
,"a1",NULL
))
3027 || (fd
= our_open(temp_filename
, OPEN_WRITE_MODE
, 0600)) < 0){
3028 dprint((1, "adrbk_write(%s): failed opening temp file (%s)\n",
3029 ab
->filename
? ab
->filename
: "?",
3030 temp_filename
? temp_filename
: "NULL"));
3034 ab_stream
= fdopen(fd
, "wb");
3035 if(ab_stream
== NULL
){
3036 dprint((1, "adrbk_write(%s): fdopen failed\n", temp_filename
? temp_filename
: "?"));
3040 if(adrbk_is_in_sort_order(ab
, be_quiet
)){
3045 *new_entry_num
= current_entry_num
;
3051 (void) adrbk_sort(ab
, current_entry_num
, new_entry_num
, be_quiet
);
3054 /* accept keyboard interrupts */
3055 if(enable_intr_handling
)
3056 we_turned_on
= intr_handling_on();
3059 tot_for_percent
= MAX(ab
->count
, 1);
3060 entry_num_for_percent
= 0;
3061 we_cancel
= busy_cue(_("Saving address book"), percent_abook_saved
, 0);
3067 * If there are any old deleted entries, copy them to new file.
3069 if(ab
->del_count
> 0){
3074 * If there are deleted entries old enough that we no longer want
3075 * to save them, remove them from the list.
3077 for(delindex
= (long) ab
->del_count
-1;
3078 delindex
>= 0L && ab
->del_count
> 0; delindex
--){
3080 ae
= adrbk_get_delae(ab
, (a_c_arg_t
) delindex
);
3081 nickname
= ae
->nickname
;
3083 if(strncmp(nickname
, DELETED
, DELETED_LEN
) == 0
3084 && isdigit((unsigned char)nickname
[DELETED_LEN
])
3085 && isdigit((unsigned char)nickname
[DELETED_LEN
+1])
3086 && nickname
[DELETED_LEN
+2] == '/'
3087 && isdigit((unsigned char)nickname
[DELETED_LEN
+3])
3088 && isdigit((unsigned char)nickname
[DELETED_LEN
+4])
3089 && nickname
[DELETED_LEN
+5] == '/'
3090 && isdigit((unsigned char)nickname
[DELETED_LEN
+6])
3091 && isdigit((unsigned char)nickname
[DELETED_LEN
+7])
3092 && nickname
[DELETED_LEN
+8] == '#'){
3093 int year
, month
, day
;
3094 struct tm
*tm_before
;
3097 now
= time((time_t *)0);
3098 before
= now
- (time_t)ABOOK_DELETED_EXPIRE_TIME
;
3099 tm_before
= localtime(&before
);
3100 tm_before
->tm_mon
++;
3103 * Check to see if it is older than 100 days.
3105 year
= atoi(&nickname
[DELETED_LEN
]);
3106 month
= atoi(&nickname
[DELETED_LEN
+3]);
3107 day
= atoi(&nickname
[DELETED_LEN
+6]);
3109 year
+= 100; /* this breaks in year 2095 */
3112 * remove it if it is more than 100 days old
3114 if(!(year
> tm_before
->tm_year
3115 || (year
== tm_before
->tm_year
3116 && month
> tm_before
->tm_mon
)
3117 || (year
== tm_before
->tm_year
3118 && month
== tm_before
->tm_mon
3119 && day
>= tm_before
->tm_mday
))){
3121 /* it's old, dump it */
3124 /* patch the array by moving everything below up */
3125 if(delindex
< ab
->del_count
){
3126 memmove(&ab
->del
[delindex
],
3127 &ab
->del
[delindex
+1],
3128 (ab
->del_count
- delindex
) *
3129 sizeof(AdrBk_Entry
));
3135 /* write out what remains */
3137 for(delindex
= 0L; delindex
< ab
->del_count
; delindex
++){
3138 ae
= adrbk_get_delae(ab
, (a_c_arg_t
) delindex
);
3139 if(write_single_abook_entry(ae
, ab_stream
, NULL
, NULL
,
3140 NULL
, NULL
) == EOF
){
3141 dprint((1, "adrbk_write(%s): failed writing deleted entry\n",
3142 temp_filename
? temp_filename
: "?"));
3148 /* nothing left, get rid of del array */
3149 fs_give((void **) &ab
->del
);
3154 dprint((4, " adrbk_write: saving %ld deleted entries\n",
3155 (long) ab
->del_count
));
3157 for(entry_num
= 0; entry_num
< ab
->count
; entry_num
++){
3158 entry_num_for_percent
++;
3162 ae
= adrbk_get_ae(ab
, (a_c_arg_t
) entry_num
);
3164 if(ae
== (AdrBk_Entry
*) NULL
){
3165 dprint((1, "adrbk_write(%s): can't find ae while writing addrbook, entry_num = %ld\n",
3166 ab
->filename
? ab
->filename
: "?",
3171 /* write to temp file */
3172 if(write_single_abook_entry(ae
, ab_stream
, &this_nick_width
,
3173 &this_full_width
, &this_addr_width
, &this_fcc_width
) == EOF
){
3174 dprint((1, "adrbk_write(%s): failed writing for entry %ld\n",
3175 temp_filename
? temp_filename
: "?",
3180 /* keep track of widths */
3181 max_nick
= MAX(max_nick
, this_nick_width
);
3182 if(this_full_width
> max_full
){
3183 full_three
= full_two
;
3184 full_two
= max_full
;
3185 max_full
= this_full_width
;
3187 else if(this_full_width
> full_two
){
3188 full_three
= full_two
;
3189 full_two
= this_full_width
;
3191 else if(this_full_width
> full_three
){
3192 full_three
= this_full_width
;
3195 if(this_addr_width
> max_addr
){
3196 addr_three
= addr_two
;
3197 addr_two
= max_addr
;
3198 max_addr
= this_addr_width
;
3200 else if(this_addr_width
> addr_two
){
3201 addr_three
= addr_two
;
3202 addr_two
= this_addr_width
;
3204 else if(this_addr_width
> addr_three
){
3205 addr_three
= this_addr_width
;
3208 if(this_fcc_width
> max_fcc
){
3209 fcc_three
= fcc_two
;
3211 max_fcc
= this_fcc_width
;
3213 else if(this_fcc_width
> fcc_two
){
3214 fcc_three
= fcc_two
;
3215 fcc_two
= this_fcc_width
;
3217 else if(this_fcc_width
> fcc_three
){
3218 fcc_three
= this_fcc_width
;
3222 * Check to see if we've been interrupted. We check at the bottom
3223 * of the loop so that we can handle an interrupt in the last
3226 if(enable_intr_handling
&& ps_global
->intr_pending
){
3227 interrupt_happened
++;
3233 cancel_busy_cue(-1);
3237 if(enable_intr_handling
&& we_turned_on
){
3238 intr_handling_off();
3242 if(fclose(ab_stream
) == EOF
){
3243 dprint((1, "adrbk_write: fclose for %s failed\n",
3244 temp_filename
? temp_filename
: "?"));
3248 ab_stream
= (FILE *) NULL
;
3250 file_attrib_copy(temp_filename
, ab
->our_filecopy
);
3252 (void) fclose(ab
->fp
);
3253 ab
->fp
= NULL
; /* in case of problems */
3256 if((i
=rename_file(temp_filename
, ab
->our_filecopy
)) < 0){
3257 dprint((1, "adrbk_write: rename(%s, %s) failed: %s\n",
3258 temp_filename
? temp_filename
: "?",
3259 ab
->our_filecopy
? ab
->our_filecopy
: "?",
3260 error_description(errno
)));
3263 q_status_message2(SM_ORDER
| SM_DING
, 5, 7,
3264 _("Can't replace address book %.200sfile \"%.200s\""),
3265 (ab
->type
== Imap
) ? "cache " : "",
3267 q_status_message(SM_ORDER
| SM_DING
, 5, 7,
3268 _("If another Alpine is running, quit that Alpine before updating address book."));
3270 #endif /* _WINDOWS */
3274 /* reopen fp to new file */
3275 if(!(ab
->fp
= our_fopen(ab
->our_filecopy
, "rb"))){
3276 dprint((1, "adrbk_write: can't reopen %s\n",
3277 ab
->our_filecopy
? ab
->our_filecopy
: "?"));
3282 our_unlink(temp_filename
);
3283 fs_give((void **) &temp_filename
);
3287 * Now copy our_filecopy back to filename.
3289 if(ab
->filename
!= ab
->our_filecopy
){
3293 if(!(ab
->filename
&& ab
->filename
[0])
3294 || !(temp_filename
= tempfile_in_same_dir(ab
->filename
,"a1",NULL
))
3295 || (fd
= our_open(temp_filename
, OPEN_WRITE_MODE
, 0600)) < 0){
3297 "adrbk_write(%s): failed opening temp file (%s)\n",
3298 ab
->filename
? ab
->filename
: "?",
3299 temp_filename
? temp_filename
: "NULL"));
3304 ab_stream
= fdopen(fd
, "wb");
3306 if(ab_stream
== NULL
){
3307 dprint((1, "adrbk_write(%s): fdopen failed\n",
3308 temp_filename
? temp_filename
: "?"));
3314 while((c
= getc(ab
->fp
)) != EOF
)
3315 if(putc(c
, ab_stream
) == EOF
){
3321 if(!err
&& fclose(ab_stream
) == EOF
)
3329 file_attrib_copy(temp_filename
, ab
->filename
);
3332 * This could fail if somebody else has it open, but they should
3333 * only have it open for a short time.
3335 while(!err
&& rename_file(temp_filename
, ab
->filename
) < 0){
3342 q_status_message2(SM_ORDER
, 0, 3,
3343 _("Replace of \"%.200s\" failed, trying %.200s"),
3344 (lc
=last_cmpnt(ab
->filename
)) ? lc
: ab
->filename
,
3345 (tries
> 1) ? "again" : "one more time");
3346 display_message('x');
3357 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
3358 _("Copy of addrbook to \"%.200s\" failed, changes NOT saved!"),
3359 (lc
=last_cmpnt(ab
->filename
)) ? lc
: ab
->filename
);
3360 dprint((2, "adrbk_write: failed copying our_filecopy (%s)back to filename (%s): %s, continuing without a net\n",
3361 ab
->our_filecopy
? ab
->our_filecopy
: "?",
3362 ab
->filename
? ab
->filename
: "?",
3363 error_description(errno
)));
3367 widths
= &ab
->widths
;
3368 widths
->max_nickname_width
= MIN(max_nick
, 99);
3369 widths
->max_fullname_width
= MIN(max_full
, 99);
3370 widths
->max_addrfield_width
= MIN(max_addr
, 99);
3371 widths
->max_fccfield_width
= MIN(max_fcc
, 99);
3372 widths
->third_biggest_fullname_width
= MIN(full_three
, 99);
3373 widths
->third_biggest_addrfield_width
= MIN(addr_three
, 99);
3374 widths
->third_biggest_fccfield_width
= MIN(fcc_three
, 99);
3376 /* record new change date of addrbook file */
3377 ab
->last_change_we_know_about
= get_adj_name_file_mtime(ab
->filename
);
3380 (void)signal(SIGHUP
, save_sighup
);
3384 * If this is a remote addressbook, copy the file over.
3385 * If it fails we warn but continue to operate on the changed,
3386 * locally cached addressbook file.
3388 if(ab
->type
== Imap
){
3394 cancel_busy_cue(-1);
3396 we_cancel
= busy_cue(_("Copying to remote addressbook"), NULL
, 1);
3398 * We don't want a cookie upgrade to blast our data in rd->lf by
3399 * copying back the remote data before doing the upgrade, and then
3400 * proceeding to finish with that data. So we tell rd_upgrade_cookie
3403 ab
->rd
->flags
|= BELIEVE_CACHE
;
3404 if((e
= rd_update_remote(ab
->rd
, datebuf
)) != 0){
3406 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
3407 _("Error opening temporary addrbook file %.200s: %.200s"),
3408 ab
->rd
->lf
, error_description(errno
));
3410 "adrbk_write: error opening temp file %s\n",
3411 ab
->rd
->lf
? ab
->rd
->lf
: "?"));
3414 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
3415 _("Error copying to %.200s: %.200s"),
3416 ab
->rd
->rn
, error_description(errno
));
3418 "adrbk_write: error copying from %s to %s\n",
3419 ab
->rd
->lf
? ab
->rd
->lf
: "?",
3420 ab
->rd
->rn
? ab
->rd
->rn
: "?"));
3423 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
3424 _("Copy of addrbook to remote folder failed, changes NOT saved remotely"));
3427 rd_update_metadata(ab
->rd
, datebuf
);
3428 ab
->rd
->read_status
= 'W';
3430 "%s: copied local to remote in adrbk_write (%ld)\n",
3431 ab
->rd
->rn
? ab
->rd
->rn
: "?",
3432 (long)ab
->rd
->last_use
));
3435 ab
->rd
->flags
&= ~BELIEVE_CACHE
;
3444 our_unlink(temp_filename
);
3445 fs_give((void **) &temp_filename
);
3453 cancel_busy_cue(-1);
3455 if(enable_intr_handling
&& we_turned_on
)
3456 intr_handling_off();
3459 (void)signal(SIGHUP
, save_sighup
);
3461 if(interrupt_happened
){
3462 q_status_message(0, 1, 2, _("Interrupt! Reverting to previous version"));
3463 display_message('x');
3464 dprint((1, "adrbk_write(%s): Interrupt\n",
3465 ab
->filename
? ab
->filename
: "?"));
3468 dprint((1, "adrbk_write(%s) (%s): some sort of io_error\n",
3469 ab
->filename
? ab
->filename
: "?",
3470 ab
->our_filecopy
? ab
->our_filecopy
: "?"));
3474 if(ab_stream
!= NULL
){
3476 ab_stream
= (FILE *)NULL
;
3480 our_unlink(temp_filename
);
3481 fs_give((void **) &temp_filename
);
3484 adrbk_partial_close(ab
);
3486 if(interrupt_happened
){
3487 errno
= EINTR
; /* for nicer error message */
3496 * Writes one addrbook entry with wrapping. Fills in widths
3497 * for display purposes. Returns 0, or EOF on error.
3499 * Continuation lines always start with spaces. Tabs are treated as
3500 * separators, never as whitespace. When we output tab separators we
3501 * always put them on the ends of lines, never on the start of a line
3502 * after a continuation. That is, there is always something printable
3503 * after continuation spaces.
3506 write_single_abook_entry(AdrBk_Entry
*ae
, FILE *fp
, int *ret_nick_width
,
3507 int *ret_full_width
, int *ret_addr_width
, int *ret_fcc_width
)
3510 int nick_width
= 0, full_width
= 0, addr_width
= 0, fcc_width
= 0;
3511 int tmplen
, this_len
;
3512 char *write_this
= NULL
;
3514 if(fp
== (FILE *) NULL
){
3515 dprint((1, "write_single_abook_entry: fp is NULL\n"));
3520 nick_width
= utf8_width(ae
->nickname
);
3522 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3523 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, ae
->nickname
);
3525 this_len
= strlen(write_this
? write_this
: "");
3528 * We aren't too concerned with where it wraps.
3529 * Long lines are ok as long as they aren't super long.
3531 if(len
> 100 || (len
+this_len
> 150 && len
> 20)){
3532 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3534 "write_single_abook_entry: fputs ind1 failed\n"));
3538 len
= this_len
+ INDENT
;
3543 if(fputs(write_this
, fp
) == EOF
){
3545 "write_single_abook_entry: fputs nick failed\n"));
3556 full_width
= utf8_width(ae
->fullname
);
3558 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3559 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, ae
->fullname
);
3561 this_len
= strlen(write_this
? write_this
: "");
3564 if(len
> 100 || (len
+this_len
> 150 && len
> 20)){
3565 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3567 "write_single_abook_entry: fputs ind2 failed\n"));
3571 len
= this_len
+ INDENT
;
3576 if(fputs(write_this
, fp
) == EOF
){
3578 "write_single_abook_entry: fputs full failed\n"));
3588 /* special case, make sure empty list has () */
3589 if(ae
->tag
== List
&& ae
->addr
.list
== NULL
){
3595 else if(ae
->addr
.addr
!= NULL
|| ae
->addr
.list
!= NULL
){
3596 if(ae
->tag
== Single
){
3597 /*----- Single: just one address ----*/
3599 addr_width
= utf8_width(ae
->addr
.addr
);
3601 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3602 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, ae
->addr
.addr
);
3604 this_len
= strlen(write_this
? write_this
: "");
3606 if(len
> 100 || (len
+this_len
> 150 && len
> 20)){
3607 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3609 "write_single_abook_entry: fputs ind3 failed\n"));
3613 len
= this_len
+ INDENT
;
3618 if(fputs(write_this
, fp
) == EOF
){
3620 "write_single_abook_entry: fputs addr failed\n"));
3627 else if(ae
->tag
== List
){
3630 /*----- List: a distribution list ------*/
3634 for(a2
= ae
->addr
.list
; *a2
!= NULL
; a2
++){
3636 if(a2
!= ae
->addr
.list
){
3641 addr_width
= MAX(addr_width
, utf8_width(*a2
));
3643 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3644 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, *a2
);
3646 this_len
= strlen(write_this
? write_this
: "");
3648 if(len
> 100 || (len
+this_len
> 150 && len
> 20)){
3649 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3651 "write_single_abook_entry: fputs ind3 failed\n"));
3655 len
= this_len
+ INDENT
;
3660 if(fputs(write_this
, fp
) == EOF
){
3662 "write_single_abook_entry: fputs addrl failed\n"));
3673 /* If either fcc or extra exists, output both, otherwise, neither */
3674 if((ae
->fcc
&& ae
->fcc
[0]) || (ae
->extra
&& ae
->extra
[0])){
3678 if(ae
->fcc
&& ae
->fcc
[0]){
3679 fcc_width
= utf8_width(ae
->fcc
);
3681 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3682 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, ae
->fcc
);
3684 this_len
= strlen(write_this
? write_this
: "");
3686 if(len
> 100 || (len
+this_len
> 150 && len
> 20)){
3687 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3689 "write_single_abook_entry: fputs ind4 failed\n"));
3693 len
= this_len
+ INDENT
;
3698 if(fputs(write_this
, fp
) == EOF
){
3700 "write_single_abook_entry: fputs fcc failed\n"));
3710 if(ae
->extra
&& ae
->extra
[0]){
3715 write_this
= backcompat_encoding_for_abook(tmp_20k_buf
, 10000,
3716 tmp_20k_buf
+10000, SIZEOF_20KBUF
-10000, ae
->extra
);
3719 * Copy ae->extra and replace newlines with spaces.
3720 * The reason we do this is because the continuation lines
3721 * produced by rfc1522_encode may interact with the
3722 * continuation lines produced below in a bad way.
3723 * In particular, what can happen is that the 1522 continuation
3724 * newline can be followed immediately by a newline produced
3725 * below. That breaks the continuation since that is a
3726 * line with nothing on it. Just turn 1522 continuations into
3727 * spaces, which work fine with 1522_decode.
3729 extra_copy
= cpystr(write_this
);
3730 REPLACE_NEWLINES_WITH_SPACE(extra_copy
);
3732 tmplen
= strlen(extra_copy
);
3734 if(len
> 100 || (len
+tmplen
> 150 && len
> 20)){
3735 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTSTR
) == EOF
){
3737 "write_single_abook_entry: fprintf indent failed\n"));
3744 space
= MAX(70 - len
, 5);
3748 if(end
-cur
> space
){
3751 /* find first space after spot we want to break */
3752 for(i
= space
; cur
+i
< end
&& cur
[i
] != SPACE
; i
++)
3756 if(fputs(cur
, fp
) == EOF
){
3758 "write_single_abook_entry: fputs extra failed\n"));
3765 if(fprintf(fp
, "%s%s", NEWLINE
, INDENTXTRA
) == EOF
){
3767 "write_single_abook_entry: fprintf indent failed\n"));
3771 space
= 70 - INDENT
;
3775 if(fputs(cur
, fp
) == EOF
){
3777 "write_single_abook_entry: fputs extra failed\n"));
3786 fs_give((void **)&extra_copy
);
3792 fprintf(fp
, "%s", NEWLINE
);
3795 *ret_nick_width
= nick_width
;
3797 *ret_full_width
= full_width
;
3799 *ret_addr_width
= addr_width
;
3801 *ret_fcc_width
= fcc_width
;
3808 backcompat_encoding_for_abook(char *buf1
, size_t buf1len
, char *buf2
,
3809 size_t buf2len
, char *srcstr
)
3811 char *encoded
= NULL
;
3816 for(p
= srcstr
; *p
&& its_ascii
; p
++)
3820 /* if it is ascii, go with that */
3824 char *trythischarset
= NULL
;
3827 * If it is possible to translate the UTF-8
3828 * string into the user's character set then
3829 * do that. For backwards compatibility with
3832 if(ps_global
->keyboard_charmap
&& ps_global
->keyboard_charmap
[0])
3833 trythischarset
= ps_global
->keyboard_charmap
;
3834 else if(ps_global
->display_charmap
&& ps_global
->display_charmap
[0])
3835 trythischarset
= ps_global
->display_charmap
;
3840 src
.data
= (unsigned char *) srcstr
;
3841 src
.size
= strlen(srcstr
);
3842 memset(&dst
, 0, sizeof(dst
));
3843 if(utf8_cstext(&src
, trythischarset
, &dst
, 0)){
3845 strncpy(buf1
, (char *) dst
.data
, buf1len
);
3846 buf1
[buf1len
-1] = '\0';
3847 fs_give((void **) &dst
.data
);
3848 encoded
= rfc1522_encode(buf2
, buf2len
,
3849 (unsigned char *) buf1
, trythischarset
);
3851 REPLACE_NEWLINES_WITH_SPACE(encoded
);
3857 encoded
= rfc1522_encode(buf1
, buf1len
, (unsigned char *) srcstr
, "UTF-8");
3859 REPLACE_NEWLINES_WITH_SPACE(encoded
);
3868 percent_abook_saved(void)
3870 return((int)(((unsigned long)entry_num_for_percent
* (unsigned long)100) /
3871 (unsigned long)tot_for_percent
));
3876 * Free memory associated with entry ae.
3878 * Args: ae -- Address book entry to be freed.
3881 free_ae(AdrBk_Entry
**ae
)
3887 fs_give((void **) ae
);
3892 * Free memory associated with entry ae but not ae itself.
3895 free_ae_parts(AdrBk_Entry
*ae
)
3902 if(ae
->nickname
&& ae
->nickname
!= empty
)
3903 fs_give((void **) &ae
->nickname
);
3905 if(ae
->fullname
&& ae
->fullname
!= empty
)
3906 fs_give((void **) &ae
->fullname
);
3908 if(ae
->tag
== Single
){
3909 if(ae
->addr
.addr
&& ae
->addr
.addr
!= empty
)
3910 fs_give((void **) &ae
->addr
.addr
);
3912 else if(ae
->tag
== List
){
3914 for(p
= ae
->addr
.list
; *p
; p
++)
3916 fs_give((void **) p
);
3918 fs_give((void **) &ae
->addr
.list
);
3922 if(ae
->fcc
&& ae
->fcc
!= empty
)
3923 fs_give((void **) &ae
->fcc
);
3925 if(ae
->extra
&& ae
->extra
!= empty
)
3926 fs_give((void **) &ae
->extra
);
3933 * Inserts element new_ae before element put_it_before_this.
3936 insert_ab_entry(AdrBk
*ab
, a_c_arg_t put_it_before_this
, AdrBk_Entry
*new_ae
,
3937 int use_deleted_list
)
3939 adrbk_cntr_t before
, old_count
, new_count
;
3940 AdrBk_Entry
*ae_before
, *ae_before_next
;
3942 dprint((7, "- insert_ab_entry(before_this=%ld) -\n", (long) put_it_before_this
));
3944 before
= (adrbk_cntr_t
) put_it_before_this
;
3946 if(!ab
|| before
== NO_NEXT
|| (!use_deleted_list
&& before
> ab
->count
)
3947 || (use_deleted_list
&& before
> ab
->del_count
)){
3952 * add space for new entry to array
3953 * slide entries [before ... old_count-1] down one
3954 * put new_ae into opened up slot
3956 old_count
= use_deleted_list
? ab
->del_count
: ab
->count
;
3957 new_count
= old_count
+ 1;
3959 if(use_deleted_list
){
3960 if(ab
->del
) /* shouldn't happen */
3961 fs_give((void **) &ab
->del
);
3963 /* first entry in new array */
3964 ab
->del
= (AdrBk_Entry
*) fs_get(new_count
* sizeof(AdrBk_Entry
));
3965 defvalue_ae(ab
->del
);
3968 if(ab
->arr
) /* shouldn't happen */
3969 fs_give((void **) &ab
->arr
);
3971 /* first entry in new array */
3972 ab
->arr
= (AdrBk_Entry
*) fs_get(new_count
* sizeof(AdrBk_Entry
));
3973 defvalue_ae(ab
->arr
);
3977 if(use_deleted_list
){
3978 fs_resize((void **) &ab
->del
, new_count
* sizeof(AdrBk_Entry
));
3979 defvalue_ae(&ab
->del
[new_count
-1]);
3982 fs_resize((void **) &ab
->arr
, new_count
* sizeof(AdrBk_Entry
));
3983 defvalue_ae(&ab
->arr
[new_count
-1]);
3987 if(use_deleted_list
){
3988 ab
->del_count
= new_count
;
3990 ae_before
= adrbk_get_delae(ab
, (a_c_arg_t
) before
);
3992 if(before
< old_count
){
3993 ae_before_next
= adrbk_get_delae(ab
, (a_c_arg_t
) (before
+1));
3994 memmove(ae_before_next
, ae_before
,
3995 (old_count
-before
) * sizeof(AdrBk_Entry
));
3998 memcpy(ae_before
, new_ae
, sizeof(AdrBk_Entry
));
4002 ab
->count
= new_count
;
4004 ae_before
= adrbk_get_ae(ab
, (a_c_arg_t
) before
);
4006 if(before
< old_count
){
4007 ae_before_next
= adrbk_get_ae(ab
, (a_c_arg_t
) (before
+1));
4008 memmove(ae_before_next
, ae_before
,
4009 (old_count
-before
) * sizeof(AdrBk_Entry
));
4012 memcpy(ae_before
, new_ae
, sizeof(AdrBk_Entry
));
4018 if(!use_deleted_list
)
4019 repair_abook_tries(ab
);
4024 * Moves element move_this_one before element put_it_before_this.
4027 move_ab_entry(AdrBk
*ab
, a_c_arg_t move_this_one
, a_c_arg_t put_it_before_this
)
4029 adrbk_cntr_t m
, before
;
4031 AdrBk_Entry
*ae_m
, *ae_m_next
, *ae_before
, *ae_before_prev
, *ae_before_next
;
4033 dprint((7, "- move_ab_entry(move_this=%ld,before_this=%ld) -\n", (long) move_this_one
, (long) put_it_before_this
));
4035 m
= (adrbk_cntr_t
) move_this_one
;
4036 before
= (adrbk_cntr_t
) put_it_before_this
;
4038 if(!ab
|| m
== NO_NEXT
|| before
== NO_NEXT
4039 || m
== before
|| m
+1 == before
|| before
> ab
->count
){
4042 else if(m
+1 < before
){
4045 * slide entries [m+1 ... before-1] up one ("up" means smaller indices)
4046 * put m into opened up slot
4048 ae_m
= adrbk_get_ae(ab
, (a_c_arg_t
) m
);
4049 ae_m_next
= adrbk_get_ae(ab
, (a_c_arg_t
) (m
+1));
4050 ae_before_prev
= adrbk_get_ae(ab
, (a_c_arg_t
) (before
-1));
4051 if(ae_m
&& ae_m_next
&& ae_before_prev
){
4052 memcpy(&ae_tmp
, ae_m
, sizeof(ae_tmp
));
4053 memmove(ae_m
, ae_m_next
, (before
-m
-1) * sizeof(ae_tmp
));
4054 memcpy(ae_before_prev
, &ae_tmp
, sizeof(ae_tmp
));
4057 else if(m
> before
){
4060 * slide entries [before ... m-1] down one
4061 * put m into opened up slot
4063 ae_m
= adrbk_get_ae(ab
, (a_c_arg_t
) m
);
4064 ae_before
= adrbk_get_ae(ab
, (a_c_arg_t
) before
);
4065 ae_before_next
= adrbk_get_ae(ab
, (a_c_arg_t
) (before
+1));
4066 if(ae_m
&& ae_before
&& ae_before_next
){
4067 memcpy(&ae_tmp
, ae_m
, sizeof(ae_tmp
));
4068 memmove(ae_before_next
, ae_before
, (m
-before
) * sizeof(ae_tmp
));
4069 memcpy(ae_before
, &ae_tmp
, sizeof(ae_tmp
));
4073 dprint((9, "- move_ab_entry: done -\n"));
4075 repair_abook_tries(ab
);
4080 * Deletes element delete_this_one from in-core data structure.
4081 * If save_it is set the deleted entry is moved to the deleted_list.
4084 delete_ab_entry(AdrBk
*ab
, a_c_arg_t delete_this_one
, int save_it
)
4087 AdrBk_Entry
*ae_deleted
, *ae_deleted_next
;
4089 dprint((7, "- delete_ab_entry(delete_this=%ld,save_it=%d) -\n", (long) delete_this_one
, save_it
));
4091 d
= (adrbk_cntr_t
) delete_this_one
;
4093 if(!ab
|| d
== NO_NEXT
|| d
>= ab
->count
){
4098 * Move the entry to the deleted_list if asked to.
4100 ae_deleted
= adrbk_get_ae(ab
, (a_c_arg_t
) d
);
4103 char *oldnick
, *newnick
;
4109 * First prepend the prefix
4110 * #DELETED-YY/MM/DD#
4113 now
= time((time_t *) NULL
);
4114 tm_now
= localtime(&now
);
4116 oldnick
= ae_deleted
->nickname
;
4117 len
= strlen(oldnick
) + DELETED_LEN
+ strlen("YY/MM/DD#");
4119 newnick
= (char *) fs_get((len
+1) * sizeof(char));
4120 snprintf(newnick
, len
+1, "%s%02d/%02d/%02d#%s",
4121 DELETED
, (tm_now
->tm_year
)%100, tm_now
->tm_mon
+1,
4122 tm_now
->tm_mday
, oldnick
? oldnick
: "");
4123 newnick
[len
] = '\0';
4125 if(ae_deleted
->nickname
&& ae_deleted
->nickname
!= empty
)
4126 fs_give((void **) &ae_deleted
->nickname
);
4128 ae_deleted
->nickname
= newnick
;
4131 * Now insert this entry in the deleted_list.
4133 insert_ab_entry(ab
, (a_c_arg_t
) ab
->del_count
, ae_deleted
, 1);
4136 free_ae_parts(ae_deleted
);
4140 * slide entries [deleted+1 ... count-1] up one
4142 if(d
+1 < ab
->count
){
4143 ae_deleted
= adrbk_get_ae(ab
, (a_c_arg_t
) d
);
4144 ae_deleted_next
= adrbk_get_ae(ab
, (a_c_arg_t
) (d
+1));
4145 if(ae_deleted
&& ae_deleted_next
)
4146 memmove(ae_deleted
, ae_deleted_next
, (ab
->count
-d
-1) * sizeof(AdrBk_Entry
));
4152 fs_resize((void **) &ab
->arr
, ab
->count
* sizeof(AdrBk_Entry
));
4154 fs_give((void **) &ab
->arr
);
4157 repair_abook_tries(ab
);
4162 * We may want to be smarter about this and repair instead of
4166 repair_abook_tries(AdrBk
*ab
)
4169 AdrBk_Trie
*save_nick_trie
, *save_addr_trie
,
4170 *save_full_trie
, *save_revfull_trie
;
4172 save_nick_trie
= ab
->nick_trie
;
4173 ab
->nick_trie
= NULL
;
4174 save_addr_trie
= ab
->addr_trie
;
4175 ab
->addr_trie
= NULL
;
4176 save_full_trie
= ab
->full_trie
;
4177 ab
->full_trie
= NULL
;
4178 save_revfull_trie
= ab
->revfull_trie
;
4179 ab
->revfull_trie
= NULL
;
4180 if(build_abook_tries(ab
, NULL
)){
4181 dprint((2, "trouble rebuilding tries, restoring\n"));
4183 free_abook_trie(&ab
->nick_trie
);
4185 /* better than nothing */
4186 ab
->nick_trie
= save_nick_trie
;
4189 free_abook_trie(&ab
->addr_trie
);
4191 ab
->addr_trie
= save_addr_trie
;
4194 free_abook_trie(&ab
->full_trie
);
4196 ab
->full_trie
= save_full_trie
;
4198 if(ab
->revfull_trie
)
4199 free_abook_trie(&ab
->revfull_trie
);
4201 ab
->revfull_trie
= save_revfull_trie
;
4205 free_abook_trie(&save_nick_trie
);
4208 free_abook_trie(&save_addr_trie
);
4211 free_abook_trie(&save_full_trie
);
4213 if(save_revfull_trie
)
4214 free_abook_trie(&save_revfull_trie
);
4221 * Free the list of distribution lists which have been expanded.
4222 * Leaves the head of the list alone.
4224 * Args: exp_head -- Head of the expanded list.
4227 exp_free(EXPANDED_S
*exp_head
)
4229 EXPANDED_S
*e
, *the_next_one
;
4231 e
= exp_head
? exp_head
->next
: NULL
;
4237 the_next_one
= e
->next
;
4238 fs_give((void **)&e
);
4242 exp_head
->next
= (EXPANDED_S
*)NULL
;
4247 * Is entry n expanded?
4249 * Args: exp_head -- Head of the expanded list.
4250 * n -- The entry num to check
4253 exp_is_expanded(EXPANDED_S
*exp_head
, a_c_arg_t n
)
4255 register EXPANDED_S
*e
;
4258 nn
= (adrbk_cntr_t
)n
;
4260 e
= exp_head
? exp_head
->next
: NULL
;
4263 * The list is kept ordered, so we search until we find it or are
4273 return(e
&& e
->ent
== nn
);
4278 * How many entries expanded in this addrbook.
4280 * Args: exp_head -- Head of the expanded list.
4283 exp_howmany_expanded(EXPANDED_S
*exp_head
)
4285 register EXPANDED_S
*e
;
4288 e
= exp_head
? exp_head
->next
: NULL
;
4300 * Are any entries expanded?
4302 * Args: exp_head -- Head of the expanded list.
4305 exp_any_expanded(EXPANDED_S
*exp_head
)
4307 return(exp_head
&& exp_head
->next
!= NULL
);
4312 * Return next entry num in list.
4314 * Args: cur -- Current position in the list.
4316 * Result: Returns the number of the next entry, or NO_NEXT if there is
4317 * no next entry. As a side effect, the cur pointer is incremented.
4320 exp_get_next(EXPANDED_S
**cur
)
4322 adrbk_cntr_t ret
= NO_NEXT
;
4324 if(cur
&& *cur
&& (*cur
)->next
){
4325 ret
= (*cur
)->next
->ent
;
4326 *cur
= (*cur
)->next
;
4334 * Mark entry n as being expanded.
4336 * Args: exp_head -- Head of the expanded list.
4337 * n -- The entry num to mark
4340 exp_set_expanded(EXPANDED_S
*exp_head
, a_c_arg_t n
)
4342 register EXPANDED_S
*e
;
4346 nn
= (adrbk_cntr_t
)n
;
4348 alpine_panic("exp_head not set in exp_set_expanded");
4350 for(e
= exp_head
; e
->next
; e
= e
->next
)
4351 if(e
->next
->ent
>= nn
)
4354 if(e
->next
&& e
->next
->ent
== nn
) /* already there */
4357 /* add new after e */
4358 new = (EXPANDED_S
*)fs_get(sizeof(EXPANDED_S
));
4360 new->next
= e
->next
;
4366 * Mark entry n as being *not* expanded.
4368 * Args: exp_head -- Head of the expanded list.
4369 * n -- The entry num to mark
4372 exp_unset_expanded(EXPANDED_S
*exp_head
, a_c_arg_t n
)
4374 register EXPANDED_S
*e
;
4375 EXPANDED_S
*delete_this_one
= NULL
;
4378 nn
= (adrbk_cntr_t
)n
;
4380 alpine_panic("exp_head not set in exp_unset_expanded");
4382 for(e
= exp_head
; e
->next
; e
= e
->next
)
4383 if(e
->next
->ent
>= nn
)
4386 if(e
->next
&& e
->next
->ent
== nn
){
4387 delete_this_one
= e
->next
;
4388 e
->next
= e
->next
->next
;
4392 fs_give((void **)&delete_this_one
);
4397 * Adjust the "expanded" list to correspond to addrbook entry n being
4400 * Args: exp_head -- Head of the expanded list.
4401 * n -- The entry num being deleted
4404 exp_del_nth(EXPANDED_S
*exp_head
, a_c_arg_t n
)
4406 register EXPANDED_S
*e
;
4407 int delete_when_done
= 0;
4410 nn
= (adrbk_cntr_t
)n
;
4412 alpine_panic("exp_head not set in exp_del_nth");
4415 while(e
&& e
->ent
< nn
)
4425 e
->ent
--; /* adjust entry nums */
4429 if(delete_when_done
)
4430 exp_unset_expanded(exp_head
, n
);
4436 * Adjust the "expanded" list to correspond to a new addrbook entry being
4437 * added between current entries n-1 and n.
4439 * Args: exp_head -- Head of the expanded list.
4440 * n -- The entry num being added
4442 * The new entry is not marked expanded.
4445 exp_add_nth(EXPANDED_S
*exp_head
, a_c_arg_t n
)
4447 register EXPANDED_S
*e
;
4450 nn
= (adrbk_cntr_t
)n
;
4452 alpine_panic("exp_head not set in exp_add_nth");
4455 while(e
&& e
->ent
< nn
)
4459 e
->ent
++; /* adjust entry nums */
4465 static AdrBk
*ab_for_sort
;
4468 * Compare two address book entries. Args are AdrBk_Entry **'s.
4469 * Sorts lists after simple addresses and then sorts on Fullname field.
4472 cmp_ae_by_full_lists_last(const qsort_t
*a
, const qsort_t
*b
)
4474 AdrBk_Entry
**x
= (AdrBk_Entry
**)a
,
4475 **y
= (AdrBk_Entry
**)b
;
4478 if((*x
)->tag
== List
&& (*y
)->tag
== Single
)
4480 else if((*x
)->tag
== Single
&& (*y
)->tag
== List
)
4483 register char *p
, *q
, *r
, *s
;
4486 if(*p
== '"' && *(p
+1))
4490 if(*q
== '"' && *(q
+1))
4493 r
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4496 s
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
+10000,
4497 SIZEOF_20KBUF
-10000, q
);
4499 result
= (*pcollator
)(r
, s
);
4501 result
= (*pcollator
)((*x
)->nickname
, (*y
)->nickname
);
4509 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4510 * Sorts lists after simple addresses and then sorts on Fullname field.
4513 cmp_cntr_by_full_lists_last(const qsort_t
*a
, const qsort_t
*b
)
4515 adrbk_cntr_t
*x
= (adrbk_cntr_t
*)a
, /* *x is an element_number */
4516 *y
= (adrbk_cntr_t
*)b
;
4520 if(ps_global
->intr_pending
)
4521 longjmp(jump_over_qsort
, 1);
4525 x_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*x
));
4526 y_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*y
));
4528 return(cmp_ae_by_full_lists_last((const qsort_t
*) &x_ae
,
4529 (const qsort_t
*) &y_ae
));
4534 * Compare two address book entries. Args are AdrBk_Entry **'s.
4535 * Sorts on Fullname field.
4538 cmp_ae_by_full(const qsort_t
*a
, const qsort_t
*b
)
4540 AdrBk_Entry
**x
= (AdrBk_Entry
**)a
,
4541 **y
= (AdrBk_Entry
**)b
;
4543 register char *p
, *q
, *r
, *s
;
4546 if(*p
== '"' && *(p
+1))
4550 if(*q
== '"' && *(q
+1))
4553 r
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4555 s
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
+10000,
4556 SIZEOF_20KBUF
-10000, q
);
4557 result
= (*pcollator
)(r
, s
);
4559 result
= (*pcollator
)((*x
)->nickname
, (*y
)->nickname
);
4566 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4567 * Sorts on Fullname field.
4570 cmp_cntr_by_full(const qsort_t
*a
, const qsort_t
*b
)
4572 adrbk_cntr_t
*x
= (adrbk_cntr_t
*)a
, /* *x is an element_number */
4573 *y
= (adrbk_cntr_t
*)b
;
4577 if(ps_global
->intr_pending
)
4578 longjmp(jump_over_qsort
, 1);
4582 x_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*x
));
4583 y_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*y
));
4585 return(cmp_ae_by_full((const qsort_t
*) &x_ae
, (const qsort_t
*) &y_ae
));
4590 * Compare two address book entries. Args are AdrBk_Entry **'s.
4591 * Sorts lists after simple addresses and then sorts on Nickname field.
4594 cmp_ae_by_nick_lists_last(const qsort_t
*a
, const qsort_t
*b
)
4596 AdrBk_Entry
**x
= (AdrBk_Entry
**)a
,
4597 **y
= (AdrBk_Entry
**)b
;
4600 if((*x
)->tag
== List
&& (*y
)->tag
== Single
)
4602 else if((*x
)->tag
== Single
&& (*y
)->tag
== List
)
4605 result
= (*pcollator
)((*x
)->nickname
, (*y
)->nickname
);
4612 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4613 * Sorts lists after simple addresses and then sorts on Nickname field.
4616 cmp_cntr_by_nick_lists_last(const qsort_t
*a
, const qsort_t
*b
)
4618 adrbk_cntr_t
*x
= (adrbk_cntr_t
*)a
, /* *x is an element_number */
4619 *y
= (adrbk_cntr_t
*)b
;
4623 if(ps_global
->intr_pending
)
4624 longjmp(jump_over_qsort
, 1);
4628 x_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*x
));
4629 y_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*y
));
4631 return(cmp_ae_by_nick_lists_last((const qsort_t
*) &x_ae
,
4632 (const qsort_t
*) &y_ae
));
4637 * Compare two address book entries. Args are AdrBk_Entry **'s.
4638 * Sorts on Nickname field.
4641 cmp_ae_by_nick(const qsort_t
*a
, const qsort_t
*b
)
4643 AdrBk_Entry
**x
= (AdrBk_Entry
**)a
,
4644 **y
= (AdrBk_Entry
**)b
;
4646 return((*pcollator
)((*x
)->nickname
, (*y
)->nickname
));
4651 * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
4652 * Sorts on Nickname field.
4655 cmp_cntr_by_nick(const qsort_t
*a
, const qsort_t
*b
)
4657 adrbk_cntr_t
*x
= (adrbk_cntr_t
*)a
, /* *x is an element_number */
4658 *y
= (adrbk_cntr_t
*)b
;
4662 if(ps_global
->intr_pending
)
4663 longjmp(jump_over_qsort
, 1);
4667 x_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*x
));
4668 y_ae
= adrbk_get_ae(ab_for_sort
, (a_c_arg_t
)(*y
));
4670 return(cmp_ae_by_nick((const qsort_t
*) &x_ae
, (const qsort_t
*) &y_ae
));
4675 * For sorting a simple list of pointers to addresses (skip initial quotes)
4678 cmp_addr(const qsort_t
*a1
, const qsort_t
*a2
)
4680 char *x
= *(char **)a1
, *y
= *(char **)a2
;
4689 r
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4691 s
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
+10000,
4692 SIZEOF_20KBUF
-10000, y
);
4693 return((*pcollator
)(r
, s
));
4698 * Sort an array of strings, except skip initial quotes.
4701 sort_addr_list(char **list
)
4705 /* find size of list */
4706 for(p
= list
; *p
!= NULL
; p
++)
4709 qsort((qsort_t
*)list
, (size_t)(p
- list
), sizeof(char *), cmp_addr
);
4714 * Sort this address book.
4716 * Args: ab -- address book to sort
4717 * current_entry_num -- see next description
4718 * new_entry_num -- return new entry_num of current_entry_num here
4720 * Result: return code: 0 all went well
4721 * -2 error writing address book, check errno
4723 * The sorting strategy is to allocate an array of length ab->count which
4724 * contains the element numbers 0, 1, ..., ab->count - 1, representing the
4725 * entries in the addrbook, of course. Sort the array, then use that
4726 * result to swap ae's in the in-core addrbook array before writing it out.
4729 adrbk_sort(AdrBk
*ab
, a_c_arg_t current_entry_num
, adrbk_cntr_t
*new_entry_num
, int be_quiet
)
4731 adrbk_cntr_t
*sort_array
, *inv
, tmp
;
4732 long i
, j
, hi
, count
;
4733 int skip_the_sort
= 0, we_cancel
= 0, we_turned_on
= 0;
4734 AdrBk_Entry ae_tmp
, *ae_i
, *ae_hi
;
4735 EXPANDED_S
*e
, *e2
, *smallest
;
4737 dprint((5, "- adrbk_sort -\n"));
4739 count
= (long) (ab
->count
);
4744 if(ab
->sort_rule
== AB_SORT_RULE_NONE
)
4750 sort_array
= (adrbk_cntr_t
*) fs_get(count
* sizeof(adrbk_cntr_t
));
4751 inv
= (adrbk_cntr_t
*) fs_get(count
* sizeof(adrbk_cntr_t
));
4753 for(i
= 0L; i
< count
; i
++)
4754 sort_array
[i
] = (adrbk_cntr_t
) i
;
4758 if(setjmp(jump_over_qsort
))
4762 we_turned_on
= intr_handling_on();
4764 we_cancel
= busy_cue(_("Sorting address book"), NULL
, 0);
4766 qsort((qsort_t
*)sort_array
,
4768 sizeof(adrbk_cntr_t
),
4769 (ab
->sort_rule
== AB_SORT_RULE_FULL_LISTS
) ?
4770 cmp_cntr_by_full_lists_last
:
4771 (ab
->sort_rule
== AB_SORT_RULE_FULL
) ?
4773 (ab
->sort_rule
== AB_SORT_RULE_NICK_LISTS
) ?
4774 cmp_cntr_by_nick_lists_last
:
4775 /* (ab->sort_rule == AB_SORT_RULE_NICK) */
4779 dprint((9, "- adrbk_sort: done with first sort -\n"));
4782 intr_handling_off();
4785 q_status_message(SM_ORDER
, 3, 3,
4786 _("Address book sort cancelled, using old order for now"));
4787 goto skip_the_write_too
;
4790 dprint((5, "- adrbk_sort (%s)\n",
4791 ab
->sort_rule
==AB_SORT_RULE_FULL_LISTS
? "FullListsLast" :
4792 ab
->sort_rule
==AB_SORT_RULE_FULL
? "Fullname" :
4793 ab
->sort_rule
==AB_SORT_RULE_NICK_LISTS
? "NickListLast" :
4794 ab
->sort_rule
==AB_SORT_RULE_NICK
? "Nickname" : "unknown"));
4797 * Rearrange the in-core array of ae's to be in the new order.
4798 * We can do that by sorting the inverse into the correct order in parallel.
4800 for(i
= 0L; i
< count
; i
++)
4801 inv
[sort_array
[i
]] = i
;
4803 if(new_entry_num
&& (adrbk_cntr_t
) current_entry_num
>= 0
4804 && (adrbk_cntr_t
) current_entry_num
< count
){
4805 *new_entry_num
= inv
[(adrbk_cntr_t
) current_entry_num
];
4809 * The expanded and selected lists will be wrong now. Correct them.
4810 * First the expanded list.
4812 e
= ab
->exp
? ab
->exp
->next
: NULL
;
4814 if(e
->ent
>= 0 && e
->ent
< count
)
4815 e
->ent
= inv
[e
->ent
];
4821 * And sort into ascending order as expected by the exp_ routines.
4823 e
= ab
->exp
? ab
->exp
->next
: NULL
;
4825 /* move smallest to e */
4829 if(e2
->ent
!= NO_NEXT
&& e2
->ent
>= 0 && e2
->ent
< count
&& e2
->ent
< smallest
->ent
)
4835 /* swap values in e and smallest */
4838 e
->ent
= smallest
->ent
;
4839 smallest
->ent
= tmp
;
4846 * Same thing for the selected list.
4848 e
= ab
->selects
? ab
->selects
->next
: NULL
;
4850 if(e
->ent
>= 0 && e
->ent
< count
)
4851 e
->ent
= inv
[e
->ent
];
4856 e
= ab
->selects
? ab
->selects
->next
: NULL
;
4858 /* move smallest to e */
4862 if(e2
->ent
!= NO_NEXT
&& e2
->ent
>= 0 && e2
->ent
< count
&& e2
->ent
< smallest
->ent
)
4868 /* swap values in e and smallest */
4871 e
->ent
= smallest
->ent
;
4872 smallest
->ent
= tmp
;
4878 for(i
= 0L; i
< count
; i
++){
4880 /* find inv[j] which = i */
4881 for(j
= i
+1; j
< count
; j
++){
4889 ae_i
= adrbk_get_ae(ab
, (a_c_arg_t
) i
);
4890 ae_hi
= adrbk_get_ae(ab
, (a_c_arg_t
) hi
);
4892 memcpy(&ae_tmp
, ae_i
, sizeof(ae_tmp
));
4893 memcpy(ae_i
, ae_hi
, sizeof(ae_tmp
));
4894 memcpy(ae_hi
, &ae_tmp
, sizeof(ae_tmp
));
4896 /* else can't happen */
4905 repair_abook_tries(ab
);
4907 dprint((9, "- adrbk_sort: done with rearranging -\n"));
4914 fs_give((void **) &sort_array
);
4917 fs_give((void **) &inv
);
4924 * Returns 1 if any addrbooks are in Open state, 0 otherwise.
4926 * This is a test for ostatus == Open, not for whether or not the address book
4935 for(i
= 0; ret
== 0 && i
< as
.n_addrbk
; i
++)
4936 if(as
.adrbks
[i
].ostatus
== Open
)
4944 * Make sure addrbooks are minimally initialized.
4947 init_ab_if_needed(void)
4949 dprint((9, "- init_ab_if_needed -\n"));
4952 (void)init_addrbooks(Closed
, 0, 0, 1);
4957 * Sets everything up to get started.
4959 * Args: want_status -- The desired OpenStatus for all addrbooks.
4960 * reset_to_top -- Forget about the old location and put cursor
4962 * open_if_only_one -- If want_status is HalfOpen and there is only
4963 * section to look at, then promote want_status
4965 * ro_warning -- Set ReadOnly warning global
4967 * Return: 1 if ok, 0 if problem
4970 init_addrbooks(OpenStatus want_status
, int reset_to_top
, int open_if_only_one
, int ro_warning
)
4972 register PerAddrBook
*pab
;
4976 dprint((4, "-- init_addrbooks(%s, %d, %d, %d) --\n",
4979 want_status
==HalfOpen
?
4981 want_status
==ThreeQuartOpen
?
4983 want_status
==NoDisplay
?
4986 reset_to_top
, open_if_only_one
, ro_warning
));
4988 as
.l_p_page
= ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)
4989 - HEADER_ROWS(ps_global
);
4990 if(as
.l_p_page
<= 0)
4993 as
.no_op_possbl
= 0;
4995 as
.ro_warning
= ro_warning
;
4997 /* already been initialized */
5002 * Special case. If there is only one addressbook we start the
5003 * user out with that open.
5005 if(want_status
== HalfOpen
&&
5006 ((open_if_only_one
&& as
.n_addrbk
== 1 && as
.n_serv
== 0) ||
5007 (F_ON(F_EXPANDED_ADDRBOOKS
, ps_global
) &&
5008 F_ON(F_CMBND_ABOOK_DISP
, ps_global
))))
5011 /* open to correct state */
5012 for(i
= 0; i
< as
.n_addrbk
; i
++)
5013 init_abook(&as
.adrbks
[i
], want_status
);
5016 warp_to_beginning();
5018 line
= first_selectable_line(0L);
5024 if(as
.cur_row
>= as
.l_p_page
)
5025 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
5027 as
.old_cur_row
= as
.cur_row
;
5030 dprint((4, "init_addrbooks: already initialized: %d books\n",
5032 return((as
.n_addrbk
+ as
.n_serv
) ? 1 : 0);
5037 /* count directory servers */
5041 if(ps_global
->VAR_LDAP_SERVERS
&&
5042 ps_global
->VAR_LDAP_SERVERS
[0] &&
5043 ps_global
->VAR_LDAP_SERVERS
[0][0])
5044 for(t
= ps_global
->VAR_LDAP_SERVERS
; t
[0] && t
[0][0]; t
++){
5048 info
= break_up_ldap_server(*t
);
5049 as
.n_impl
+= (info
&& info
->impl
) ? 1 : 0;
5051 free_ldap_server_info(&info
);
5053 #endif /* ENABLE_LDAP */
5055 /* count addressbooks */
5056 as
.how_many_personals
= 0;
5057 if(ps_global
->VAR_ADDRESSBOOK
&&
5058 ps_global
->VAR_ADDRESSBOOK
[0] &&
5059 ps_global
->VAR_ADDRESSBOOK
[0][0])
5060 for(t
= ps_global
->VAR_ADDRESSBOOK
; t
[0] && t
[0][0]; t
++)
5061 as
.how_many_personals
++;
5063 as
.n_addrbk
= as
.how_many_personals
;
5064 if(ps_global
->VAR_GLOB_ADDRBOOK
&&
5065 ps_global
->VAR_GLOB_ADDRBOOK
[0] &&
5066 ps_global
->VAR_GLOB_ADDRBOOK
[0][0])
5067 for(t
= ps_global
->VAR_GLOB_ADDRBOOK
; t
[0] && t
[0][0]; t
++)
5070 if(want_status
== HalfOpen
&&
5071 ((open_if_only_one
&& as
.n_addrbk
== 1 && as
.n_serv
== 0) ||
5072 (F_ON(F_EXPANDED_ADDRBOOKS
, ps_global
) &&
5073 F_ON(F_CMBND_ABOOK_DISP
, ps_global
))))
5078 * allocate array of PerAddrBooks
5079 * (we don't give this up until we exit Pine, but it's small)
5082 as
.adrbks
= (PerAddrBook
*)fs_get(as
.n_addrbk
* sizeof(PerAddrBook
));
5083 memset((void *)as
.adrbks
, 0, as
.n_addrbk
* sizeof(PerAddrBook
));
5085 /* init PerAddrBook data */
5086 for(as
.cur
= 0; as
.cur
< as
.n_addrbk
; as
.cur
++){
5087 char *nickname
= NULL
,
5090 if(as
.cur
< as
.how_many_personals
)
5091 q
= ps_global
->VAR_ADDRESSBOOK
[as
.cur
];
5093 q
= ps_global
->VAR_GLOB_ADDRBOOK
[as
.cur
- as
.how_many_personals
];
5095 pab
= &as
.adrbks
[as
.cur
];
5097 /* Parse entry for optional nickname and filename */
5098 get_pair(q
, &nickname
, &filename
, 0, 0);
5100 if(nickname
&& !*nickname
)
5101 fs_give((void **)&nickname
);
5103 strncpy(tmp_20k_buf
, filename
, SIZEOF_20KBUF
);
5104 fs_give((void **)&filename
);
5106 filename
= tmp_20k_buf
;
5107 if(nickname
== NULL
)
5108 pab
->abnick
= cpystr(filename
);
5110 pab
->abnick
= nickname
;
5112 if(*filename
== '~')
5113 fnexpand(filename
, SIZEOF_20KBUF
);
5115 if(*filename
== '{' || is_absolute_path(filename
)){
5116 pab
->filename
= cpystr(filename
); /* fully qualified */
5119 char book_path
[MAXPATH
+1];
5120 char *lc
= last_cmpnt(ps_global
->pinerc
);
5122 book_path
[0] = '\0';
5124 strncpy(book_path
, ps_global
->pinerc
,
5125 MIN(lc
- ps_global
->pinerc
, sizeof(book_path
)-1));
5126 book_path
[MIN(lc
- ps_global
->pinerc
,
5127 sizeof(book_path
)-1)] = '\0';
5130 strncat(book_path
, filename
,
5131 sizeof(book_path
)-1-strlen(book_path
));
5132 pab
->filename
= cpystr(book_path
);
5135 if(*pab
->filename
== '{')
5136 pab
->type
|= REMOTE_VIA_IMAP
;
5138 if(as
.cur
>= as
.how_many_personals
)
5139 pab
->type
|= GLOBAL
;
5141 pab
->access
= adrbk_access(pab
);
5143 /* global address books are forced readonly */
5144 if(pab
->type
& GLOBAL
&& pab
->access
!= NoAccess
)
5145 pab
->access
= ReadOnly
;
5147 pab
->ostatus
= TotallyClosed
;
5150 * and remember that the memset above initializes everything
5154 init_abook(pab
, want_status
);
5159 * Have to reset_to_top in this case since this is the first open,
5160 * regardless of the value of the argument, since these values haven't been
5165 warp_to_beginning();
5166 line
= first_selectable_line(0L);
5173 if(as
.cur_row
>= as
.l_p_page
){
5174 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
5175 as
.cur_row
= as
.l_p_page
- 1;
5178 as
.old_cur_row
= as
.cur_row
;
5180 return((as
.n_addrbk
+ as
.n_serv
) ? 1 : 0);
5185 * Something was changed in options screen, so need to start over.
5188 addrbook_reset(void)
5190 dprint((4, "- addrbook_reset -\n"));
5191 completely_done_with_adrbks();
5196 * Sort was changed in options screen. Since we only sort normally
5197 * when we actually make a change to the address book, we need to
5198 * go out of our way to sort here.
5201 addrbook_redo_sorts(void)
5207 dprint((4, "- addrbook_redo_sorts -\n"));
5210 init_ab_if_needed();
5212 for(i
= 0; i
< as
.n_addrbk
; i
++){
5213 pab
= &as
.adrbks
[i
];
5214 init_abook(pab
, NoDisplay
);
5215 ab
= pab
->address_book
;
5217 if(!adrbk_is_in_sort_order(ab
, 0))
5218 adrbk_write(ab
, 0, NULL
, NULL
, 1, 0);
5226 * Returns type of access allowed on this addrbook.
5229 adrbk_access(PerAddrBook
*pab
)
5231 char fbuf
[MAXPATH
+1];
5232 AccessType access
= NoExists
;
5233 CONTEXT_S
*dummy_cntxt
= NULL
;
5235 dprint((9, "- addrbook_access -\n"));
5237 if(pab
&& pab
->type
& REMOTE_VIA_IMAP
){
5239 * Open_fcc creates the folder if it didn't already exist.
5241 if((pab
->so
= open_fcc(pab
->filename
, &dummy_cntxt
, 1,
5243 " Can't fetch remote addrbook.")) != NULL
){
5245 * We know the folder is there but don't know what access
5246 * rights we have until we try to select it, which we don't
5247 * want to do unless we have to. So delay evaluating.
5252 else if(pab
){ /* local file */
5253 #if defined(NO_LOCAL_ADDRBOOKS)
5254 /* don't allow any access to local addrbooks */
5256 #else /* !NO_LOCAL_ADDRBOOKS) */
5257 build_path(fbuf
, is_absolute_path(pab
->filename
) ? NULL
5258 : ps_global
->home_dir
,
5259 pab
->filename
, sizeof(fbuf
));
5261 #if defined(DOS) || defined(OS2)
5263 * Microsoft networking causes some access calls to do a DNS query (!!)
5264 * when it is turned on. In particular, if there is a / in the filename
5265 * this seems to happen. So, just don't allow it.
5267 if(strindex(fbuf
, '/') != NULL
){
5268 dprint((2, "\"/\" not allowed in addrbook name\n"));
5272 /* also prevent backslash in non-DOS addrbook names */
5273 if(strindex(fbuf
, '\\') != NULL
){
5274 dprint((2, "\"\\\" not allowed in addrbook name\n"));
5279 if(can_access(fbuf
, ACCESS_EXISTS
) == 0){
5280 if(can_access(fbuf
, EDIT_ACCESS
) == 0){
5284 if((p
= last_cmpnt(fbuf
)) != NULL
){
5286 dir
= *fbuf
? fbuf
: "/";
5289 #if defined(DOS) || defined(OS2)
5291 * If the dir has become a drive letter and : (e.g. "c:")
5292 * then append a "\". The library function access() in the
5293 * win 16 version of MSC seems to require this.
5295 if(isalpha((unsigned char) *dir
)
5296 && *(dir
+1) == ':' && *(dir
+2) == '\0'){
5300 #endif /* DOS || OS2 */
5303 * Even if we can edit the address book file itself, we aren't
5304 * going to be able to change it unless we can also write in
5305 * the directory that contains it (because we write into a
5306 * temp file and then rename).
5308 if(can_access(dir
, EDIT_ACCESS
) == 0)
5312 q_status_message1(SM_ORDER
, 2, 2,
5313 "Address book directory (%.200s) is ReadOnly",
5317 else if(can_access(fbuf
, READ_ACCESS
) == 0)
5322 #endif /* !NO_LOCAL_ADDRBOOKS) */
5330 * Trim back remote address books if necessary.
5333 trim_remote_adrbks(void)
5335 register PerAddrBook
*pab
;
5338 dprint((2, "- trim_remote_adrbks -\n"));
5343 for(i
= 0; i
< as
.n_addrbk
; i
++){
5344 pab
= &as
.adrbks
[i
];
5345 if(pab
->ostatus
!= TotallyClosed
&& pab
->address_book
5346 && pab
->address_book
->rd
)
5347 rd_trim_remdata(&pab
->address_book
->rd
);
5353 * Free and close everything.
5356 completely_done_with_adrbks(void)
5358 register PerAddrBook
*pab
;
5361 dprint((2, "- completely_done_with_adrbks -\n"));
5363 ab_nesting_level
= 0;
5368 for(i
= 0; i
< as
.n_addrbk
; i
++)
5369 init_abook(&as
.adrbks
[i
], TotallyClosed
);
5371 for(i
= 0; i
< as
.n_addrbk
; i
++){
5372 pab
= &as
.adrbks
[i
];
5375 fs_give((void **)&pab
->filename
);
5378 fs_give((void **)&pab
->abnick
);
5381 done_with_dlc_cache();
5384 fs_give((void **)&as
.adrbks
);
5392 * Initialize or re-initialize this address book.
5394 * Args: pab -- the PerAddrBook ptr
5395 * want_status -- desired OpenStatus for this address book
5398 init_abook(PerAddrBook
*pab
, OpenStatus want_status
)
5400 register OpenStatus new_status
;
5402 dprint((4, "- init_abook -\n"));
5403 dprint((7, " addrbook nickname = %s filename = %s",
5404 pab
->abnick
? pab
->abnick
: "<null>",
5405 pab
->filename
? pab
->filename
: "<null>"));
5406 dprint((7, " ostatus was %s, want %s\n",
5407 pab
->ostatus
==Open
? "Open" :
5408 pab
->ostatus
==HalfOpen
? "HalfOpen" :
5409 pab
->ostatus
==ThreeQuartOpen
? "ThreeQuartOpen" :
5410 pab
->ostatus
==NoDisplay
? "NoDisplay" :
5411 pab
->ostatus
==Closed
? "Closed" : "TotallyClosed",
5412 want_status
==Open
? "Open" :
5413 want_status
==HalfOpen
? "HalfOpen" :
5414 want_status
==ThreeQuartOpen
? "ThreeQuartOpen" :
5415 want_status
==NoDisplay
? "NoDisplay" :
5416 want_status
==Closed
? "Closed" : "TotallyClosed"));
5418 new_status
= want_status
; /* optimistic default */
5420 if(want_status
== TotallyClosed
){
5421 if(pab
->address_book
!= NULL
){
5422 adrbk_close(pab
->address_book
);
5423 pab
->address_book
= NULL
;
5426 if(pab
->so
!= NULL
){
5432 * If we don't need it, release some addrbook memory by calling
5433 * adrbk_partial_close().
5435 else if((want_status
== Closed
|| want_status
== HalfOpen
) &&
5436 pab
->address_book
!= NULL
){
5437 adrbk_partial_close(pab
->address_book
);
5439 /* If we want the addrbook read in and it hasn't been, do so */
5440 else if(want_status
== Open
|| want_status
== NoDisplay
){
5441 if(pab
->address_book
== NULL
){ /* abook handle is not currently active */
5442 if(pab
->access
!= NoAccess
){
5443 char warning
[800]; /* place to put a warning */
5447 if(pab
->access
== ReadOnly
)
5448 sort_rule
= AB_SORT_RULE_NONE
;
5450 sort_rule
= ps_global
->ab_sort_rule
;
5452 pab
->address_book
= adrbk_open(pab
,
5453 ps_global
->home_dir
, warning
, sizeof(warning
), sort_rule
);
5455 if(pab
->address_book
== NULL
){
5456 pab
->access
= NoAccess
;
5457 if(want_status
== Open
){
5458 new_status
= HalfOpen
; /* best we can do */
5459 q_status_message1(SM_ORDER
| SM_DING
, *warning
?1:3, 4,
5460 _("Error opening/creating address book %.200s"),
5463 q_status_message2(SM_ORDER
, 3, 4, "%.200s: %.200s",
5464 as
.n_addrbk
> 1 ? pab
->abnick
: "addressbook",
5468 new_status
= Closed
;
5470 dprint((1, "Error opening address book %s: %s\n",
5471 pab
->abnick
? pab
->abnick
: "?",
5472 error_description(errno
)));
5475 if(pab
->access
== NoExists
)
5476 pab
->access
= ReadWrite
;
5478 if(pab
->access
== ReadWrite
){
5480 * Add forced entries if there are any. These are
5481 * entries that are always supposed to show up in
5482 * personal address books. They're specified in the
5483 * global config file.
5485 add_forced_entries(pab
->address_book
);
5488 new_status
= want_status
;
5489 dprint((2, "Address book %s (%s) opened with %ld items\n",
5490 pab
->abnick
? pab
->abnick
: "?",
5491 pab
->filename
? pab
->filename
: "?",
5492 (long)adrbk_count(pab
->address_book
)));
5495 "Addressbook parse error in %s (%s): %s\n",
5496 pab
->abnick
? pab
->abnick
: "?",
5497 pab
->filename
? pab
->filename
: "?",
5499 if(!pab
->gave_parse_warnings
&& want_status
== Open
){
5500 pab
->gave_parse_warnings
++;
5501 q_status_message2(SM_ORDER
, 3, 4, "%.200s: %.200s",
5502 as
.n_addrbk
> 1 ? pab
->abnick
: "addressbook",
5509 if(want_status
== Open
){
5510 new_status
= HalfOpen
; /* best we can do */
5511 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
5512 "Insufficient permissions for opening address book %.200s",
5516 new_status
= Closed
;
5520 * File handle was already open but we were in Closed or HalfOpen
5521 * state. Check to see if someone else has changed the addrbook
5522 * since we first opened it.
5524 else if((pab
->ostatus
== Closed
|| pab
->ostatus
== HalfOpen
) &&
5525 ps_global
->remote_abook_validity
> 0)
5526 (void)adrbk_check_and_fix(pab
, 1, 0, 0);
5529 pab
->ostatus
= new_status
;
5534 * Does a validity check on all the open address books.
5536 * Return -- number of address books with invalid data
5539 adrbk_check_all_validity_now(void)
5542 int something_out_of_date
= 0;
5545 dprint((7, "- adrbk_check_all_validity_now -\n"));
5548 for(i
= 0; i
< as
.n_addrbk
; i
++){
5549 pab
= &as
.adrbks
[i
];
5550 if(pab
->address_book
){
5551 adrbk_check_validity(pab
->address_book
, 1L);
5552 if(pab
->address_book
->flags
& FILE_OUTOFDATE
||
5553 (pab
->address_book
->rd
&&
5554 pab
->address_book
->rd
->flags
& REM_OUTOFDATE
))
5555 something_out_of_date
++;
5560 return(something_out_of_date
);
5565 * Fix out-of-date address book. This only fixes the AdrBk part of the
5566 * problem, not the pab and display. It closes and reopens the address_book
5567 * file, clears the cache, reads new data.
5569 * Arg pab -- Pointer to the PerAddrBook data for this addrbook
5570 * safe -- It is safe to apply the fix
5571 * low_freq -- This is a low frequency check (longer between checks)
5573 * Returns non-zero if addrbook was fixed
5576 adrbk_check_and_fix(PerAddrBook
*pab
, int safe
, int low_freq
, int check_now
)
5581 if(!as
.initialized
|| !pab
)
5584 dprint((7, "- adrbk_check_and_fix(%s) -\n",
5585 pab
->filename
? pab
->filename
: "?"));
5587 if(pab
->address_book
){
5591 chk_interval
= -60L * MAX(LOW_FREQ_CHK_INTERVAL
,
5592 ps_global
->remote_abook_validity
+ 5);
5596 adrbk_check_validity(pab
->address_book
, chk_interval
);
5598 if(pab
->address_book
->flags
& FILE_OUTOFDATE
||
5599 (pab
->address_book
->rd
&&
5600 pab
->address_book
->rd
->flags
& REM_OUTOFDATE
&&
5601 !(pab
->address_book
->rd
->flags
& USER_SAID_NO
))){
5603 OpenStatus save_status
;
5604 int save_rem_abook_valid
= 0;
5606 dprint((2, "adrbk_check_and_fix %s: fixing %s\n",
5607 debug_time(0,0,ps_global
->signal_in_progress
),
5608 pab
->filename
? pab
->filename
: "?"));
5609 if(ab_nesting_level
> 0){
5610 q_status_message3(SM_ORDER
, 0, 2,
5611 "Resyncing address book%.200s%.200s%.200s",
5612 as
.n_addrbk
> 1 ? " \"" : "",
5613 as
.n_addrbk
> 1 ? pab
->abnick
: "",
5614 as
.n_addrbk
> 1 ? "\"" : "");
5615 display_message('x');
5620 save_status
= pab
->ostatus
;
5622 /* don't do the trim right now */
5623 if(pab
->address_book
->rd
)
5624 pab
->address_book
->rd
->flags
&= ~DO_REMTRIM
;
5627 * Have to change this from -1 or we won't actually do
5630 if((pab
->address_book
->rd
&&
5631 pab
->address_book
->rd
->flags
& REM_OUTOFDATE
) &&
5632 ps_global
->remote_abook_validity
== -1){
5633 save_rem_abook_valid
= -1;
5634 ps_global
->remote_abook_validity
= 0;
5637 init_abook(pab
, TotallyClosed
);
5640 /* this sets up pab->so, so is important */
5641 pab
->access
= adrbk_access(pab
);
5644 * If we just re-init to HalfOpen... we won't actually
5645 * open the address book, which was open before. That
5646 * would be fine but it's a little nicer if we can open
5647 * it now so that we don't defer the resync until
5648 * the next open, which would be a user action for sure.
5649 * Right now we may be here during a newmail check
5650 * timeout, so this is a good time to do the resync.
5652 if(save_status
== HalfOpen
||
5653 save_status
== ThreeQuartOpen
||
5654 save_status
== Closed
)
5655 init_abook(pab
, NoDisplay
);
5657 init_abook(pab
, save_status
);
5659 if(save_rem_abook_valid
)
5660 ps_global
->remote_abook_validity
= save_rem_abook_valid
;
5662 if(ab_nesting_level
> 0 && pab
->ostatus
== save_status
)
5663 q_status_message3(SM_ORDER
, 0, 2,
5664 "Resynced address book%.200s%.200s%.200s",
5665 as
.n_addrbk
> 1 ? " \"" : "",
5666 as
.n_addrbk
> 1 ? pab
->abnick
: "",
5667 as
.n_addrbk
> 1 ? "\"" : "");
5671 "adrbk_check_and_fix: not safe to fix %s\n",
5672 pab
->filename
? pab
->filename
: "?"));
5680 static time_t last_check_and_fix_all
;
5682 * Fix out of date address books. This only fixes the AdrBk part of the
5683 * problem, not the pab and display. It closes and reopens the address_book
5684 * files, clears the caches, reads new data.
5686 * Args safe -- It is safe to apply the fix
5687 * low_freq -- This is a low frequency check (longer between checks)
5689 * Returns non-zero if an addrbook was fixed
5692 adrbk_check_and_fix_all(int safe
, int low_freq
, int check_now
)
5700 dprint((7, "- adrbk_check_and_fix_all -\n"));
5702 last_check_and_fix_all
= get_adj_time();
5704 for(i
= 0; i
< as
.n_addrbk
; i
++){
5705 pab
= &as
.adrbks
[i
];
5706 if(pab
->address_book
)
5707 ret
+= adrbk_check_and_fix(pab
, safe
, low_freq
, check_now
);
5718 adrbk_maintenance(void)
5720 static time_t last_time_here
= 0;
5723 long low_freq_interval
;
5725 dprint((9, "- adrbk_maintenance -\n"));
5730 now
= get_adj_time();
5732 if(now
< last_time_here
+ 120)
5735 last_time_here
= now
;
5737 low_freq_interval
= MAX(LOW_FREQ_CHK_INTERVAL
,
5738 ps_global
->remote_abook_validity
+ 5);
5740 /* check the time here to make it cheap */
5741 if(ab_nesting_level
== 0 &&
5742 ps_global
->remote_abook_validity
> 0 &&
5743 now
> last_check_and_fix_all
+ low_freq_interval
* 60L)
5744 (void)adrbk_check_and_fix_all(1, 1, 0);
5746 /* close down idle connections */
5747 for(i
= 0; i
< as
.n_addrbk
; i
++){
5750 pab
= &as
.adrbks
[i
];
5752 if(pab
->address_book
&&
5753 pab
->address_book
->type
== Imap
&&
5754 pab
->address_book
->rd
&&
5755 rd_stream_exists(pab
->address_book
->rd
)){
5757 "adrbk_maint: %s: idle cntr %ld (%ld)\n",
5758 pab
->address_book
->orig_filename
5759 ? pab
->address_book
->orig_filename
: "?",
5760 (long)(now
- pab
->address_book
->rd
->last_use
),
5761 (long)pab
->address_book
->rd
->last_use
));
5763 if(now
> pab
->address_book
->rd
->last_use
+ IMAP_IDLE_TIMEOUT
){
5765 "adrbk_maint %s: closing idle (%ld secs) connection: %s\n",
5766 debug_time(0,0,ps_global
->signal_in_progress
),
5767 (long)(now
- pab
->address_book
->rd
->last_use
),
5768 pab
->address_book
->orig_filename
5769 ? pab
->address_book
->orig_filename
: "?"));
5770 rd_close_remote(pab
->address_book
->rd
);
5774 * If we aren't going to close it, we ping it instead to
5775 * make sure it stays open and doesn't timeout on us.
5776 * This shouldn't be necessary unless the server has a
5777 * really short timeout. If we got killed for some reason
5778 * we set imap.stream to NULL.
5779 * Instead of just pinging, we may as well check for
5782 if(ab_nesting_level
== 0 &&
5783 ps_global
->remote_abook_validity
> 0){
5784 time_t save_last_use
;
5787 * We shouldn't count this as a real last_use.
5789 save_last_use
= pab
->address_book
->rd
->last_use
;
5790 (void)adrbk_check_and_fix(pab
, 1, 0, 1);
5791 pab
->address_book
->rd
->last_use
= save_last_use
;
5793 /* just ping it if not safe to fix it */
5794 else if(!rd_ping_stream(pab
->address_book
->rd
)){
5796 "adrbk_maint: %s: abook stream closed unexpectedly: %s\n",
5797 debug_time(0,0,ps_global
->signal_in_progress
),
5798 pab
->address_book
->orig_filename
5799 ? pab
->address_book
->orig_filename
: "?"));
5808 * Parses a string of comma-separated addresses or nicknames into an
5811 * Returns an allocated, null-terminated list, or NULL.
5814 parse_addrlist(char *addrfield
)
5816 #define LISTCHUNK 500 /* Alloc this many addresses for list at a time */
5818 char *next_addr
, *cur_addr
, *p
, *q
;
5819 int slots
= LISTCHUNK
;
5822 return((char **)NULL
);
5824 /* allocate first chunk */
5826 al
= (char **)fs_get(sizeof(char *) * (slots
+1));
5831 /* skip any leading whitespace */
5832 for(q
= p
; *q
&& *q
== SPACE
; q
++)
5835 next_addr
= (*q
) ? q
: NULL
;
5837 /* Loop adding each address in list to array al */
5838 for(cur_addr
= next_addr
; cur_addr
; cur_addr
= next_addr
){
5840 next_addr
= skip_to_next_addr(cur_addr
);
5845 /* allocate more space */
5846 if((ad
-al
) >= slots
){
5848 fs_resize((void **)&al
, sizeof(char *) * (slots
+1));
5849 ad
= al
+ slots
- LISTCHUNK
;
5858 /* free up any excess we've allocated */
5859 fs_resize((void **)&al
, sizeof(char *) * (ad
- al
));
5865 * Args cur -- pointer to the start of the current addr in list.
5867 * Returns a pointer to the start of the next addr or NULL if there are
5870 * Side effect: current addr has trailing white space removed
5871 * and is null terminated.
5874 skip_to_next_addr(char *cur
)
5881 char prev_char
= '\0';
5884 * Find delimiting comma or end.
5885 * Quoted commas and commented commas don't count.
5887 for(q
= cur
; *q
; q
++){
5890 if(!in_quotes
&& !in_comment
)
5895 if(!in_quotes
&& !in_comment
)
5900 if(in_comment
&& prev_char
!= BSLASH
)
5905 if(in_quotes
&& prev_char
!= BSLASH
)
5907 else if(!in_quotes
&& !in_comment
)
5919 if(*q
){ /* trailing comma case */
5921 ret_pointer
= q
+ 1;
5924 ret_pointer
= NULL
; /* no more addrs after cur */
5926 /* remove trailing white space from cur */
5927 for(p
= q
- 1; p
>= cur
&& isspace((unsigned char)*p
); p
--)
5930 return(ret_pointer
);
5935 * Add entries specified by system administrator. If the nickname already
5936 * exists, it is not touched.
5939 add_forced_entries(AdrBk
*abook
)
5942 char *nickname
, *fullname
, *address
;
5943 char *end_of_nick
, *end_of_full
, **t
;
5946 if(!ps_global
->VAR_FORCED_ABOOK_ENTRY
||
5947 !ps_global
->VAR_FORCED_ABOOK_ENTRY
[0] ||
5948 !ps_global
->VAR_FORCED_ABOOK_ENTRY
[0][0])
5951 for(t
= ps_global
->VAR_FORCED_ABOOK_ENTRY
; t
[0] && t
[0][0]; t
++){
5955 * syntax for each element is
5956 * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
5959 /* find end of nickname */
5960 end_of_nick
= nickname
;
5962 && !isspace((unsigned char)*end_of_nick
)
5963 && *end_of_nick
!= '|')
5966 /* find the pipe character between nickname and fullname */
5967 fullname
= end_of_nick
;
5968 while(*fullname
&& *fullname
!= '|')
5974 *end_of_nick
= '\0';
5975 abe
= adrbk_lookup_by_nick(abook
, nickname
, NULL
);
5977 if(!abe
){ /* If it isn't there, add it */
5979 /* skip whitespace before fullname */
5980 fullname
= skip_white_space(fullname
);
5982 /* find the pipe character between fullname and address */
5983 end_of_full
= fullname
;
5984 while(*end_of_full
&& *end_of_full
!= '|')
5989 "missing | in forced-abook-entry \"%s\"\n",
5990 nickname
? nickname
: "?"));
5994 address
= end_of_full
+ 1;
5996 /* skip whitespace before address */
5997 address
= skip_white_space(address
);
5999 if(*address
== '('){
6001 "no lists allowed in forced-abook-entry \"%s\"\n",
6002 address
? address
: "?"));
6006 /* go back and remove trailing white space from fullname */
6007 while(*end_of_full
== '|' || isspace((unsigned char)*end_of_full
)){
6008 *end_of_full
= '\0';
6013 "Adding forced abook entry \"%s\"\n", nickname
? nickname
: ""));
6015 (void)adrbk_add(abook
,
6023 (adrbk_cntr_t
*)NULL
,
6032 /* Go through the list of addressbooks and check if any
6033 * of them point to the given stream.
6036 any_addressbook_in_remote_stream(MAILSTREAM
*stream
)
6040 char *nickname
= NULL
;
6041 char *filename
= NULL
;
6045 if(ps_global
->VAR_ADDRESSBOOK
&&
6046 ps_global
->VAR_ADDRESSBOOK
[num
] &&
6047 ps_global
->VAR_ADDRESSBOOK
[num
][0]){
6048 q
= ps_global
->VAR_ADDRESSBOOK
[num
++];
6051 else if(ps_global
->VAR_GLOB_ADDRBOOK
&&
6052 ps_global
->VAR_GLOB_ADDRBOOK
[i
-num
] &&
6053 ps_global
->VAR_GLOB_ADDRBOOK
[i
-num
][0]){
6054 q
= ps_global
->VAR_GLOB_ADDRBOOK
[i
- num
];
6058 get_pair(q
, &nickname
, &filename
, 0, 0);
6060 if(nickname
) fs_give((void **)&nickname
);
6064 && same_stream(filename
, stream
) != NULL
)
6066 fs_give((void **)&filename
);
6069 } while (rv
== 0 && q
!= NULL
);