Finalize vcard editing:
[citadel.git] / webcit / vcard_edit.c
1 /*
2  * Copyright (c) 1996-2012 by the citadel.org team
3  *
4  * This program is open source software.  You can redistribute it and/or
5  * modify it under the terms of the GNU General Public License, version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12
13 #include "webcit.h"
14 #include "webserver.h"
15 #include "calendar.h"
16
17 CtxType CTX_VCARD = CTX_NONE;
18 CtxType CTX_VCARD_TYPE = CTX_NONE;
19 long VCEnumCounter = 0;
20
21 typedef enum _VCStrEnum {
22         FlatString,
23         StringCluster,
24         PhoneNumber,
25         EmailAddr,
26         Address,
27         Street,
28         Number,
29         AliasFor,
30         Base64BinaryAttachment,
31         UnKnown,
32         TerminateList
33 }VCStrEnum;
34 typedef struct vcField vcField;
35 struct vcField {
36         ConstStr STR;
37         VCStrEnum Type;
38         vcField *Sub;
39         long cval;
40         long parentCVal;
41         ConstStr Name;
42 };
43
44 vcField VCStr_Ns [] = {
45         {{HKEY("last")},   FlatString,    NULL, 0, 0, {HKEY("Last Name")}},
46         {{HKEY("first")},  FlatString,    NULL, 0, 0, {HKEY("First Name")}},
47         {{HKEY("middle")}, FlatString,    NULL, 0, 0, {HKEY("Middle Name")}},
48         {{HKEY("prefix")}, FlatString,    NULL, 0, 0, {HKEY("Prefix")}},
49         {{HKEY("suffix")}, FlatString,    NULL, 0, 0, {HKEY("Suffix")}},
50         {{HKEY("")},       TerminateList, NULL, 0, 0, {HKEY("")}}
51 };
52
53 vcField VCStr_Addrs [] = {
54         {{HKEY("POBox")},    Address,       NULL, 0, 0, {HKEY("PO box")}},
55         {{HKEY("extadr")},   Address,       NULL, 0, 0, {HKEY("Address")}},
56         {{HKEY("street")},   Address,       NULL, 0, 0, {HKEY("")}},
57         {{HKEY("city")},     Address,       NULL, 0, 0, {HKEY("City")}},
58         {{HKEY("state")},    Address,       NULL, 0, 0, {HKEY("State")}},
59         {{HKEY("zip")},      Address,       NULL, 0, 0, {HKEY("ZIP code")}},
60         {{HKEY("country")},  Address,       NULL, 0, 0, {HKEY("Country")}},
61         {{HKEY("")},         TerminateList, NULL, 0, 0, {HKEY("")}}
62 };
63
64 vcField VCStrE [] = {
65         {{HKEY("version")},         Number,                 NULL,        0, 0, {HKEY("")}},
66         {{HKEY("rev")},             Number,                 NULL,        0, 0, {HKEY("")}},
67         {{HKEY("label")},           FlatString,             NULL,        0, 0, {HKEY("")}},
68         {{HKEY("uid")},             FlatString,             NULL,        0, 0, {HKEY("")}},
69         {{HKEY("n")},               StringCluster,          VCStr_Ns,    0, 0, {HKEY("")}}, /* N is name, but only if there's no FN already there */
70         {{HKEY("fn")},              FlatString,             NULL,        0, 0, {HKEY("")}}, /* FN (full name) is a true 'display name' field */
71         {{HKEY("title")},           FlatString,             NULL,        0, 0, {HKEY("Title:")}},
72         {{HKEY("org")},             FlatString,             NULL,        0, 0, {HKEY("Organization:")}},/* organization */
73         {{HKEY("email")},           EmailAddr,              NULL,        0, 0, {HKEY("E-mail:")}},
74         {{HKEY("tel")},             PhoneNumber,            NULL,        0, 0, {HKEY("Telephone:")}},
75         {{HKEY("adr")},             StringCluster,          VCStr_Addrs, 0, 0, {HKEY("Address:")}},
76         {{HKEY("photo")},           Base64BinaryAttachment, NULL,        0, 0, {HKEY("Photo:")}},
77         {{HKEY("tel;home")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (home)")}},
78         {{HKEY("tel;work")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (work)")}},
79         {{HKEY("tel;fax")},         PhoneNumber,            NULL,        0, 0, {HKEY(" (fax)")}},
80         {{HKEY("tel;cell")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (cell)")}},
81         {{HKEY("email;internet")},  EmailAddr,              NULL,        0, 0, {HKEY("E-mail:")}},
82         {{HKEY("UNKNOWN")},         UnKnown,                NULL,        0, 0, {HKEY("")}},
83         {{HKEY("")},                TerminateList,          NULL,        0, 0, {HKEY("")}}
84 };
85
86 ConstStr VCStr [] = {
87         {HKEY("")},
88         {HKEY("n")}, /* N is name, but only if there's no FN already there */
89         {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
90         {HKEY("title")},   /* title */
91         {HKEY("org")},    /* organization */
92         {HKEY("email")},
93         {HKEY("tel")},
94         {HKEY("work")},
95         {HKEY("home")},
96         {HKEY("cell")},
97         {HKEY("adr")},
98         {HKEY("photo")},
99         {HKEY("version")},
100         {HKEY("rev")},
101         {HKEY("label")},
102         {HKEY("uid")}
103 };
104
105
106 HashList *DefineToToken = NULL;
107 HashList *VCTokenToDefine = NULL;
108 HashList *vcNames = NULL; /* todo: fill with the name strings */
109 vcField* vcfUnknown = NULL;
110
111 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
112 {
113         if (vf->Type == UnKnown) {
114                 vcfUnknown = vf;
115         }
116         RegisterTokenParamDefine(SKEY(name), vf->cval);
117         Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
118         Put(vcNames, LKEY(vf->cval), NewStrBufPlain(CKEY(vf->Name)), HFreeStrBuf);
119
120         syslog(LOG_DEBUG, "Token: %s -> %ld, %d", 
121                ChrPtr(name),
122                vf->cval, 
123                inTokenCount);
124
125 }
126
127 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer, long parentCVal)
128 {
129         int i = 0;
130         StrBuf *subStr = NewStrBuf();
131         while (vf[i].STR.len > 0) {
132                 FlushStrBuf(subStr);
133                 vf[i].cval = (*enumCounter) ++;
134                 vf[i].parentCVal = parentCVal;
135                 StrBufAppendBuf(subStr, BaseStr, 0);
136                 if (StrLength(subStr) > 0) {
137                         StrBufAppendBufPlain(subStr, HKEY("."), 0);
138                 }
139                 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
140                 if (layer == 0) {
141                         Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
142                 }
143                 switch (vf[i].Type) {
144                 case FlatString:
145                         break;
146                 case StringCluster:
147                 {
148                         autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1, vf[i].cval);
149                 }
150                 break;
151                 case PhoneNumber:
152                         break;
153                 case EmailAddr:
154                         break;
155                 case Street:
156                         break;
157                 case Number:
158                         break;
159                 case AliasFor:
160                         break;
161                 case Base64BinaryAttachment:
162                         break;
163                 case TerminateList:
164                         break;
165                 case Address:
166                         break;
167                 case UnKnown:
168                         break;
169                 }
170                 RegisterVCardToken(&vf[i], subStr, i);
171                 i++;
172         }
173         FreeStrBuf(&subStr);
174 }
175
176 int preeval_vcard_item(WCTemplateToken *Token)
177 {
178         WCTemplputParams TPP;
179         WCTemplputParams *TP;
180         int searchFieldNo;
181         StrBuf *Target = NULL;
182
183         memset(&TPP, 0, sizeof(WCTemplputParams));
184         TP = &TPP;
185         TP->Tokens = Token;
186         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
187         if (searchFieldNo >= VCEnumCounter) {
188                 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
189                                  "Invalid define");
190                 return 0;
191         }
192         return 1;
193 }
194
195 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
196 {
197         void *vItem;
198         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
199         HashList *vc = (HashList*) CTX(CTX_VCARD);
200         if (GetHash(vc, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
201                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
202         }
203 }
204
205 void tmpl_vcard_context_item(StrBuf *Target, WCTemplputParams *TP)
206 {
207         void *vItem;
208         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
209         HashList *vc = (HashList*) CTX(CTX_VCARD);
210
211         if (t == NULL) {
212                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
213                                  "Missing context");
214                 return;
215         }
216
217         if (GetHash(vc, LKEY(t->cval), &vItem) && (vItem != NULL)) {
218                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 0);
219         }
220         else {
221                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
222                                  "Doesn't have that key - did you miss to filter in advance?");
223         }
224 }
225 int preeval_vcard_name_str(WCTemplateToken *Token)
226 {
227         WCTemplputParams TPP;
228         WCTemplputParams *TP;
229         int searchFieldNo;
230         StrBuf *Target = NULL;
231
232         memset(&TPP, 0, sizeof(WCTemplputParams));
233         TP = &TPP;
234         TP->Tokens = Token;
235         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
236         if (searchFieldNo >= VCEnumCounter) {
237                 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
238                                  "Invalid define");
239                 return 0;
240         }
241         return 1;
242 }
243
244 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
245 {
246         void *vItem;
247         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
248         /* todo: get descriptive string for this vcard type */
249         if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
250                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
251         }
252         else {
253                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
254                                  "No i18n string for this.");
255                 return;
256         }
257 }
258
259 void tmpl_vcard_context_name_str(StrBuf *Target, WCTemplputParams *TP)
260 {
261         void *vItem;
262         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
263
264         if (t == NULL) {
265                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
266                                  "Missing context");
267                 return;
268         }
269         
270         if (GetHash(vcNames, LKEY(t->cval), &vItem) && (vItem != NULL)) {
271                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
272         }
273         else {
274                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
275                                  "No i18n string for this.");
276                 return;
277         }
278 }
279
280 int filter_VC_ByType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
281 {
282         long searchType;
283         long type = 0;
284         void *v;
285         int rc = 0;
286         vcField *vf = (vcField*) Context;
287
288         memcpy(&type, key, sizeof(long));
289         searchType = GetTemplateTokenNumber(Target, TP, IT_ADDT_PARAM(0), 0);
290         
291         if (vf->Type == searchType) {
292                 HashList *vc = (HashList*) CTX(CTX_VCARD);
293                 if (GetHash(vc, LKEY(vf->cval), &v) && v != NULL)
294                         return 1;
295         }
296         return rc;
297 }
298
299
300
301
302 HashList *getContextVcard(StrBuf *Target, WCTemplputParams *TP)
303 {
304         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
305         HashList *vc = (HashList*) CTX(CTX_VCARD);
306
307         if ((vf == NULL) || (vc == NULL)) {
308                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
309                                  "Need VCard and Vcard type in context");
310                 
311                 return NULL;
312         }
313         return vc;
314 }
315
316 int filter_VC_ByContextType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
317 {
318         long searchType;
319         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
320
321         memcpy(&searchType, key, sizeof(long));
322         
323         if (vf->cval == searchType) {
324                 return 1;
325         }
326         else {
327                 return 0;
328         }
329 }
330
331
332 int conditional_VC_Havetype(StrBuf *Target, WCTemplputParams *TP)
333 {
334         HashList *vc = (HashList*) CTX(CTX_VCARD);
335         long HaveFieldType = GetTemplateTokenNumber(Target, TP, 2, 0);
336         int rc = 0;     
337         void *vVCitem;
338         const char *Key;
339         long len;
340         HashPos *it = GetNewHashPos(vc, 0);
341         while (GetNextHashPos(vc, it, &len, &Key, &vVCitem) && 
342                (vVCitem != NULL)) 
343         {
344                 void *vvcField;
345                 long type = 0;
346                 memcpy(&type, Key, sizeof(long));
347                 if (GetHash(DefineToToken, LKEY(type), &vvcField) &&
348                     (vvcField != NULL))
349                 {
350                         vcField *t = (vcField*) vvcField;
351                         if (t && t->Type == HaveFieldType) {
352                                 rc = 1;
353                                 break;
354                         }
355                 }
356         }
357         DeleteHashPos(&it);
358         return rc;
359 }
360
361 /*
362  * Record compare function for sorting address book indices
363  */
364 int abcmp(const void *ab1, const void *ab2) {
365         return(strcasecmp(
366                 (((const addrbookent *)ab1)->ab_name),
367                 (((const addrbookent *)ab2)->ab_name)
368         ));
369 }
370
371
372 /*
373  * Helper function for do_addrbook_view()
374  * Converts a name into a three-letter tab label
375  */
376 void nametab(char *tabbuf, long len, char *name) {
377         stresc(tabbuf, len, name, 0, 0);
378         tabbuf[0] = toupper(tabbuf[0]);
379         tabbuf[1] = tolower(tabbuf[1]);
380         tabbuf[2] = tolower(tabbuf[2]);
381         tabbuf[3] = 0;
382 }
383
384
385 /*
386  * If it's an old "Firstname Lastname" style record, try to convert it.
387  */
388 void lastfirst_firstlast(char *namebuf) {
389         char firstname[SIZ];
390         char lastname[SIZ];
391         int i;
392
393         if (namebuf == NULL) return;
394         if (strchr(namebuf, ';') != NULL) return;
395
396         i = num_tokens(namebuf, ' ');
397         if (i < 2) return;
398
399         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
400         remove_token(namebuf, i-1, ' ');
401         strcpy(firstname, namebuf);
402         sprintf(namebuf, "%s; %s", lastname, firstname);
403 }
404
405
406
407 wc_mime_attachment *load_vcard(message_summary *Msg) 
408 {
409         HashPos  *it;
410         StrBuf *FoundCharset = NewStrBuf();
411         StrBuf *Error;
412         void *vMime;
413         const char *Key;
414         long len;
415         wc_mime_attachment *Mime;
416         wc_mime_attachment *VCMime = NULL;
417
418         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
419         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
420         Msg->MsgBody->msgnum = Msg->msgnum;
421
422         load_message(Msg, FoundCharset, &Error);
423
424         FreeStrBuf(&FoundCharset);
425         /* look up the vcard... */
426         it = GetNewHashPos(Msg->AllAttach, 0);
427         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
428                (vMime != NULL)) 
429         {
430                 Mime = (wc_mime_attachment*) vMime;
431                 if ((strcmp(ChrPtr(Mime->ContentType),
432                            "text/x-vcard") == 0) ||
433                     (strcmp(ChrPtr(Mime->ContentType),
434                             "text/vcard") == 0))
435                 {
436                         VCMime = Mime;
437                         break;
438                 }
439         }
440         DeleteHashPos(&it);
441         if (VCMime == NULL)
442                 return NULL;
443
444         if (VCMime->Data == NULL)
445                 MimeLoadData(VCMime);
446         return VCMime;
447 }
448
449 /*
450  * fetch the display name off a vCard
451  */
452 void fetch_ab_name(message_summary *Msg, char **namebuf) {
453         long len;
454         int i;
455         wc_mime_attachment *VCMime = NULL;
456
457         if (namebuf == NULL) return;
458
459         VCMime = load_vcard(Msg);
460         if (VCMime == NULL)
461                 return;
462
463         /* Grab the name off the card */
464         display_vcard(WC->WBuf, VCMime, 0, 0, namebuf, Msg->msgnum);
465
466         if (*namebuf != NULL) {
467                 lastfirst_firstlast(*namebuf);
468                 striplt(*namebuf);
469                 len = strlen(*namebuf);
470                 for (i=0; i<len; ++i) {
471                         if ((*namebuf)[i] != ';') return;
472                 }
473                 free (*namebuf);
474                 (*namebuf) = strdup(_("(no name)"));
475         }
476         else {
477                 (*namebuf) = strdup(_("(no name)"));
478         }
479 }
480
481
482
483 /*
484  * Turn a vCard "n" (name) field into something displayable.
485  */
486 void vcard_n_prettyize(char *name)
487 {
488         char *original_name;
489         int i, j, len;
490
491         original_name = strdup(name);
492         len = strlen(original_name);
493         for (i=0; i<5; ++i) {
494                 if (len > 0) {
495                         if (original_name[len-1] == ' ') {
496                                 original_name[--len] = 0;
497                         }
498                         if (original_name[len-1] == ';') {
499                                 original_name[--len] = 0;
500                         }
501                 }
502         }
503         strcpy(name, "");
504         j=0;
505         for (i=0; i<len; ++i) {
506                 if (original_name[i] == ';') {
507                         name[j++] = ',';
508                         name[j++] = ' ';                        
509                 }
510                 else {
511                         name[j++] = original_name[i];
512                 }
513         }
514         name[j] = '\0';
515         free(original_name);
516 }
517
518
519
520
521 /*
522  * preparse a vcard name
523  * display_vcard() calls this after parsing the textual vCard into
524  * our 'struct vCard' data object.
525  * This gets called instead of display_parsed_vcard() if we are only looking
526  * to extract the person's name instead of displaying the card.
527  */
528 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
529         char *name;
530         char *prop;
531         char buf[SIZ];
532         int j, n, len;
533         int is_qp = 0;
534         int is_b64 = 0;
535
536         *storename = NULL;
537
538         name = vcard_get_prop(v, "n", 1, 0, 0);
539         if (name != NULL) {
540                 len = strlen(name);
541                 prop = vcard_get_prop(v, "n", 1, 0, 1);
542                 n = num_tokens(prop, ';');
543
544                 for (j=0; j<n; ++j) {
545                         extract_token(buf, prop, j, ';', sizeof buf);
546                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
547                                 is_qp = 1;
548                         }
549                         if (!strcasecmp(buf, "encoding=base64")) {
550                                 is_b64 = 1;
551                         }
552                 }
553                 if (is_qp) {
554                         /* %ff can become 6 bytes in utf8  */
555                         *storename = malloc(len * 2 + 3); 
556                         j = CtdlDecodeQuotedPrintable(
557                                 *storename, name,
558                                 len);
559                         (*storename)[j] = 0;
560                 }
561                 else if (is_b64) {
562                         /* ff will become one byte.. */
563                         *storename = malloc(len + 50);
564                         CtdlDecodeBase64(
565                                 *storename, name,
566                                 len);
567                 }
568                 else {
569                         size_t len;
570
571                         len = strlen (name);
572                         
573                         *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
574                         memcpy(*storename, name, len + 1);
575                 }
576                 /* vcard_n_prettyize(storename); */
577         }
578
579 }
580
581
582
583
584 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
585 {
586         /* if we have some untagged QP, detect it here. */
587         if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
588                 StrBuf *b;
589                 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
590                 b = ThisFieldStr;
591                 ThisFieldStr = Swap; 
592                 Swap = b;
593                 FlushStrBuf(Swap);
594         }
595         Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
596 }
597 /*
598  * html print a vcard
599  * display_vcard() calls this after parsing the textual vCard into
600  * our 'struct vCard' data object.
601  *
602  * Set 'full' to nonzero to display the full card, otherwise it will only
603  * show a summary line.
604  *
605  * This code is a bit ugly, so perhaps an explanation is due: we do this
606  * in two passes through the vCard fields.  On the first pass, we process
607  * fields we understand, and then render them in a pretty fashion at the
608  * end.  Then we make a second pass, outputting all the fields we don't
609  * understand in a simple two-column name/value format.
610  * v            the vCard to parse
611  * msgnum       Citadel message pointer
612  */
613 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
614 {
615         StrBuf *Val = NULL;
616         StrBuf *Swap = NULL;
617         int i, j, k;
618         char buf[SIZ];
619         int is_qp = 0;
620         int is_b64 = 0;
621         int ntokens, len;
622         StrBuf *thisname = NULL;
623         char firsttoken[SIZ];
624         StrBuf *thisVCToken;
625         void *vField = NULL;
626
627         Swap = NewStrBuf ();
628         thisname = NewStrBuf();
629         thisVCToken = NewStrBufPlain(NULL, 63);
630         for (i=0; i<(v->numprops); ++i) {
631                 FlushStrBuf(thisVCToken);
632                 is_qp = 0;
633                 is_b64 = 0;
634                 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
635                 StrBufPlain(thisname, v->prop[i].name, -1);
636                 StrBufLowerCase(thisname);
637                 
638                 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
639                 ntokens = num_tokens(ChrPtr(thisname), ';');
640                 for (j=0, k=0; j < ntokens && k < 10; ++j) {
641                         ///int evc[10];
642                         
643                         len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
644                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
645                                 is_qp = 1;
646 /*                              remove_token(thisname, j, ';');*/
647                         }
648                         else if (!strcasecmp(buf, "encoding=base64")) {
649                                 is_b64 = 1;
650 /*                              remove_token(thisname, j, ';');*/
651                         }
652                         else{
653                                 if (StrLength(thisVCToken) > 0) {
654                                         StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
655                                 }
656                                 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
657                                 /*
658                                 if (GetHash(VCToEnum, buf, len, &V))
659                                 {
660                                         evc[k] = (int) V;
661
662                                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
663
664                                         syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
665                                         k++;
666                                 }
667 */
668
669                         }
670                 }
671
672                 vField = NULL;  
673                 if ((StrLength(thisVCToken) > 0) &&
674                     GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) && 
675                     (vField != NULL)) {
676                         vcField *thisField = (vcField *)vField;
677                         StrBuf *ThisFieldStr = NULL;
678                         syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
679                         switch (thisField->Type) {
680                         case StringCluster: {
681                                 int j = 0;
682                                 const char *Pos = NULL;
683                                 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
684                                 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
685                                 while (thisField->Sub[j].STR.len > 0) {
686                                         StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
687                                         ThisFieldStr = NewStrBufDup(Buf);
688                                         
689                                         PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
690                                         j++;
691                                 }
692                                 FreeStrBuf(&thisArray);
693                                 FreeStrBuf(&Buf);
694                         }
695                                 break;
696                         case Address:
697                         case FlatString:
698                         case PhoneNumber:
699                         case EmailAddr:
700                         case Street:
701                         case Number:
702                         case AliasFor:
703                                 /* copy over the payload into a StrBuf */
704                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
705                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
706
707                                 break;
708                         case Base64BinaryAttachment:
709                         case TerminateList:
710                         case UnKnown:
711                                 break;
712                         }
713
714                 }
715                 else if (StrLength(thisVCToken) > 0) {
716                         /* Add it to the UNKNOWN field... */
717                         void *pv = NULL;
718                         StrBuf *oldVal;
719                         GetHash(VC, IKEY(vcfUnknown->cval), &pv);
720                         oldVal = (StrBuf*) pv;
721                         if (oldVal == NULL) {
722                                 oldVal = NewStrBuf();
723                                 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
724                         }
725                         else {
726                                 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
727                         }
728
729                         StrBufAppendBuf(oldVal, thisVCToken, 0);
730                         StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
731                         StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
732                         continue;
733                 }
734
735                 /* copy over the payload into a StrBuf */
736                 Val = NewStrBufPlain(v->prop[i].value, -1);
737                         
738                 /* if we have some untagged QP, detect it here. */
739                 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
740                         StrBuf *b;
741                         StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
742                         b = Val;
743                         Val = Swap; 
744                         Swap = b;
745                         FlushStrBuf(Swap);
746                 }
747                 else if (is_b64) {
748                         StrBufDecodeBase64(Val);
749                 }
750 #if 0
751                 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
752                         firsttoken,
753                        ChrPtr(thisname),
754                         ChrPtr(Val),
755                         v->prop[i].value);
756                 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
757                 {
758                         eVC evc = (eVC) V;
759                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
760                         syslog(LOG_DEBUG, "[%ul]\n", evc);
761                         Val = NULL;
762                 }
763                 else
764                         syslog(LOG_DEBUG, "[]\n");
765 /*
766 TODO: check for layer II
767                 else 
768                 {
769                         long max = num_tokens(thisname, ';');
770                         firsttoken[len] = '_';
771
772                         for (j = 0; j < max; j++) {
773 //                      firsttoken[len]
774
775                                 extract_token(buf, thisname, j, ';', sizeof (buf));
776                                         if (!strcasecmp(buf, "tel"))
777                                                 strcat(phone, "");
778                                         else if (!strcasecmp(buf, "work"))
779                                                 strcat(phone, _(" (work)"));
780                                         else if (!strcasecmp(buf, "home"))
781                                                 strcat(phone, _(" (home)"));
782                                         else if (!strcasecmp(buf, "cell"))
783                                                 strcat(phone, _(" (cell)"));
784                                         else {
785                                                 strcat(phone, " (");
786                                                 strcat(phone, buf);
787                                                 strcat(phone, ")");
788                                         }
789                                 }
790                         }
791
792                 }
793 */
794 #endif  
795                 FreeStrBuf(&Val);
796         }
797         FreeStrBuf(&thisname);
798         FreeStrBuf(&Swap);
799         FreeStrBuf(&thisVCToken);
800 }
801
802 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
803 {
804         HashList *VC = CTX(CTX_VCARD);
805         int evc;
806         void *vStr;
807
808         evc = GetTemplateTokenNumber(Target, TP, 0, -1);
809         if (evc != -1)
810         {
811                 if (GetHash(VC, IKEY(evc), &vStr))
812                 {
813                         StrBufAppendTemplate(Target, TP,
814                                              (StrBuf*) vStr,
815                                              1);
816                 }
817         }
818         
819 }
820
821 void display_one_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
822 {
823         HashList *VC;   WCTemplputParams SubTP;
824
825         memset(&SubTP, 0, sizeof(WCTemplputParams));    
826
827
828         VC = NewHash(0, lFlathash);
829         parse_vcard(Target, v, VC, Mime);
830
831         {
832                 WCTemplputParams *TP = NULL;
833                 WCTemplputParams SubTP;
834                 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
835
836                 DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
837                 UnStackContext(&SubTP);
838         }
839         DeleteHash(&VC);
840 }
841
842
843
844 /*
845  * Display a textual vCard
846  * (Converts to a vCard object and then calls the actual display function)
847  * Set 'full' to nonzero to display the whole card instead of a one-liner.
848  * Or, if "storename" is non-NULL, just store the person's name in that
849  * buffer instead of displaying the card at all.
850  *
851  * vcard_source the buffer containing the vcard text
852  * alpha        Display only if name begins with this letter of the alphabet
853  * full         Display the full vCard (otherwise just the display name)
854  * storename    If not NULL, also store the display name here
855  * msgnum       Citadel message pointer
856  */
857 void display_vcard(StrBuf *Target, 
858                    wc_mime_attachment *Mime, 
859                    char alpha, 
860                    int full, 
861                    char **storename, 
862                    long msgnum) 
863 {
864         struct vCard *v;
865         char *name;
866         StrBuf *Buf;
867         StrBuf *Buf2;
868         char this_alpha = 0;
869
870         v = VCardLoad(Mime->Data);
871
872         if (v == NULL) return;
873
874         name = vcard_get_prop(v, "n", 1, 0, 0);
875         if (name != NULL) {
876                 Buf = NewStrBufPlain(name, -1);
877                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
878                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
879                 this_alpha = ChrPtr(Buf)[0];
880                 FreeStrBuf(&Buf);
881                 FreeStrBuf(&Buf2);
882         }
883
884         if (storename != NULL) {
885                 fetchname_parsed_vcard(v, storename);
886         }
887         else if ((alpha == 0) || 
888                  ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) || 
889                  ((!isalpha(alpha)) && (!isalpha(this_alpha)))
890                 ) 
891         {
892                 display_one_vcard (Target, v, full, Mime);
893         }
894
895         vcard_free(v);
896 }
897
898
899
900 /*
901  * Render the address book using info we gathered during the scan
902  *
903  * addrbook     the addressbook to render
904  * num_ab       the number of the addressbook
905  */
906 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
907         int i = 0;
908         int displayed = 0;
909         int bg = 0;
910         static int NAMESPERPAGE = 60;
911         int num_pages = 0;
912         int tabfirst = 0;
913         char tabfirst_label[64];
914         int tablast = 0;
915         char tablast_label[64];
916         char this_tablabel[64];
917         int page = 0;
918         char **tablabels;
919
920         if (num_ab == 0) {
921                 wc_printf("<br><br><br><div align=\"center\"><i>");
922                 wc_printf(_("This address book is empty."));
923                 wc_printf("</i></div>\n");
924                 return;
925         }
926
927         if (num_ab > 1) {
928                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
929         }
930
931         num_pages = (num_ab / NAMESPERPAGE) + 1;
932
933         tablabels = malloc(num_pages * sizeof (char *));
934         if (tablabels == NULL) {
935                 wc_printf("<br><br><br><div align=\"center\"><i>");
936                 wc_printf(_("An internal error has occurred."));
937                 wc_printf("</i></div>\n");
938                 return;
939         }
940
941         for (i=0; i<num_pages; ++i) {
942                 tabfirst = i * NAMESPERPAGE;
943                 tablast = tabfirst + NAMESPERPAGE - 1;
944                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
945                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
946                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
947                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
948                 tablabels[i] = strdup(this_tablabel);
949         }
950
951         tabbed_dialog(num_pages, tablabels);
952         page = (-1);
953
954         for (i=0; i<num_ab; ++i) {
955
956                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
957                         page = (i / NAMESPERPAGE);
958                         if (page > 0) {
959                                 wc_printf("</tr></table>\n");
960                                 end_tab(page-1, num_pages);
961                         }
962                         begin_tab(page, num_pages);
963                         wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
964                         displayed = 0;
965                 }
966
967                 if ((displayed % 4) == 0) {
968                         if (displayed > 0) {
969                                 wc_printf("</tr>\n");
970                         }
971                         bg = 1 - bg;
972                         wc_printf("<tr bgcolor=\"#%s\">",
973                                 (bg ? "dddddd" : "ffffff")
974                         );
975                 }
976         
977                 wc_printf("<td>");
978
979                 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
980                         addrbook[i].ab_msgnum);
981                 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
982                 vcard_n_prettyize(addrbook[i].ab_name);
983                 escputs(addrbook[i].ab_name);
984                 wc_printf("</a></td>\n");
985                 ++displayed;
986         }
987
988         /* Placeholders for empty columns at end */
989         if ((num_ab % 4) != 0) {
990                 for (i=0; i<(4-(num_ab % 4)); ++i) {
991                         wc_printf("<td>&nbsp;</td>");
992                 }
993         }
994
995         wc_printf("</tr></table>\n");
996         end_tab((num_pages-1), num_pages);
997
998         begin_tab(num_pages, num_pages);
999         /* FIXME there ought to be something here */
1000         end_tab(num_pages, num_pages);
1001
1002         for (i=0; i<num_pages; ++i) {
1003                 free(tablabels[i]);
1004         }
1005         free(tablabels);
1006 }
1007
1008
1009
1010
1011 /*
1012  * Edit the vCard component of a MIME message.  
1013  * Supply the message number
1014  * and MIME part number to fetch.  Or, specify -1 for the message number
1015  * to start with a blank card.
1016  */
1017 void do_edit_vcard(long msgnum, char *partnum, 
1018                    message_summary *VCMsg,
1019                    wc_mime_attachment *VCAtt,
1020                    const char *return_to, 
1021                    const char *force_room) {
1022         HashList *VC;   WCTemplputParams SubTP;
1023         wcsession *WCC = WC;
1024         message_summary *Msg = NULL;
1025         wc_mime_attachment *VCMime = NULL;
1026         struct vCard *v;
1027         char whatuser[256];
1028         VC = NewHash(0, lFlathash);
1029
1030         /* Display the form */
1031         output_headers(1, 1, 1, 0, 0, 0);
1032
1033         safestrncpy(whatuser, "", sizeof whatuser);
1034
1035         if ((msgnum >= 0) || 
1036             ((VCMsg != NULL) && (VCAtt != NULL)))
1037         {
1038                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1039
1040                         Msg = (message_summary *) malloc(sizeof(message_summary));
1041                         memset(Msg, 0, sizeof(message_summary));
1042                         Msg->msgnum = msgnum;
1043                         VCMime = load_vcard(Msg);
1044                         if (VCMime == NULL) {
1045                                 convenience_page("770000", _("Error"), "");///TODO: important message
1046                                 DestroyMessageSummary(Msg);
1047                                 return;
1048                         }
1049                 
1050                         v = VCardLoad(VCMime->Data);
1051                 }
1052                 else {
1053                         v = VCardLoad(VCAtt->Data);
1054                 }
1055
1056                 parse_vcard(WCC->WBuf, v, VC, NULL);
1057         
1058         
1059                 vcard_free(v);
1060         }
1061
1062         memset(&SubTP, 0, sizeof(WCTemplputParams));    
1063
1064         {
1065                 WCTemplputParams *TP = NULL;
1066                 WCTemplputParams SubTP;
1067                 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
1068
1069                 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
1070                 UnStackContext(&SubTP);
1071         }
1072         DeleteHash(&VC);
1073
1074
1075         wDumpContent(1);
1076         if (Msg != NULL) {
1077                 DestroyMessageSummary(Msg);
1078         }
1079 }
1080
1081
1082 /*
1083  *  commit the edits to the citadel server
1084  */
1085 void edit_vcard(void) {
1086         long msgnum;
1087         char *partnum;
1088
1089         msgnum = lbstr("msgnum");
1090         partnum = bstr("partnum");
1091         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1092 }
1093
1094
1095
1096 /*
1097  *  parse edited vcard from the browser
1098  */
1099 void submit_vcard(void) {
1100         struct vCard *v;
1101         char *serialized_vcard;
1102         StrBuf *Buf;
1103         const StrBuf *ForceRoom;
1104         HashList* postVcard;
1105         HashPos *it, *itSub;
1106         const char *Key;
1107         long len;
1108         void *pv;
1109         StrBuf *SubStr;
1110         const StrBuf *s;
1111         const char *Pos = NULL;
1112
1113         if (!havebstr("ok_button")) { 
1114                 readloop(readnew, eUseDefault);
1115                 return;
1116         }
1117
1118         if (havebstr("force_room")) {
1119                 ForceRoom = sbstr("force_room");
1120                 if (gotoroom(ForceRoom) != 200) {
1121                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1122                         AppendImportantMessage(HKEY(": "));
1123                         AppendImportantMessage(SKEY(ForceRoom));
1124                         AppendImportantMessage(HKEY("; "));
1125                         AppendImportantMessage(_("Aborting."), -1);
1126
1127                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1128                                 select_user_to_edit(NULL);
1129                         }
1130                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1131                                 do_welcome();
1132                         }
1133                         else if (!IsEmptyStr(bstr("return_to"))) {
1134                                 http_redirect(bstr("return_to"));
1135                         }
1136                         else {
1137                                 readloop(readnew, eUseDefault);
1138                         }
1139                         return;
1140                 }
1141         }
1142
1143         postVcard = getSubStruct(HKEY("VC"));
1144         if (postVcard == NULL) {
1145                 AppendImportantMessage(_("An error has occurred."), -1);
1146                 edit_vcard();
1147                 return;//// more details
1148         }
1149         
1150         Buf = NewStrBuf();
1151         serv_write(HKEY("ENT0 1|||4\n"));
1152         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1153         {
1154                 edit_vcard();
1155                 return;
1156         }
1157         
1158         /* Make a vCard structure out of the data supplied in the form */
1159         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1160                      bstr("extrafields")
1161         );
1162         v = VCardLoad(Buf);     /* Start with the extra fields */
1163         if (v == NULL) {
1164                 AppendImportantMessage(_("An error has occurred."), -1);
1165                 edit_vcard();
1166                 FreeStrBuf(&Buf);
1167                 return;
1168         }
1169
1170         SubStr = NewStrBuf();
1171         it = GetNewHashPos(DefineToToken, 0);
1172         while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) && 
1173                (pv != NULL)) 
1174         {
1175                 char buf[32];
1176                 long blen;
1177                 vcField *t = (vcField*) pv;
1178
1179                 if (t->Sub != NULL){
1180                         vcField *Sub;
1181                         FlushStrBuf(SubStr);
1182                         itSub = GetNewHashPos(DefineToToken, 0);
1183                         while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) && 
1184                                (pv != NULL)) 
1185                         {
1186                                 Sub = (vcField*) pv;
1187                                 if (Sub->parentCVal == t->cval) {
1188                                         if (StrLength(SubStr) > 0)
1189                                                 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
1190
1191
1192
1193                                         blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
1194                                         s = SSubBstr(postVcard, buf, blen);
1195                         
1196                                         if ((s != NULL) && (StrLength(s) > 0)) {
1197                                                 /// todo: utf8 qp
1198                                                 StrBufAppendBuf(SubStr, s, 0);
1199                                         }
1200                                 }
1201                         }
1202                         if (StrLength(SubStr) > 0) {
1203                                 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
1204                         }
1205                         DeleteHashPos(&itSub);
1206                 }
1207                 else if (t->parentCVal == 0) {
1208                         blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
1209                         s = SSubBstr(postVcard, buf, blen);
1210                         
1211                         if ((s != NULL) && (StrLength(s) > 0)) {
1212                                 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
1213                         }
1214                         /*
1215                     (vvcField != NULL))
1216                 {
1217                         vcField *t = (vcField*) vvcField;
1218                         if (t->layer == 0) switch (t->Type) {
1219                                 break;
1220                         case StringCluster:
1221                         {
1222                                 
1223                                 i++;
1224                                 continue;
1225                         }
1226                         break;
1227                                 break;
1228                         case EmailAddr:
1229                                 break;
1230                         case Street:
1231                                 break;
1232                         case FlatString:
1233                         case PhoneNumber:
1234                         case Number:
1235                                 break;
1236                         case AliasFor:
1237                                 break;
1238                         case Base64BinaryAttachment:
1239                                 break;
1240                         case TerminateList:
1241                                 break;
1242                         case Address:
1243                                 break;
1244
1245                         }
1246 */
1247                 }
1248         }
1249         DeleteHashPos(&it);
1250
1251         s = sbstr("other_inetemail");
1252         if (StrLength(s) > 0) {
1253                 FlushStrBuf(SubStr);
1254                 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
1255                         if (StrLength(SubStr) > 0) {
1256                                 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
1257                         }
1258                 }
1259         }
1260
1261         FreeStrBuf(&SubStr);
1262
1263
1264         serialized_vcard = vcard_serialize(v);
1265         vcard_free(v);
1266         if (serialized_vcard == NULL) {
1267                 AppendImportantMessage(_("An error has occurred."), -1);
1268                 edit_vcard();
1269                 FreeStrBuf(&Buf);
1270                 return;
1271         }
1272
1273         printf("%s", serialized_vcard);
1274         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1275         serv_write(HKEY("\n"));
1276         serv_printf("%s\r\n", serialized_vcard);
1277         serv_write(HKEY("000\n"));
1278         free(serialized_vcard);
1279
1280         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1281                 select_user_to_edit(NULL);
1282         }
1283         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1284                 do_welcome();
1285         }
1286         else if (!IsEmptyStr(bstr("return_to"))) {
1287                 http_redirect(bstr("return_to"));
1288         }
1289         else {
1290                 readloop(readnew, eUseDefault);
1291         }
1292         FreeStrBuf(&Buf);
1293 }
1294
1295
1296
1297 /*
1298  * Extract an embedded photo from a vCard for display on the client
1299  */
1300 void display_vcard_photo_img(void)
1301 {
1302         long msgnum = 0L;
1303         StrBuf *vcard;
1304         struct vCard *v;
1305         char *photosrc;
1306         const char *contentType;
1307         wcsession *WCC = WC;
1308
1309         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1310         
1311         vcard = load_mimepart(msgnum,"1");
1312         v = VCardLoad(vcard);
1313         
1314         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1315         FlushStrBuf(WCC->WBuf);
1316         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1317         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1318                 FlushStrBuf(WCC->WBuf);
1319                 
1320                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1321                 output_headers(0, 0, 0, 0, 0, 0);
1322                 hprintf("Content-Type: text/plain\r\n");
1323                 begin_burst();
1324                 wc_printf(_("Could Not decode vcard photo\n"));
1325                 end_burst();
1326                 return;
1327         }
1328         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1329         http_transmit_thing(contentType, 0);
1330         free(v);
1331         free(photosrc);
1332 }
1333
1334 typedef struct _vcardview_struct {
1335         long is_singlecard;
1336         addrbookent *addrbook;
1337         long num_ab;
1338
1339 } vcardview_struct;
1340
1341 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
1342                                  void **ViewSpecific, 
1343                                  long oper, 
1344                                  char *cmd, 
1345                                  long len,
1346                                  char *filter,
1347                                  long flen)
1348 {
1349         vcardview_struct *VS;
1350
1351         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1352         memset(VS, 0, sizeof(vcardview_struct));
1353         *ViewSpecific = (void*)VS;
1354
1355         VS->is_singlecard = ibstr("is_singlecard");
1356         if (VS->is_singlecard != 1) {
1357                 if (oper == do_search) {
1358                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1359                 }
1360                 else {
1361                         strcpy(cmd, "MSGS ALL");
1362                 }
1363                 Stat->maxmsgs = 9999999;
1364         }
1365         return 200;
1366 }
1367
1368 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
1369                             void **ViewSpecific, 
1370                             message_summary* Msg, 
1371                             int is_new, 
1372                             int i)
1373 {
1374         vcardview_struct *VS;
1375         char *ab_name;
1376
1377         VS = (vcardview_struct*) *ViewSpecific;
1378
1379         ab_name = NULL;
1380         fetch_ab_name(Msg, &ab_name);
1381         if (ab_name == NULL) 
1382                 return 0;
1383         ++VS->num_ab;
1384         VS->addrbook = realloc(VS->addrbook,
1385                                (sizeof(addrbookent) * VS->num_ab) );
1386         safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1387                     sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1388         VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1389         free(ab_name);
1390         return 0;
1391 }
1392
1393
1394 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1395 {
1396         const StrBuf *Mime;
1397         vcardview_struct *VS;
1398
1399         VS = (vcardview_struct*) *ViewSpecific;
1400         if (VS->is_singlecard)
1401                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1402         else
1403                 do_addrbook_view(VS->addrbook, VS->num_ab);     /* Render the address book */
1404         return 0;
1405 }
1406
1407 int vcard_Cleanup(void **ViewSpecific)
1408 {
1409         vcardview_struct *VS;
1410
1411         VS = (vcardview_struct*) *ViewSpecific;
1412         wDumpContent(1);
1413         if ((VS != NULL) && 
1414             (VS->addrbook != NULL))
1415                 free(VS->addrbook);
1416         if (VS != NULL) 
1417                 free(VS);
1418         return 0;
1419 }
1420
1421 void 
1422 ServerStartModule_VCARD
1423 (void)
1424 {
1425         ///VCToEnum = NewHash(0, NULL);
1426
1427 }
1428
1429 void 
1430 ServerShutdownModule_VCARD
1431 (void)
1432 {
1433         DeleteHash(&DefineToToken);
1434         DeleteHash(&vcNames);
1435         DeleteHash(&VCTokenToDefine);
1436         /// DeleteHash(&VCToEnum);
1437 }
1438
1439 void 
1440 InitModule_VCARD
1441 (void)
1442 {
1443         RegisterCTX(CTX_VCARD);
1444         RegisterCTX(CTX_VCARD_TYPE);
1445         RegisterReadLoopHandlerset(
1446                 VIEW_ADDRESSBOOK,
1447                 vcard_GetParamsGetServerCall,
1448                 NULL,
1449                 NULL,
1450                 NULL, 
1451                 vcard_LoadMsgFromServer,
1452                 vcard_RenderView_or_Tail,
1453                 vcard_Cleanup);
1454         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1455         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1456         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1457 /*
1458         Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1459         Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1460         Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1461         Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1462         Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1463         Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1464         Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1465         Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1466         Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1467         Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1468         Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1469         Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1470         Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1471         Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1472 */
1473 /*
1474         RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1475
1476         REGISTERTokenParamDefine(VC_n);
1477         REGISTERTokenParamDefine(VC_fn);
1478         REGISTERTokenParamDefine(VC_title);
1479         REGISTERTokenParamDefine(VC_org);
1480         REGISTERTokenParamDefine(VC_email);
1481         REGISTERTokenParamDefine(VC_tel);
1482         REGISTERTokenParamDefine(VC_work);
1483         REGISTERTokenParamDefine(VC_home);
1484         REGISTERTokenParamDefine(VC_cell);
1485         REGISTERTokenParamDefine(VC_adr);
1486         REGISTERTokenParamDefine(VC_photo);
1487         REGISTERTokenParamDefine(VC_version);
1488         REGISTERTokenParamDefine(VC_rev);
1489         REGISTERTokenParamDefine(VC_label);
1490 */
1491
1492         {
1493                 StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1494                 DefineToToken   = NewHash(1, lFlathash);
1495                 vcNames         = NewHash(1, lFlathash);
1496                 VCTokenToDefine = NewHash(1, NULL);
1497                 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1498                 FreeStrBuf(&Prefix);
1499         }
1500         RegisterCTX(CTX_VCARD);
1501         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1502         RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1503         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1504         RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1505         REGISTERTokenParamDefine(FlatString);
1506         REGISTERTokenParamDefine(StringCluster);
1507         REGISTERTokenParamDefine(PhoneNumber);
1508         REGISTERTokenParamDefine(EmailAddr);
1509         REGISTERTokenParamDefine(Street);
1510         REGISTERTokenParamDefine(Number);
1511         REGISTERTokenParamDefine(AliasFor);
1512         REGISTERTokenParamDefine(Base64BinaryAttachment);
1513         REGISTERTokenParamDefine(TerminateList);
1514         REGISTERTokenParamDefine(Address);
1515
1516         RegisterConditional("VC:HAVE:TYPE",      1, conditional_VC_Havetype, CTX_VCARD);
1517         RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1518         RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1519 }
1520