summaryrefslogtreecommitdiff
path: root/pith/adrbklib.h
blob: a66d45b7e9699631964d9376deb54aed6393ab8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
/*
 * $Id: adrbklib.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
 *
 * ========================================================================
 * Copyright 2006-2008 University of Washington
 * Copyright 2013-2020 Eduardo Chappa
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * ========================================================================
 */

#ifndef PITH_ADRBKLIB_INCLUDED
#define PITH_ADRBKLIB_INCLUDED


#include "../pith/indxtype.h"
#include "../pith/remtype.h"
#include "../pith/store.h"
#include "../pith/string.h"


/*
 *  Some notes:
 *
 * The order that the address book is stored in on disk is the order that
 * the entries will be displayed in. When an address book is opened,
 * if it is ReadWrite the sort order is checked and it is sorted if it is
 * out of order.
 *
 * Considerable complexity that previously existed in the address book
 * code has been removed. Cpu speeds have improved significantly and
 * memory has increased dramatically, as well. The cost of the complexity
 * of the lookup file and hashtables and all that stuff is thought to be
 * more than the benefits. See pre-5.00 pine adrbklib.h for the way it
 * used to be.
 *
 * The display code has remained the same.
 * There is also some allocation happening
 * in addrbook.c. In particular, the display is a window into an array
 * of rows, at least one row per addrbook entry plus more for lists.
 * Each row is an AddrScrn_Disp structure and those should typically take
 * up 6 or 8 bytes. A cached copy of addrbook entries is not kept, just
 * the element number to look it up (and often get it out of the EntryRef
 * cache). In order to avoid having to allocate all those rows, this is
 * also in the form of a cache. Only 3 * screen_length rows are kept in
 * the cache, and the cache is always a simple interval of rows. That is,
 * rows between valid_low and valid_high are all in the cache. Row numbers
 * in the display list are a little mysterious. There is no origin. That
 * is, you don't necessarily know where the start or end of the display
 * is. You only know how to go forward and backward and how to "warp"
 * to new locations in the display and go forward and backward from there.
 * This is because we don't know how many rows there are all together. It
 * is also a way to avoid searching through everything to get somewhere.
 * If you want to go to the End, you warp there and start backwards instead
 * of reading through all the entries to get there. If you edit an entry
 * so that it sorts into a new location, you warp to that new location to
 * save processing all of the entries in between.
 *
 *
 * Notes about RFC1522 encoding:
 *
 * If the fullname field contains other than US-ASCII characters, it is
 * encoded using the rules of RFC1522 or its successor. The value actually
 * stored in the file is encoded, even if it matches the character set of
 * the user. This is so it is easy to pass the entry around or to change
 * character sets without invalidating entries in the address book. When
 * a fullname is displayed, it is first decoded. If the fullname field is
 * encoded as being from a character set other than the user's character
 * set, that will be retained until the user edits the field. Then it will
 * change over to the user's character set. The comment field works in
 * the same way, as do the "phrase" fields of any addresses. On outgoing
 * mail, the correct character set will be retained if you use ComposeTo
 * from the address book screen. However, if you use a nickname from the
 * composer or ^T from the composer, the character set will be lost if it
 * is different from the user's character set.
 *
 *
 * Notes about RemoteViaImap address books:
 *
 * There are currently two types of address books, Local and Imap. Local means
 * it is in a local file. Imap means it is stored in a folder on a remote
 * IMAP server. The folder is a regular folder containing mail messages but
 * the messages are special. The first message is a header message. The last
 * message is the address book data. In between messages are old versions of
 * the address book data. The address book data is stored in the message as
 * it would be on disk, with no fancy mime encoding or anything. When it is
 * used the data from the last message in the folder is copied to a local file
 * and then it is used just like a local file. The local file is a cache for
 * the remote data. We can tell the remote data has been changed by looking
 * at the Date of the last message in the remote folder. If we see it has
 * changed we copy the whole file over again and replace the cache file.
 * A possibly quicker way to tell if it has changed is if the UID has
 * changed or the number of messages in the folder has changed. We use those
 * methods if possible since they don't require opening a new stream and
 * selecting the folder.  There is one metadata file for address book data.
 * The name of that file is stored in the pinerc file. It contains the names
 * of the cache files for RemoveViaImap address books plus other caching
 * information for those address books (uid...).
 */

#define NFIELDS 11 /* one more than num of data fields in addrbook entry */

/*
 * The type a_c_arg_t is a little confusing. It's the type we use in
 * place of adrbk_cntr_t when we want to pass an adrbk_cntr_t in an argument.
 * We were running into problems with the integral promotion of adrbk_cntr_t
 * args. A_c_arg_t has to be large enough to hold a promoted adrbk_cntr_t.
 * So, if adrbk_cntr_t is unsigned short, then a_c_arg_t needs to be int if
 * int is larger than short, or unsigned int if short is same size as int.
 * Since usign16_t always fits in a short, a_c_arg_t of unsigned int should
 * always work for !HUGE. For HUGE, UINT32 will be either an unsigned int
 * or an unsigned long. If it is an unsigned long, then a_c_arg_t better be
 * an unsigned long, too. If it is an unsigned int, then a_c_arg_t could
 * be an unsigned int, too. However, if we just make it unsigned long, then
 * it will be the same in all cases and big enough in all cases.
 */

#define adrbk_cntr_t UINT32  /* addrbook counter type                */
typedef unsigned long a_c_arg_t;     /* type of arg passed for adrbk_cntr_t  */
#define NO_NEXT ((adrbk_cntr_t)-1)
#define MAX_ADRBK_SIZE (2000000000L) /* leave room for extra display lines   */

/*
 * The value NO_NEXT is reserved to mean that there is no next address, or that
 * there is no address number to return. This is similar to getc returning
 * -1 when there is no char to get, but since we've defined this to be
 * unsigned we need to reserve one of the valid values for this purpose.
 * With current implementation it needs to be all 1's, so memset initialization
 * will work correctly.
 */

typedef enum {NotSet, Single, List} Tag;


/* This is what is actually used by the routines that manipulate things */
typedef struct adrbk_entry {
    char *nickname;    /* UTF-8                                         */
    char *fullname;    /* of simple addr or list (stored in UTF-8)      */
    union addr {
        char *addr;    /* for simple Single entries                     */
        char **list;   /* for distribution lists                        */
    } addr;
    char *fcc;         /* fcc specific for when sending to this address */
    char *extra;       /* comments field (stored in UTF-8)              */
    char  referenced;  /* for detecting loops during lookup             */
    Tag   tag;         /* single addr (Single) or a list (List)         */
} AdrBk_Entry;


typedef struct abook_tree_node {
    struct abook_tree_node *down;	/* next letter of nickname */
    struct abook_tree_node *right;	/* alternate letter of nickname */
    char                    value;	/* character at this node */
    adrbk_cntr_t            entrynum;	/* use NO_NEXT as no-data indicator */
} AdrBk_Trie;


/* information useful for displaying the addrbook */
typedef struct width_stuff {
    int max_nickname_width;
    int max_fullname_width;
    int max_addrfield_width;
    int max_fccfield_width;
    int third_biggest_fullname_width;
    int third_biggest_addrfield_width;
    int third_biggest_fccfield_width;
} WIDTH_INFO_S;


typedef struct expanded_list {
    adrbk_cntr_t          ent;
    struct expanded_list *next;
} EXPANDED_S;


typedef enum {Local, Imap} AdrbkType;


#ifndef IMAP_IDLE_TIMEOUT
#define IMAP_IDLE_TIMEOUT	(10L * 60L)	/* seconds */
#endif
#ifndef FILE_VALID_CHK_INTERVAL
#define FILE_VALID_CHK_INTERVAL (      15L)	/* seconds */
#endif
#ifndef LOW_FREQ_CHK_INTERVAL
#define LOW_FREQ_CHK_INTERVAL	(240)		/* minutes */
#endif

typedef struct adrbk {
    AdrbkType	  type;                /* type of address book               */
    char         *orig_filename;       /* passed in filename                 */
    char         *filename;            /* addrbook filename                  */
    char         *our_filecopy;        /* session copy of filename contents  */
    FILE         *fp;                  /* fp for our_filecopy                */
    adrbk_cntr_t  count;               /* how many entries in addrbook       */
    adrbk_cntr_t  del_count;           /* how many #DELETED entries in abook */
    AdrBk_Entry  *arr;                 /* array of entries                   */
    AdrBk_Entry  *del;                 /* array of deleted entries           */
    AdrBk_Trie   *nick_trie;
    AdrBk_Trie   *addr_trie;
    AdrBk_Trie   *full_trie;
    AdrBk_Trie   *revfull_trie;
    time_t        last_change_we_know_about;/* to look for others changing it*/
    time_t        last_local_valid_chk;/* when valid check was done          */
    unsigned      flags;               /* see defines in alpine.h (DEL_FILE...)*/
    WIDTH_INFO_S  widths;              /* helps addrbook.c format columns    */
    int           sort_rule;
    EXPANDED_S   *exp;                 /* this is for addrbook.c to use.  A
			       list of expanded list entry nums is kept here */
    EXPANDED_S   *checks;              /* this is for addrbook.c to use.  A
			       list of checked entry nums is kept here */
    EXPANDED_S   *selects;             /* this is for addrbook.c to use.  A
			       list of selected entry nums is kept here */
    REMDATA_S    *rd;
} AdrBk;


/*
 * The definitions starting here have to do with the virtual scrolling
 * region view into the addressbooks. That is, the display.
 *
 * We could make every use of an AdrBk_Entry go through a function call
 * like adrbk_get_ae(). Instead, we try to be smart and avoid the extra
 * function calls by knowing when the addrbook entry is still valid, either
 * because we haven't called any functions that could invalidate it or because
 * we have locked it in the cache. If we do lock it, we need to be careful
 * that it eventually gets unlocked. That can be done by an explicit
 * adrbk_get_ae(Unlock) call, or it is done implicitly when the address book
 * is written out. The reason it can get invalidated is that the abe that
 * we get returned to us is just a pointer to a cached addrbook entry, and
 * that entry can be flushed from the cache by other addrbook activity.
 * So we need to be careful to make sure the abe is certain to be valid
 * before using it.
 *
 * Data structures for the display of the address book. There's one
 * of these structures per line on the screen.
 *
 * Types: Title -- The title line for the different address books. It has
 *		   a ptr to the text of the Title line.
 * ClickHereCmb -- This is the line that says to click here to
 *                 expand.  It changes types into the individual expanded
 *                 components once it is expanded.  It doesn't have any data
 *                 other than an implicit title. This is only used with the
 *                 combined-style addrbook display.
 * ListClickHere --This is the line that says to click here to
 *                 expand the members of a distribution list. It changes
 *                 types into the individual expanded ListEnt's (if any)
 *                 when it is expanded. It has a ptr to an AdrBk_Entry.
 *    ListEmpty -- Line that says this is an empty distribution list. No data.
 *        Empty -- Line that says this is an empty addressbook. No data.
 *    ZoomEmpty -- Line that says no addrs in zoomed view. No data.
 * AddFirstGLob -- Place holder for adding an abook. No data.
 * AddFirstPers -- Place holder for adding an abook. No data.
 *   DirServers -- Place holder for accessing directory servers. No data.
 *       Simple -- A single addressbook entry. It has a ptr to an AdrBk_Entry.
 *                 When it is displayed, the fields are usually:
 *                 <nickname>       <fullname>       <address or another nic>
 *     ListHead -- The head of an address list. This has a ptr to an
 *		   AdrBk_Entry.
 *                 <blank line> followed by
 *                 <nickname>       <fullname>       "DISTRIBUTION LIST:"
 *      ListEnt -- The rest of an address list. It has a pointer to its
 *		   ListHead element and a ptr (other) to this specific address
 *		   (not a ptr to another AdrBk_Entry).
 *                 <blank>          <blank>          <address or another nic>
 *         Text -- A ptr to text. For example, the ----- lines and
 *		   whitespace lines.
 *    NoAbooks  -- There are no address books at all.
 *   Beginnning -- The (imaginary) elements before the first real element
 *          End -- The (imaginary) elements after the last real element
 */
typedef enum {DlNotSet, Empty, ZoomEmpty, AddFirstPers, AddFirstGlob,
	      AskServer, Title, Simple, ListHead, ListClickHere,
	      ListEmpty, ListEnt, Text, Beginning, End, NoAbooks,
	      ClickHereCmb, TitleCmb} LineType;
/* each line of address book display is one of these structures */
typedef struct addrscrn_disp {
    union {
        struct {
            adrbk_cntr_t  ab_element_number; /* which addrbook entry     */
	    adrbk_cntr_t  ab_list_offset;    /* which member of the list */
        }addrbook_entry;
        char        *text_ptr;               /* a UTF-8 string */
    }union_to_save_space;
    LineType       type;
} AddrScrn_Disp;
#define usst union_to_save_space.text_ptr
#define elnum union_to_save_space.addrbook_entry.ab_element_number
#define l_offset union_to_save_space.addrbook_entry.ab_list_offset

#define entry_is_checked    exp_is_expanded
#define entry_get_next      exp_get_next
#define entry_set_checked   exp_set_expanded
#define entry_unset_checked exp_unset_expanded
#define any_checked         exp_any_expanded
#define howmany_checked     exp_howmany_expanded

#define entry_is_selected   exp_is_expanded
#define entry_set_selected  exp_set_expanded
#define entry_unset_selected exp_unset_expanded
#define any_selected        exp_any_expanded
#define howmany_selected    exp_howmany_expanded

#define entry_is_deleted    exp_is_expanded
#define entry_set_deleted   exp_set_expanded
#define howmany_deleted     exp_howmany_expanded

#define entry_is_added      exp_is_expanded
#define entry_set_added     exp_set_expanded

/*
 * Argument to expand_address and build_address_internal is a BuildTo,
 * which is either a char * address or an AdrBk_Entry * (if we have already
 * looked it up in an addrbook).
 */
typedef enum {Str, Abe} Build_To_Arg_Type;
typedef struct build_to {
    Build_To_Arg_Type type;
    union {
	char        *str;  /* normal looking address string */
	AdrBk_Entry *abe;  /* addrbook entry */
    }arg;
} BuildTo;


/* Display lines used up by each top-level addrbook, counting blanks */
#define LINES_PER_ABOOK (3)
/* How many of those lines are visible (not blank) */
#define VIS_LINES_PER_ABOOK (2)
/* How many extra lines are between the personal and global sections */
#define XTRA_LINES_BETWEEN (1)
/* How many lines does the PerAdd line take, counting blank line */
#define LINES_PER_ADD_LINE (2)
/* Extra title lines above first entry that are shown when the combined-style
   display is turned on. */
#define XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP (4)

typedef enum {DlcNotSet,
	      DlcPersAdd,		/* config screen only */
	      DlcGlobAdd,		/*    "      "    "   */

	      DlcTitle,			/* top level displays */
	      DlcTitleNoPerm,		/*  "    "      "     */
	      DlcSubTitle,		/*  "    "      "     */
	      DlcTitleBlankTop,		/*  "    "      "     */
	      DlcGlobDelim1,		/*  "    "      "     */
	      DlcGlobDelim2,		/*  "    "      "     */
	      DlcDirDelim1,		/*  "    "      "     */
	      DlcDirDelim2,		/*  "    "      "     */
	      DlcDirAccess,		/*  "    "      "     */
	      DlcDirSubTitle,		/*  "    "      "     */
	      DlcDirBlankTop,		/*  "    "      "     */

	      DlcTitleDashTopCmb,	/* combined-style top level display */
	      DlcTitleCmb,		/*     "      "    "    "      "    */
	      DlcTitleDashBottomCmb,	/*     "      "    "    "      "    */
	      DlcTitleBlankBottomCmb,	/*     "      "    "    "      "    */
	      DlcClickHereCmb,		/*     "      "    "    "      "    */
	      DlcTitleBlankTopCmb,	/*     "      "    "    "      "    */
	      DlcDirDelim1a,		/*     "      "    "    "      "    */
	      DlcDirDelim1b,		/*     "      "    "    "      "    */
	      DlcDirDelim1c,		/*     "      "    "    "      "    */

	      DlcEmpty,			/* display of a single address book */
	      DlcZoomEmpty,		/*    "                             */
	      DlcNoPermission,		/*    "                             */
	      DlcSimple,		/*    "                             */
	      DlcListHead,		/*    "                             */
	      DlcListClickHere,		/*    "                             */
	      DlcListEmpty,		/*    "                             */
	      DlcListEnt,		/*    "                             */
	      DlcListBlankTop,		/*    "                             */
	      DlcListBlankBottom,	/*    "                             */
	      DlcNoAbooks,		/*    "                             */

	      DlcOneBeforeBeginning,	/* used in both */
	      DlcTwoBeforeBeginning,	/*  "   "   "   */
	      DlcBeginning,		/*  "   "   "   */
	      DlcEnd} DlCacheType;

typedef enum {Initialize, FirstEntry, LastEntry, ArbitraryStartingPoint,
	      DoneWithCache, FlushDlcFromCache, Lookup} DlMgrOps;
typedef enum {Warp, DontWarp} HyperType;

/*
 * The DlCacheTypes are the types that a dlcache element can be labeled.
 * The idea is that there needs to be enough information in the single
 * cache element by itself so that you can figure out what the next and
 * previous dl rows are by just knowing this one row.
 *
 * In the top-level display, there are DlcTitle lines or DlcTitleNoPerm
 * lines, which are the same except we know that we can't access the
 * address book in the latter case. DlcSubTitle lines follow each of the
 * types of Title lines, and Titles within a section are separated by
 * DlcTitleBlankTop lines, which belong to (have the same adrbk_num as)
 * the Title they are above.
 * If there are no address books and no directory servers defined, we
 * have a DlcNoAbooks line. When we are displaying an individual address
 * book (not in the top-level display) there is another set of types. An
 * empty address book consists of one line of type DlcEmpty. An address
 * book without read permission is a DlcNoPermission. Simple address book
 * entries consist of a single DlcSimple line. Lists begin with a
 * DlcListHead. If the list is not expanded the DlcListHead is followed by
 * a DlcListClickHere. If it is known to be a list with no members the
 * DlcListHead is followed by a DlcListEmpty. If there are members and
 * the list is expanded, each list member is a single line of type
 * DlcListEnt. Two lists are separated by a DlcListBlankBottom belonging
 * to the first list. A list followed or preceded by a DlcSimple address
 * row has a DlcListBlank(Top or Bottom) separating it from the
 * DlcSimple. Above the top row of the display is an imaginary line of
 * type DlcOneBeforeBeginning. Before that is a DlcTwoBeforeBeginning. And
 * before that all the lines are just DlcBeginning lines. After the last
 * display line is a DlcEnd.
 *
 * The DlcDirAccess's are indexed by adrbk_num (re-used for this).
 * Adrbk_num -1 means access all of the servers.
 * Adrbk_num 0 ... n_serv -1 means access all a particular server.
 * Adrbk_num n_serv means access as if from composer using config setup.
 *
 * Here are the types of lines and where they fall in the top-level display:
 *
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcTwoBeforeBeginning
 * (not a visible line)			DlcOneBeforeBeginning
 *     Title				DlcTitle (or TitleNoPerm)
 *       Subtitle			DlcSubTitle
 * ---this is blank----------------	DlcTitleBlankTop
 *     Title				DlcTitle (or TitleNoPerm)
 *       Subtitle			DlcSubTitle
 * ---this is blank----------------	DlcGlobDelim1
 * ---this is blank----------------	DlcGlobDelim2
 *     Title				DlcTitle (or TitleNoPerm)
 *       Subtitle			DlcSubTitle
 * ---this is blank----------------	DlcTitleBlankTop
 *     Title				DlcTitle (or TitleNoPerm)
 *       Subtitle			DlcSubTitle
 * ---this is blank----------------	DlcDirDelim1
 * ---this is blank----------------	DlcDirDelim2
 *     Directory (query server 1)	DlcDirAccess (adrbk_num 0)
 *       Subtitle 			DlcDirSubTitle (adrbk_num 0)
 * ---this is blank----------------	DlcDirBlankTop
 *     Directory (query server 2)	DlcDirAccess (adrbk_num 1)
 *       Subtitle 			DlcDirSubTitle (adrbk_num 1)
 * (not a visible line)			DlcEnd
 * (not a visible line)			DlcEnd
 *
 *
 * There is a combined-style display triggered by the F_CMBND_ABOOK_DISP
 * feature. It's a mixture of the top-level and open addrbook displays. When an
 * addrbook is opened the rest of the addrbooks don't disappear from the
 * screen. In this view, the ClickHere lines can be replaced with the entire
 * contents of the addrbook, but the other stuff remains on the screen, too.
 * Here are the types of lines and where they fall in the
 * combined-style display:
 *
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcTwoBeforeBeginning
 * (not a visible line)			DlcOneBeforeBeginning
 * --------------------------------	DlcTitleDashTopOld
 *     Title				DlcTitleOld
 * --------------------------------	DlcTitleDashBottomOld
 * ---this is blank----------------	DlcTitleBlankBottom
 *     ClickHere			DlcClickHereOld
 * ---this is blank----------------	DlcTitleBlankTop
 * --------------------------------	DlcTitleDashTopOld
 *     Title				DlcTitleOld
 * --------------------------------	DlcTitleDashBottomOld
 * ---this is blank----------------	DlcTitleBlankBottom
 *     ClickHere			DlcClickHereOld
 * ---this is blank----------------	DlcDirDelim1
 * --------------------------------	DlcDirDelim1a
 * Directories                      	DlcDirDelim1b
 * --------------------------------	DlcDirDelim1c
 * ---this is blank----------------	DlcDirDelim2
 *     Directory (query server 1)	DlcDirAccess (adrbk_num 0)
 *       Subtitle 			DlcDirSubTitle (adrbk_num 0)
 * ---this is blank----------------	DlcDirBlankTop
 *     Directory (query server 2)	DlcDirAccess (adrbk_num 1)
 *       Subtitle 			DlcDirSubTitle (adrbk_num 1)
 * (not a visible line)			DlcEnd
 * (not a visible line)			DlcEnd
 *
 * If there are no addrbooks in either of the two sections, or no Directory
 * servers, then that section is left out of the display. If there is only
 * one address book and no Directories, then the user goes directly into the
 * single addressbook view which looks like:
 *
 * if(no entries in addrbook)
 *
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcTwoBeforeBeginning
 * (not a visible line)			DlcOneBeforeBeginning
 *     Empty or NoPerm or NoAbooks	DlcEmpty, DlcZoomEmpty, DlcNoPermission,
 *					or DlcNoAbooks
 * (not a visible line)			DlcEnd
 * (not a visible line)			DlcEnd
 *
 * else
 *
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcBeginning
 * (not a visible line)			DlcTwoBeforeBeginning
 * (not a visible line)			DlcOneBeforeBeginning
 *     Simple Entry			DlcSimple
 *     Simple Entry			DlcSimple
 *     Simple Entry			DlcSimple
 *					DlcListBlankTop
 *     List Header			DlcListHead
 *          Unexpanded List		DlcListClickHere
 *          or
 *          Empty List			DlcListEmpty
 *          or
 *          List Entry 1		DlcListEnt
 *          List Entry 2		DlcListEnt
 *					DlcListBlankBottom
 *     List Header			DlcListHead
 *          List Entry 1		DlcListEnt
 *          List Entry 2		DlcListEnt
 *          List Entry 3		DlcListEnt
 *					DlcListBlankBottom
 *     Simple Entry			DlcSimple
 *					DlcListBlankTop
 *     List Header			DlcListHead
 *          Unexpanded List		DlcListClickHere
 * (not a visible line)			DlcEnd
 * (not a visible line)			DlcEnd
 *
 * The config screen view is similar to the top-level view except there
 * is no directory section (it has it's own config screen) and if there
 * are zero personal addrbooks or zero global addrbooks then a placeholder
 * line of type DlcPersAdd or DlcGlobAdd takes the place of the DlcTitle
 * line.
 */
typedef struct dl_cache {
    long         global_row; /* disp_list row number */
    adrbk_cntr_t dlcelnum;   /* which elnum from that addrbook */
    adrbk_cntr_t dlcoffset;  /* offset in a list, only for ListEnt rows */
    short        adrbk_num;  /* which address book we're related to */
    DlCacheType  type;       /* type of this row */
    AddrScrn_Disp dl;	     /* the actual dl that goes with this row */
} DL_CACHE_S;


typedef enum {Nickname, Fullname, Addr, Filecopy, Comment, Notused,
	      Def, WhenNoAddrDisplayed, Checkbox, Selected} ColumnType;

/*
 * Users can customize the addrbook display, so this tells us which data
 * is in a particular column and how wide the column is. There is an
 * array of these per addrbook, of length NFIELDS (number of possible cols).
 */
typedef struct column_description {
    ColumnType type;
    WidthType  wtype;
    int        req_width; /* requested width (for fixed and percent types) */
    int        width;     /* actual width to use */
    int        old_width;
} COL_S;


/* address book attributes for peraddrbook type */
#define GLOBAL		0x1	/* else it is personal */
#define REMOTE_VIA_IMAP	0x2	/* else it is a local file  */


typedef enum {TotallyClosed, /* hash tables not even set up yet               */
	      Closed,     /* data not read in, no display list                */
	      NoDisplay,  /* data is accessible, no display list              */
	      HalfOpen,   /* data not accessible, initial display list is set */
	      ThreeQuartOpen, /* like HalfOpen without partial_close          */
	      Open        /* data is accessible and display list is set       */
	     } OpenStatus;

/*
 * There is one of these per addressbook.
 */
typedef struct peraddrbook {
    unsigned        	type;
    AccessType          access;
    OpenStatus          ostatus;
    char               *abnick,	             /* kept internally in UTF-8 */
		       *filename;
    AdrBk              *address_book;        /* the address book handle */
    int                 gave_parse_warnings;
    COL_S               disp_form[NFIELDS];  /* display format */
    int			nick_is_displayed;   /* these are for convenient, */
    int			full_is_displayed;   /* fast access.  Could get   */
    int			addr_is_displayed;   /* same info from disp_form. */
    int			fcc_is_displayed;
    int			comment_is_displayed;
    STORE_S	       *so;                  /* storage obj for addrbook
						temporarily stored here   */
} PerAddrBook;


/*
 * This keeps track of the state of the screen and information about all
 * the address books. We usually only have one of these but sometimes
 * we save a version of this state (with save_state) and re-call the
 * address book functions. Then when we pop back up to where we were
 * (with restore_state) the screen and the state of the address books
 * is restored to what it was.
 */
typedef struct addrscreenstate {
    PerAddrBook   *adrbks;       /* array of addrbooks                    */
    int		   initialized,  /* have we done at least simple init?    */
                   n_addrbk,     /* how many addrbooks are there          */
                   how_many_personals, /* how many of those are personal? */
                   cur,          /* current addrbook                      */
                   cur_row,      /* currently selected line               */
                   old_cur_row,  /* previously selected line              */
                   l_p_page;	 /* lines per (screen) page               */
    long           top_ent;      /* index in disp_list of top entry on screen */
    int            ro_warning,   /* whether or not to give warning        */
                   checkboxes,   /* whether or not to display checkboxes  */
                   selections,   /* whether or not to display selections  */
                   do_bold,      /* display selections in bold            */
                   no_op_possbl, /* user can't do anything with current conf */
                   zoomed,       /* zoomed into view only selected entries */
                   config,       /* called from config screen             */
                   n_serv,       /* how many directory servers are there  */
                   n_impl;       /* how many of those have impl bit set   */
#ifdef	_WINDOWS
    long	   last_ent;	 /* index of last known entry		  */
#endif
} AddrScrState;


/*
 * AddrBookScreen and AddrBookConfig are the maintenance screens, all the
 * others are selection screens. The AddrBookConfig screen is an entry
 * point from the Setup/Addressbooks command in the main menu. Those that
 * end in Com are called from the pico HeaderEditor, either while in the
 * composer or while editing an address book entry.  SelectManyNicks
 * returns a comma-separated list of nicknames. SelectAddrLccCom and
 * SelectNicksCom return a comma-separated list of nicknames.
 * SelectNickTake, SelectNickCom, and SelectNick all return a single
 * nickname.  The ones that returns multiple nicknames or multiple
 * addresses all allow ListMode. They are SelectAddrLccCom,
 * SelectNicksCom, and SelectMultNoFull.
 */
typedef enum {AddrBookScreen,	   /* maintenance screen                     */
	      AddrBookConfig,	   /* config screen                          */
	      SelectAddrLccCom,	   /* returns list of nicknames of lists     */
	      SelectNicksCom,	   /* just like SelectAddrLccCom, but allows
				      selecting simple *and* list entries    */
	      SelectNick,	   /* returns single nickname                */
	      SelectNickTake,	   /* Same as SelectNick but different help  */
	      SelectNickCom,	   /* Same as SelectNick but from composer   */
	      SelectManyNicks,	   /* Returns list of nicks */
	      SelectAddr,	   /* Returns single address */
	      SelectAddrNoFull,	   /* Returns single address without fullname */
	      SelectMultNoFull	   /* Returns mult addresses without fullname */
	     } AddrBookArg;


typedef struct save_state_struct {
    AddrScrState *savep;
    OpenStatus   *stp;
    DL_CACHE_S   *dlc_to_warp_to;
} SAVE_STATE_S;


typedef struct act_list {
    PerAddrBook *pab;
    adrbk_cntr_t num,
		 num_in_dst;
    unsigned int skip:1,
		 dup:1;
} ACTION_LIST_S;


typedef struct ta_abook_state {
    PerAddrBook  *pab;
    SAVE_STATE_S  state;
} TA_STATE_S;


/*
 * Many of these should really only have a single value but we give them
 * an array for uniformity.
 */
typedef struct _vcard_info {
    char **nickname;
    char **fullname;
    char  *first;
    char  *middle;
    char  *last;
    char **fcc;
    char **note;
    char **title;
    char **tel;
    char **email;
} VCARD_INFO_S;


extern AddrScrState as;
extern jmp_buf      addrbook_changed_unexpectedly;
extern long         msgno_for_pico_callback;
extern BODY        *body_for_pico_callback;
extern ENVELOPE    *env_for_pico_callback;
extern int          ab_nesting_level;


/*
 * These constants are supposed to be suitable for use as longs where the longs
 * are representing a line number or message number.
 * These constants aren't suitable for use with type adrbk_cntr_t. There is
 * a constant called NO_NEXT which you probably want for that.
 */
#define NO_LINE         (2147483645L)
#define CHANGED_CURRENT (NO_LINE + 1L)


/*
 * The do-while stuff is so these are statements and can be written with
 * a following ; like a regular statement without worrying about braces and all.
 */
#define SKIP_SPACE(p) do{while(*p && *p == SPACE)p++;}while(0)
#define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
#define RM_END_SPACE(start,end)                                             \
	    do{char *_ptr = end;                                            \
	       while(--_ptr >= start && *_ptr == SPACE)*_ptr = '\0';}while(0)
#define REPLACE_NEWLINES_WITH_SPACE(p)                                      \
		do{register char *_qq;                                      \
		   for(_qq = p; *_qq; _qq++)                                \
		       if(*_qq == '\n' || *_qq == '\r')                     \
			   *_qq = SPACE;}while(0)
#define DELETED "#DELETED-"
#define DELETED_LEN 9


#define ONE_HUNDRED_DAYS (60L * 60L * 24L * 100L)

/*
 * When address book entries are deleted, they are left in the file
 * with the nickname prepended with a string like #DELETED-96/01/25#, 
 * which stands for year 96, month 1, day 25 of the month. When one of
 * these entries is more than ABOOK_DELETED_EXPIRE_TIME seconds old,
 * then it will be totally removed from the address book the next time
 * an adrbk_write() is done. This is for emergencies where somebody
 * deletes something from their address book and would like to get it
 * back. You get it back by editing the nickname field manually to remove
 * the extra 18 characters off the front.
 */
#ifndef ABOOK_DELETED_EXPIRE_TIME
#define ABOOK_DELETED_EXPIRE_TIME   ONE_HUNDRED_DAYS
#endif


#ifdef	ENABLE_LDAP
typedef struct _cust_filt {
    char *filt;
    int   combine;
} CUSTOM_FILT_S;

#define RUN_LDAP	"LDAP: "
#define LEN_RL		6
#define QRUN_LDAP	"\"LDAP: "
#define LEN_QRL		7
#define LDAP_DISP	"[ LDAP Lookup ]"
#endif


/*
 * There are no restrictions on the length of any of the fields, except that
 * there are some restrictions in the current input routines.
 */

/*
 * The on-disk address book has entries that look like:
 *
 * Nickname TAB Fullname TAB Address_Field TAB Fcc TAB Comment
 *
 * An entry may be broken over more than one line but only at certain
 * spots. A continuation line starts with spaces (spaces, not white space).
 * One place a line break can occur is after any of the TABs. The other
 * place is in the middle of a list of addresses, between addresses.
 * The Address_Field may be either a simple address without the fullname
 * or brackets, or it may be an address list. An address list is
 * distinguished by the fact that it begins with "(" and ends with ")".
 * Addresses within a list are comma separated and each address in the list
 * may be a full rfc822 address, including Fullname and so on.
 *
 * Examples:
 * fred TAB Flintstone, Fred TAB fred@bedrock.net TAB fcc-flintstone TAB comment
 * or
 * fred TAB Flintstone, Fred TAB \n
 *    fred@bedrock.net TAB fcc-flintstone TAB \n
 *    comment
 * somelist TAB Some List TAB (fred, \n
 *    Barney Rubble <barney@bedrock.net>, wilma@bedrock.net) TAB \n
 *    fcc-for-some-list TAB comment
 */


/* exported prototypes */
AdrBk         *adrbk_open(PerAddrBook *, char *, char *, size_t, int);
int            adrbk_is_in_sort_order(AdrBk *, int);
adrbk_cntr_t   adrbk_count(AdrBk *);
AdrBk_Entry   *adrbk_get_ae(AdrBk *, a_c_arg_t);
AdrBk_Entry   *adrbk_lookup_by_nick(AdrBk *, char *, adrbk_cntr_t *);
AdrBk_Entry   *adrbk_lookup_by_addr(AdrBk *, char *, adrbk_cntr_t *);
char          *adrbk_formatname(char *, char **, char **);
void           adrbk_clearrefs(AdrBk *);
AdrBk_Entry   *adrbk_newentry(void);
AdrBk_Entry   *copy_ae(AdrBk_Entry *);
int            adrbk_add(AdrBk *, a_c_arg_t, char *, char *, char *, char *,
			 char *, Tag, adrbk_cntr_t *, int *, int, int, int);
int            adrbk_append(AdrBk *, char *, char *, char *, 
			    char *, char *, Tag, adrbk_cntr_t *);
int            adrbk_delete(AdrBk *, a_c_arg_t, int, int, int, int);
int            adrbk_listdel(AdrBk *, a_c_arg_t, char *);
int            adrbk_listdel_all(AdrBk *, a_c_arg_t);
int            adrbk_nlistadd(AdrBk *, a_c_arg_t,adrbk_cntr_t *,int *,char **,int,int,int);
void           adrbk_check_validity(AdrBk *, long);
MAILSTREAM    *adrbk_handy_stream(char *);
void           adrbk_close(AdrBk *);
void           adrbk_partial_close(AdrBk *);
void           note_closed_adrbk_stream(MAILSTREAM *);
int            adrbk_write(AdrBk *, a_c_arg_t, adrbk_cntr_t *, int *, int, int);
void           free_ae(AdrBk_Entry **);
void           exp_free(EXPANDED_S *);
int            exp_is_expanded(EXPANDED_S *, a_c_arg_t);
int            exp_howmany_expanded(EXPANDED_S *);
int            exp_any_expanded(EXPANDED_S *);
adrbk_cntr_t   exp_get_next(EXPANDED_S **);
void           exp_set_expanded(EXPANDED_S *, a_c_arg_t);
void           exp_unset_expanded(EXPANDED_S *, a_c_arg_t);
int            adrbk_sort(AdrBk *, a_c_arg_t, adrbk_cntr_t *, int);
int            any_ab_open(void);
void           init_ab_if_needed(void);
int            init_addrbooks(OpenStatus, int, int, int);
void           addrbook_reset(void);
void           addrbook_redo_sorts(void);
AccessType     adrbk_access(PerAddrBook *);
void           trim_remote_adrbks(void);
void           completely_done_with_adrbks(void);
void           init_abook(PerAddrBook *, OpenStatus);
int            adrbk_check_all_validity_now(void);
int            adrbk_check_and_fix(PerAddrBook *, int, int, int);
int            adrbk_check_and_fix_all(int, int, int);
void           adrbk_maintenance(void);
char         **parse_addrlist(char *);
char          *skip_to_next_addr(char *);
void           add_forced_entries(AdrBk *);
int	     any_addressbook_in_remote_stream(MAILSTREAM *);

#endif /* PITH_ADRBKLIB_INCLUDED */