8345d003fa9524cac5ad58f1d6a11cb9c5c2d1ba
[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         Street,
27         Number,
28         AliasFor,
29         Base64BinaryAttachment,
30         TerminateList
31 }VCStrEnum;
32 typedef struct vcField vcField;
33 struct vcField {
34         ConstStr STR;
35         VCStrEnum Type;
36         vcField *Sub;
37         long cval;
38         ConstStr Name;
39 };
40
41 vcField VCStr_Ns [] = {
42         {{HKEY("last")},   FlatString,    NULL, 0, {HKEY("Last Name")}},
43         {{HKEY("first")},  FlatString,    NULL, 0, {HKEY("First Name")}},
44         {{HKEY("middle")}, FlatString,    NULL, 0, {HKEY("Middle Name")}},
45         {{HKEY("prefix")}, FlatString,    NULL, 0, {HKEY("Prefix")}},
46         {{HKEY("suffix")}, FlatString,    NULL, 0, {HKEY("Suffix")}},
47         {{HKEY("")},       TerminateList, NULL, 0, {HKEY("")}}
48 };
49
50 vcField VCStr_Addrs [] = {
51         {{HKEY("POBox")},    FlatString,    NULL, 0, {HKEY("PO box")}},
52         {{HKEY("address")},  FlatString,    NULL, 0, {HKEY("Address")}},
53         {{HKEY("address2")}, FlatString,    NULL, 0, {HKEY("")}},
54         {{HKEY("city")},     FlatString,    NULL, 0, {HKEY("City")}},
55         {{HKEY("state")},    FlatString,    NULL, 0, {HKEY("State")}},
56         {{HKEY("zip")},      FlatString,    NULL, 0, {HKEY("ZIP code")}},
57         {{HKEY("country")},  FlatString,    NULL, 0, {HKEY("Country")}},
58         {{HKEY("")},         TerminateList, NULL, 0, {HKEY("")}}
59 };
60
61 vcField VCStrE [] = {
62         {{HKEY("version")},         Number,                 NULL,        0, {HKEY("")}},
63         {{HKEY("rev")},             Number,                 NULL,        0, {HKEY("")}},
64         {{HKEY("label")},           FlatString,             NULL,        0, {HKEY("")}},
65         {{HKEY("uid")},             FlatString,             NULL,        0, {HKEY("")}},
66         {{HKEY("n")},               StringCluster,          VCStr_Ns,    0, {HKEY("")}}, /* N is name, but only if there's no FN already there */
67         {{HKEY("fn")},              FlatString,             NULL,        0, {HKEY("")}}, /* FN (full name) is a true 'display name' field */
68         {{HKEY("title")},           FlatString,             NULL,        0, {HKEY("Title:")}},
69         {{HKEY("org")},             FlatString,             NULL,        0, {HKEY("Organization:")}},/* organization */
70         {{HKEY("email")},           EmailAddr,              NULL,        0, {HKEY("E-mail:")}},
71         {{HKEY("tel")},             PhoneNumber,            NULL,        0, {HKEY("Telephone:")}},
72         {{HKEY("adr")},             StringCluster,          VCStr_Addrs, 0, {HKEY("Address:")}},
73         {{HKEY("photo")},           Base64BinaryAttachment, NULL,        0, {HKEY("Photo:")}},
74         {{HKEY("tel;home")},        PhoneNumber,            NULL,        0, {HKEY(" (home)")}},
75         {{HKEY("tel;work")},        PhoneNumber,            NULL,        0, {HKEY(" (work)")}},
76         {{HKEY("tel;fax")},         PhoneNumber,            NULL,        0, {HKEY(" (fax)")}},
77         {{HKEY("tel;cell")},        PhoneNumber,            NULL,        0, {HKEY(" (cell)")}},
78         {{HKEY("email;internet")},  EmailAddr,              NULL,        0, {HKEY("E-mail:")}},
79         {{HKEY("")},                TerminateList,          NULL,        0, {HKEY("")}}
80 };
81
82 ConstStr VCStr [] = {
83         {HKEY("")},
84         {HKEY("n")}, /* N is name, but only if there's no FN already there */
85         {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
86         {HKEY("title")},   /* title */
87         {HKEY("org")},    /* organization */
88         {HKEY("email")},
89         {HKEY("tel")},
90         {HKEY("work")},
91         {HKEY("home")},
92         {HKEY("cell")},
93         {HKEY("adr")},
94         {HKEY("photo")},
95         {HKEY("version")},
96         {HKEY("rev")},
97         {HKEY("label")},
98         {HKEY("uid")}
99 };
100
101
102 HashList *DefineToToken = NULL;
103 HashList *VCTokenToDefine = NULL;
104 HashList *vcNames = NULL; /* todo: fill with the name strings */
105
106
107 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
108 {
109         RegisterTokenParamDefine(SKEY(name), vf->cval);
110         Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
111         Put(vcNames, LKEY(vf->cval), NewStrBufPlain(CKEY(vf->Name)), HFreeStrBuf);
112
113         syslog(LOG_DEBUG, "Token: %s -> %ld, %d", 
114                ChrPtr(name),
115                vf->cval, 
116                inTokenCount);
117
118 }
119
120 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer)
121 {
122         int i = 0;
123         while (vf[i].STR.len > 0) {
124                 StrBuf *subStr = NewStrBuf();
125                 vf[i].cval = (*enumCounter) ++;
126                 StrBufAppendBuf(subStr, BaseStr, 0);
127                 if (StrLength(subStr) > 0) {
128                         StrBufAppendBufPlain(subStr, HKEY("."), 0);
129                 }
130                 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
131                 if (layer == 0) {
132                         Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
133                 }
134                 switch (vf[i].Type) {
135                 case FlatString:
136                         break;
137                 case StringCluster:
138                 {
139                         autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1);
140                         i++;
141                         continue;
142                 }
143                 break;
144                 case PhoneNumber:
145                         break;
146                 case EmailAddr:
147                         break;
148                 case Street:
149                         break;
150                 case Number:
151                         break;
152                 case AliasFor:
153                         break;
154                 case Base64BinaryAttachment:
155                         break;
156                 case TerminateList:
157                         break;
158                 }
159                 RegisterVCardToken(&vf[i], subStr, i);
160                 i++;
161         }
162 }
163
164 int preeval_vcard_item(WCTemplateToken *Token)
165 {
166         WCTemplputParams TPP;
167         WCTemplputParams *TP;
168         int searchFieldNo;
169         StrBuf *Target = NULL;
170
171         memset(&TPP, 0, sizeof(WCTemplputParams));
172         TP = &TPP;
173         TP->Tokens = Token;
174         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
175         if (searchFieldNo >= VCEnumCounter) {
176                 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
177                                  "Invalid define");
178                 return 0;
179         }
180         return 1;
181 }
182
183 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
184 {
185         void *vItem;
186         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
187         HashList *vc = (HashList*) CTX(CTX_VCARD);
188         if (GetHash(vc, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
189                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
190         }
191 }
192
193 void tmpl_vcard_context_item(StrBuf *Target, WCTemplputParams *TP)
194 {
195         void *vItem;
196         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
197         HashList *vc = (HashList*) CTX(CTX_VCARD);
198
199         if (t == NULL) {
200                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
201                                  "Missing context");
202                 return;
203         }
204
205         if (GetHash(vc, LKEY(t->cval), &vItem) && (vItem != NULL)) {
206                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 0);
207         }
208         else {
209                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
210                                  "Doesn't have that key - did you miss to filter in advance?");
211         }
212 }
213 int preeval_vcard_name_str(WCTemplateToken *Token)
214 {
215         WCTemplputParams TPP;
216         WCTemplputParams *TP;
217         int searchFieldNo;
218         StrBuf *Target = NULL;
219
220         memset(&TPP, 0, sizeof(WCTemplputParams));
221         TP = &TPP;
222         TP->Tokens = Token;
223         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
224         if (searchFieldNo >= VCEnumCounter) {
225                 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
226                                  "Invalid define");
227                 return 0;
228         }
229         return 1;
230 }
231
232 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
233 {
234         void *vItem;
235         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
236         /* todo: get descriptive string for this vcard type */
237         if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
238                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
239         }
240         else {
241                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
242                                  "No i18n string for this.");
243                 return;
244         }
245 }
246
247 void tmpl_vcard_context_name_str(StrBuf *Target, WCTemplputParams *TP)
248 {
249         void *vItem;
250         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
251
252         if (t == NULL) {
253                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
254                                  "Missing context");
255                 return;
256         }
257         
258         if (GetHash(vcNames, LKEY(t->cval), &vItem) && (vItem != NULL)) {
259                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
260         }
261         else {
262                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
263                                  "No i18n string for this.");
264                 return;
265         }
266 }
267
268 int filter_VC_ByType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
269 {
270         long searchType;
271         long type = 0;
272         void *v;
273         int rc = 0;
274         vcField *vf = (vcField*) Context;
275
276         memcpy(&type, key, sizeof(long));
277         searchType = GetTemplateTokenNumber(Target, TP, IT_ADDT_PARAM(0), 0);
278         
279         if (vf->Type == searchType) {
280                 HashList *vc = (HashList*) CTX(CTX_VCARD);
281                 if (GetHash(vc, LKEY(vf->cval), &v) && v != NULL)
282                         return 1;
283         }
284         return rc;
285 }
286
287
288
289
290 HashList *getContextVcard(StrBuf *Target, WCTemplputParams *TP)
291 {
292         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
293         HashList *vc = (HashList*) CTX(CTX_VCARD);
294
295         if ((vf == NULL) || (vc == NULL)) {
296                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
297                                  "Need VCard and Vcard type in context");
298                 
299                 return NULL;
300         }
301         return vc;
302 }
303
304 int filter_VC_ByContextType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
305 {
306         long searchType;
307         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
308
309         memcpy(&searchType, key, sizeof(long));
310         
311         if (vf->cval == searchType) {
312                 return 1;
313         }
314         else {
315                 return 0;
316         }
317 }
318
319
320 int conditional_VC_Havetype(StrBuf *Target, WCTemplputParams *TP)
321 {
322         HashList *vc = (HashList*) CTX(CTX_VCARD);
323         long HaveFieldType = GetTemplateTokenNumber(Target, TP, 2, 0);
324         int rc = 0;     
325         void *vVCitem;
326         const char *Key;
327         long len;
328         HashPos *it = GetNewHashPos(vc, 0);
329         while (GetNextHashPos(vc, it, &len, &Key, &vVCitem) && 
330                (vVCitem != NULL)) 
331         {
332                 void *vvcField;
333                 long type = 0;
334                 memcpy(&type, Key, sizeof(long));
335                 if (GetHash(DefineToToken, LKEY(type), &vvcField) &&
336                     (vvcField != NULL))
337                 {
338                         vcField *t = (vcField*) vvcField;
339                         if (t && t->Type == HaveFieldType) {
340                                 rc = 1;
341                                 break;
342                         }
343                 }
344         }
345         DeleteHashPos(&it);
346         return rc;
347 }
348
349 /*
350  * Record compare function for sorting address book indices
351  */
352 int abcmp(const void *ab1, const void *ab2) {
353         return(strcasecmp(
354                 (((const addrbookent *)ab1)->ab_name),
355                 (((const addrbookent *)ab2)->ab_name)
356         ));
357 }
358
359
360 /*
361  * Helper function for do_addrbook_view()
362  * Converts a name into a three-letter tab label
363  */
364 void nametab(char *tabbuf, long len, char *name) {
365         stresc(tabbuf, len, name, 0, 0);
366         tabbuf[0] = toupper(tabbuf[0]);
367         tabbuf[1] = tolower(tabbuf[1]);
368         tabbuf[2] = tolower(tabbuf[2]);
369         tabbuf[3] = 0;
370 }
371
372
373 /*
374  * If it's an old "Firstname Lastname" style record, try to convert it.
375  */
376 void lastfirst_firstlast(char *namebuf) {
377         char firstname[SIZ];
378         char lastname[SIZ];
379         int i;
380
381         if (namebuf == NULL) return;
382         if (strchr(namebuf, ';') != NULL) return;
383
384         i = num_tokens(namebuf, ' ');
385         if (i < 2) return;
386
387         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
388         remove_token(namebuf, i-1, ' ');
389         strcpy(firstname, namebuf);
390         sprintf(namebuf, "%s; %s", lastname, firstname);
391 }
392
393
394
395 wc_mime_attachment *load_vcard(message_summary *Msg) 
396 {
397         HashPos  *it;
398         StrBuf *FoundCharset = NewStrBuf();
399         StrBuf *Error;
400         void *vMime;
401         const char *Key;
402         long len;
403         wc_mime_attachment *Mime;
404         wc_mime_attachment *VCMime = NULL;
405
406         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
407         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
408         Msg->MsgBody->msgnum = Msg->msgnum;
409
410         load_message(Msg, FoundCharset, &Error);
411
412         FreeStrBuf(&FoundCharset);
413         /* look up the vcard... */
414         it = GetNewHashPos(Msg->AllAttach, 0);
415         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
416                (vMime != NULL)) 
417         {
418                 Mime = (wc_mime_attachment*) vMime;
419                 if ((strcmp(ChrPtr(Mime->ContentType),
420                            "text/x-vcard") == 0) ||
421                     (strcmp(ChrPtr(Mime->ContentType),
422                             "text/vcard") == 0))
423                 {
424                         VCMime = Mime;
425                         break;
426                 }
427         }
428         DeleteHashPos(&it);
429         if (VCMime == NULL)
430                 return NULL;
431
432         if (VCMime->Data == NULL)
433                 MimeLoadData(VCMime);
434         return VCMime;
435 }
436
437 /*
438  * fetch the display name off a vCard
439  */
440 void fetch_ab_name(message_summary *Msg, char **namebuf) {
441         long len;
442         int i;
443         wc_mime_attachment *VCMime = NULL;
444
445         if (namebuf == NULL) return;
446
447         VCMime = load_vcard(Msg);
448         if (VCMime == NULL)
449                 return;
450
451         /* Grab the name off the card */
452         display_vcard(WC->WBuf, VCMime, 0, 0, namebuf, Msg->msgnum);
453
454         if (*namebuf != NULL) {
455                 lastfirst_firstlast(*namebuf);
456                 striplt(*namebuf);
457                 len = strlen(*namebuf);
458                 for (i=0; i<len; ++i) {
459                         if ((*namebuf)[i] != ';') return;
460                 }
461                 free (*namebuf);
462                 (*namebuf) = strdup(_("(no name)"));
463         }
464         else {
465                 (*namebuf) = strdup(_("(no name)"));
466         }
467 }
468
469
470
471 /*
472  * Turn a vCard "n" (name) field into something displayable.
473  */
474 void vcard_n_prettyize(char *name)
475 {
476         char *original_name;
477         int i, j, len;
478
479         original_name = strdup(name);
480         len = strlen(original_name);
481         for (i=0; i<5; ++i) {
482                 if (len > 0) {
483                         if (original_name[len-1] == ' ') {
484                                 original_name[--len] = 0;
485                         }
486                         if (original_name[len-1] == ';') {
487                                 original_name[--len] = 0;
488                         }
489                 }
490         }
491         strcpy(name, "");
492         j=0;
493         for (i=0; i<len; ++i) {
494                 if (original_name[i] == ';') {
495                         name[j++] = ',';
496                         name[j++] = ' ';                        
497                 }
498                 else {
499                         name[j++] = original_name[i];
500                 }
501         }
502         name[j] = '\0';
503         free(original_name);
504 }
505
506
507
508
509 /*
510  * preparse a vcard name
511  * display_vcard() calls this after parsing the textual vCard into
512  * our 'struct vCard' data object.
513  * This gets called instead of display_parsed_vcard() if we are only looking
514  * to extract the person's name instead of displaying the card.
515  */
516 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
517         char *name;
518         char *prop;
519         char buf[SIZ];
520         int j, n, len;
521         int is_qp = 0;
522         int is_b64 = 0;
523
524         *storename = NULL;
525
526         name = vcard_get_prop(v, "n", 1, 0, 0);
527         if (name != NULL) {
528                 len = strlen(name);
529                 prop = vcard_get_prop(v, "n", 1, 0, 1);
530                 n = num_tokens(prop, ';');
531
532                 for (j=0; j<n; ++j) {
533                         extract_token(buf, prop, j, ';', sizeof buf);
534                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
535                                 is_qp = 1;
536                         }
537                         if (!strcasecmp(buf, "encoding=base64")) {
538                                 is_b64 = 1;
539                         }
540                 }
541                 if (is_qp) {
542                         /* %ff can become 6 bytes in utf8  */
543                         *storename = malloc(len * 2 + 3); 
544                         j = CtdlDecodeQuotedPrintable(
545                                 *storename, name,
546                                 len);
547                         (*storename)[j] = 0;
548                 }
549                 else if (is_b64) {
550                         /* ff will become one byte.. */
551                         *storename = malloc(len + 50);
552                         CtdlDecodeBase64(
553                                 *storename, name,
554                                 len);
555                 }
556                 else {
557                         size_t len;
558
559                         len = strlen (name);
560                         
561                         *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
562                         memcpy(*storename, name, len + 1);
563                 }
564                 /* vcard_n_prettyize(storename); */
565         }
566
567 }
568
569
570
571 /*
572  * html print a vcard
573  * display_vcard() calls this after parsing the textual vCard into
574  * our 'struct vCard' data object.
575  *
576  * Set 'full' to nonzero to display the full card, otherwise it will only
577  * show a summary line.
578  *
579  * This code is a bit ugly, so perhaps an explanation is due: we do this
580  * in two passes through the vCard fields.  On the first pass, we process
581  * fields we understand, and then render them in a pretty fashion at the
582  * end.  Then we make a second pass, outputting all the fields we don't
583  * understand in a simple two-column name/value format.
584  * v            the vCard to display
585  * full         display all items of the vcard?
586  * msgnum       Citadel message pointer
587  */
588 void display_parsed_vcard(StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
589 {
590         int i, j;
591         char buf[SIZ];
592         char *name;
593         int is_qp = 0;
594         int is_b64 = 0;
595         char *thisname, *thisvalue;
596         char firsttoken[SIZ];
597         int pass;
598
599         char fullname[SIZ];
600         char title[SIZ];
601         char org[SIZ];
602         char phone[SIZ];
603         char mailto[SIZ];
604
605         strcpy(fullname, "");
606         strcpy(phone, "");
607         strcpy(mailto, "");
608         strcpy(title, "");
609         strcpy(org, "");
610
611         if (!full) {
612                 StrBufAppendPrintf(Target, "<td>");
613                 name = vcard_get_prop(v, "fn", 1, 0, 0);
614                 if (name != NULL) {
615                         StrEscAppend(Target, NULL, name, 0, 0);
616                 }
617                 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
618                         strcpy(fullname, name);
619                         vcard_n_prettyize(fullname);
620                         StrEscAppend(Target, NULL, fullname, 0, 0);
621                 }
622                 else {
623                         StrBufAppendPrintf(Target, "&nbsp;");
624                 }
625                 StrBufAppendPrintf(Target, "</td>");
626                 return;
627         }
628
629         StrBufAppendPrintf(Target, "<div align=\"center\">"
630                 "<table bgcolor=\"#aaaaaa\" width=\"50%%\">");
631         for (pass=1; pass<=2; ++pass) {
632
633                 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
634                         int len;
635                         thisname = strdup(v->prop[i].name);
636                         extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
637         
638                         for (j=0; j<num_tokens(thisname, ';'); ++j) {
639                                 extract_token(buf, thisname, j, ';', sizeof buf);
640                                 if (!strcasecmp(buf, "encoding=quoted-printable")) {
641                                         is_qp = 1;
642                                         remove_token(thisname, j, ';');
643                                 }
644                                 if (!strcasecmp(buf, "encoding=base64")) {
645                                         is_b64 = 1;
646                                         remove_token(thisname, j, ';');
647                                 }
648                         }
649                         
650                         len = strlen(v->prop[i].value);
651                         /* if we have some untagged QP, detect it here. */
652                         if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL))
653                                 utf8ify_rfc822_string(&v->prop[i].value);
654
655                         if (is_qp) {
656                                 /* %ff can become 6 bytes in utf8 */
657                                 thisvalue = malloc(len * 2 + 3); 
658                                 j = CtdlDecodeQuotedPrintable(
659                                         thisvalue, v->prop[i].value,
660                                         len);
661                                 thisvalue[j] = 0;
662                         }
663                         else if (is_b64) {
664                                 /* ff will become one byte.. */
665                                 thisvalue = malloc(len + 50);
666                                 CtdlDecodeBase64(
667                                         thisvalue, v->prop[i].value,
668                                         strlen(v->prop[i].value) );
669                         }
670                         else {
671                                 thisvalue = strdup(v->prop[i].value);
672                         }
673         
674                         /* Various fields we may encounter ***/
675         
676                         /* N is name, but only if there's no FN already there */
677                         if (!strcasecmp(firsttoken, "n")) {
678                                 if (IsEmptyStr(fullname)) {
679                                         strcpy(fullname, thisvalue);
680                                         vcard_n_prettyize(fullname);
681                                 }
682                         }
683         
684                         /* FN (full name) is a true 'display name' field */
685                         else if (!strcasecmp(firsttoken, "fn")) {
686                                 strcpy(fullname, thisvalue);
687                         }
688
689                         /* title */
690                         else if (!strcasecmp(firsttoken, "title")) {
691                                 strcpy(title, thisvalue);
692                         }
693         
694                         /* organization */
695                         else if (!strcasecmp(firsttoken, "org")) {
696                                 strcpy(org, thisvalue);
697                         }
698         
699                         else if (!strcasecmp(firsttoken, "email")) {
700                                 size_t len;
701                                 if (!IsEmptyStr(mailto)) strcat(mailto, "<br>");
702                                 strcat(mailto,
703                                         "<a href=\"display_enter"
704                                         "?force_room=_MAIL_?recp=");
705
706                                 len = strlen(mailto);
707                                 urlesc(&mailto[len], SIZ - len, "\"");
708                                 len = strlen(mailto);
709                                 urlesc(&mailto[len], SIZ - len,  fullname);
710                                 len = strlen(mailto);
711                                 urlesc(&mailto[len], SIZ - len, "\" <");
712                                 len = strlen(mailto);
713                                 urlesc(&mailto[len], SIZ - len, thisvalue);
714                                 len = strlen(mailto);
715                                 urlesc(&mailto[len], SIZ - len, ">");
716
717                                 strcat(mailto, "\">");
718                                 len = strlen(mailto);
719                                 stresc(mailto+len, SIZ - len, thisvalue, 1, 1);
720                                 strcat(mailto, "</A>");
721                         }
722                         else if (!strcasecmp(firsttoken, "tel")) {
723                                 if (!IsEmptyStr(phone)) strcat(phone, "<br>");
724                                 strcat(phone, thisvalue);
725                                 for (j=0; j<num_tokens(thisname, ';'); ++j) {
726                                         extract_token(buf, thisname, j, ';', sizeof buf);
727                                         if (!strcasecmp(buf, "tel"))
728                                                 strcat(phone, "");
729                                         else if (!strcasecmp(buf, "work"))
730                                                 strcat(phone, _(" (work)"));
731                                         else if (!strcasecmp(buf, "home"))
732                                                 strcat(phone, _(" (home)"));
733                                         else if (!strcasecmp(buf, "cell"))
734                                                 strcat(phone, _(" (cell)"));
735                                         else {
736                                                 strcat(phone, " (");
737                                                 strcat(phone, buf);
738                                                 strcat(phone, ")");
739                                         }
740                                 }
741                         }
742                         else if (!strcasecmp(firsttoken, "adr")) {
743                                 if (pass == 2) {
744                                         StrBufAppendPrintf(Target, "<tr><td>");
745                                         StrBufAppendPrintf(Target, _("Address:"));
746                                         StrBufAppendPrintf(Target, "</td><td>");
747                                         for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
748                                                 extract_token(buf, thisvalue, j, ';', sizeof buf);
749                                                 if (!IsEmptyStr(buf)) {
750                                                         StrEscAppend(Target, NULL, buf, 0, 0);
751                                                         if (j<3) StrBufAppendPrintf(Target, "<br>");
752                                                         else StrBufAppendPrintf(Target, " ");
753                                                 }
754                                         }
755                                         StrBufAppendPrintf(Target, "</td></tr>\n");
756                                 }
757                         }
758                         /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { 
759                                 // Only output on second pass
760                                 StrBufAppendPrintf(Target, "<tr><td>");
761                                 StrBufAppendPrintf(Target, _("Photo:"));
762                                 StrBufAppendPrintf(Target, "</td><td>");
763                                 StrBufAppendPrintf(Target, "<img src=\"/vcardphoto/%ld/\" alt=\"Contact photo\"/>",msgnum);
764                                 StrBufAppendPrintf(Target, "</td></tr>\n");
765                         } */
766                         else if (!strcasecmp(firsttoken, "version")) {
767                                 /* ignore */
768                         }
769                         else if (!strcasecmp(firsttoken, "rev")) {
770                                 /* ignore */
771                         }
772                         else if (!strcasecmp(firsttoken, "label")) {
773                                 /* ignore */
774                         }
775                         else {
776
777                                 /*** Don't show extra fields.  They're ugly.
778                                 if (pass == 2) {
779                                         StrBufAppendPrintf(Target, "<TR><TD>");
780                                         StrEscAppend(Target, NULL, thisname, 0, 0);
781                                         StrBufAppendPrintf(Target, "</TD><TD>");
782                                         StrEscAppend(Target, NULL, thisvalue, 0, 0);
783                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
784                                 }
785                                 ***/
786                         }
787         
788                         free(thisname);
789                         free(thisvalue);
790                 }
791         
792                 if (pass == 1) {
793                         StrBufAppendPrintf(Target, "<tr bgcolor=\"#aaaaaa\">"
794       "<td colspan=2 bgcolor=\"#ffffff\">"
795       "<img align=\"center\" src=\"static/webcit_icons/essen/32x32/contact.png\">"
796       "<font size=\"+1\"><b>");
797                         StrEscAppend(Target, NULL, fullname, 0, 0);
798                         StrBufAppendPrintf(Target, "</b></font>");
799                         if (!IsEmptyStr(title)) {
800                                 StrBufAppendPrintf(Target, "<div align=\"right>\"");
801                                 StrEscAppend(Target, NULL, title, 0, 0);
802                                 StrBufAppendPrintf(Target, "</div>");
803                         }
804                         if (!IsEmptyStr(org)) {
805                                 StrBufAppendPrintf(Target, "<div align=\"right\">");
806                                 StrEscAppend(Target, NULL, org, 0, 0);
807                                 StrBufAppendPrintf(Target, "</div>");
808                         }
809                         StrBufAppendPrintf(Target, "</td></tr>\n");
810                 
811                         if (!IsEmptyStr(phone)) {
812                                 StrBufAppendPrintf(Target, "<tr><td>");
813                                 StrBufAppendPrintf(Target, _("Telephone:"));
814                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", phone);
815                         }
816                         if (!IsEmptyStr(mailto)) {
817                                 StrBufAppendPrintf(Target, "<tr><td>");
818                                 StrBufAppendPrintf(Target, _("E-mail:"));
819                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", mailto);
820                         }
821                 }
822
823         }
824
825         StrBufAppendPrintf(Target, "</table></div>\n");
826 }
827
828
829 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
830 {
831         /* if we have some untagged QP, detect it here. */
832         if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
833                 StrBuf *b;
834                 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
835                 b = ThisFieldStr;
836                 ThisFieldStr = Swap; 
837                 Swap = b;
838                 FlushStrBuf(Swap);
839         }
840         Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
841 }
842 /*
843  * html print a vcard
844  * display_vcard() calls this after parsing the textual vCard into
845  * our 'struct vCard' data object.
846  *
847  * Set 'full' to nonzero to display the full card, otherwise it will only
848  * show a summary line.
849  *
850  * This code is a bit ugly, so perhaps an explanation is due: we do this
851  * in two passes through the vCard fields.  On the first pass, we process
852  * fields we understand, and then render them in a pretty fashion at the
853  * end.  Then we make a second pass, outputting all the fields we don't
854  * understand in a simple two-column name/value format.
855  * v            the vCard to display
856  * full         display all items of the vcard?
857  * msgnum       Citadel message pointer
858  */
859 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, int full, wc_mime_attachment *Mime)
860 {
861         StrBuf *Val = NULL;
862         StrBuf *Swap = NULL;
863         int i, j, k;
864         char buf[20]; //SIZ];
865         int is_qp = 0;
866         int is_b64 = 0;
867         int ntokens, len;
868         StrBuf *thisname = NULL;
869         char firsttoken[20]; ///SIZ];
870         //void *V;
871         HashList *thisVC;
872         StrBuf *thisVCToken;
873         void *vField = NULL;
874
875         thisVC = NewHash(0, lFlathash);
876         Swap = NewStrBuf ();
877         thisname = NewStrBuf();
878         thisVCToken = NewStrBufPlain(NULL, 63);
879         for (i=0; i<(v->numprops); ++i) {
880                 FlushStrBuf(thisVCToken);
881                 is_qp = 0;
882                 is_b64 = 0;
883                 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
884                 StrBufPlain(thisname, v->prop[i].name, -1);
885                 StrBufLowerCase(thisname);
886                 
887                 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
888                 ntokens = num_tokens(ChrPtr(thisname), ';');
889                 for (j=0, k=0; j < ntokens && k < 10; ++j) {
890                         ///int evc[10];
891                         
892                         len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
893                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
894                                 is_qp = 1;
895 /*                              remove_token(thisname, j, ';');*/
896                         }
897                         else if (!strcasecmp(buf, "encoding=base64")) {
898                                 is_b64 = 1;
899 /*                              remove_token(thisname, j, ';');*/
900                         }
901                         else{
902                                 if (StrLength(thisVCToken) > 0) {
903                                         StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
904                                 }
905                                 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
906                                 /*
907                                 if (GetHash(VCToEnum, buf, len, &V))
908                                 {
909                                         evc[k] = (int) V;
910
911                                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
912
913                                         syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
914                                         k++;
915                                 }
916 */
917
918                         }
919                 }
920
921                 vField = NULL;  
922                 if ((StrLength(thisVCToken) > 0) &&
923                     GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) && 
924                     (vField != NULL)) {
925                         vcField *thisField = (vcField *)vField;
926                         StrBuf *ThisFieldStr = NULL;
927                         syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
928                         switch (thisField->Type) {
929                         case StringCluster: {
930                                 int j = 0;
931                                 const char *Pos = NULL;
932                                 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
933                                 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
934                                 while (thisField->Sub[j].STR.len > 0) {
935                                         StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
936                                         ThisFieldStr = NewStrBufDup(Buf);
937                                         
938                                         PutVcardItem(thisVC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
939                                         j++;
940                                 }
941                         }
942                                 break;
943                         case FlatString:
944                         case PhoneNumber:
945                         case EmailAddr:
946                         case Street:
947                         case Number:
948                         case AliasFor:
949                                 /* copy over the payload into a StrBuf */
950                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
951                                 PutVcardItem(thisVC, thisField, ThisFieldStr, is_qp, Swap);
952
953                                 break;
954                         case Base64BinaryAttachment:
955                         case TerminateList:
956                                 break;
957                         }
958
959                 }
960                 /* copy over the payload into a StrBuf */
961                 Val = NewStrBufPlain(v->prop[i].value, -1);
962                         
963                 /* if we have some untagged QP, detect it here. */
964                 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
965                         StrBuf *b;
966                         StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
967                         b = Val;
968                         Val = Swap; 
969                         Swap = b;
970                         FlushStrBuf(Swap);
971                 }
972                 else if (is_b64) {
973                         StrBufDecodeBase64(Val);
974
975                 }
976 #if 0
977                 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
978                         firsttoken,
979                        ChrPtr(thisname),
980                         ChrPtr(Val),
981                         v->prop[i].value);
982                 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
983                 {
984                         eVC evc = (eVC) V;
985                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
986                         syslog(LOG_DEBUG, "[%ul]\n", evc);
987                         Val = NULL;
988                 }
989                 else
990                         syslog(LOG_DEBUG, "[]\n");
991 /*
992 TODO: check for layer II
993                 else 
994                 {
995                         long max = num_tokens(thisname, ';');
996                         firsttoken[len] = '_';
997
998                         for (j = 0; j < max; j++) {
999 //                      firsttoken[len]
1000
1001                                 extract_token(buf, thisname, j, ';', sizeof (buf));
1002                                         if (!strcasecmp(buf, "tel"))
1003                                                 strcat(phone, "");
1004                                         else if (!strcasecmp(buf, "work"))
1005                                                 strcat(phone, _(" (work)"));
1006                                         else if (!strcasecmp(buf, "home"))
1007                                                 strcat(phone, _(" (home)"));
1008                                         else if (!strcasecmp(buf, "cell"))
1009                                                 strcat(phone, _(" (cell)"));
1010                                         else {
1011                                                 strcat(phone, " (");
1012                                                 strcat(phone, buf);
1013                                                 strcat(phone, ")");
1014                                         }
1015                                 }
1016                         }
1017
1018                 }
1019 */
1020 #endif  
1021                 FreeStrBuf(&Val);
1022                 ////free(thisname);
1023                 /// thisname = NULL;
1024         }
1025
1026
1027         {
1028                 WCTemplputParams *TP = NULL;
1029                 WCTemplputParams SubTP;
1030                 FlushStrBuf(Target);
1031                 StackContext(TP, &SubTP, thisVC, CTX_VCARD, 0, NULL);
1032                 {
1033                         DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
1034                 }
1035                 UnStackContext(&SubTP);
1036         }
1037         printf("%s\n", ChrPtr(Target));
1038         FreeStrBuf(&thisVCToken);
1039         DeleteHash(&thisVC);/// todo
1040 }
1041
1042 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
1043 {
1044         HashList *VC = CTX(CTX_VCARD);
1045         int evc;
1046         void *vStr;
1047
1048         evc = GetTemplateTokenNumber(Target, TP, 0, -1);
1049         if (evc != -1)
1050         {
1051                 if (GetHash(VC, IKEY(evc), &vStr))
1052                 {
1053                         StrBufAppendTemplate(Target, TP,
1054                                              (StrBuf*) vStr,
1055                                              1);
1056                 }
1057         }
1058         
1059 }
1060
1061 void new_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
1062 {
1063         HashList *VC;
1064         WCTemplputParams SubTP;
1065
1066         memset(&SubTP, 0, sizeof(WCTemplputParams));    
1067
1068
1069         VC = NewHash(0, Flathash);
1070         parse_vcard(Target, v, VC, full, Mime);
1071
1072         SubTP.Filter.ContextType = CTX_VCARD;
1073         SubTP.Context = VC;
1074
1075         //DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
1076         DeleteHash(&VC);
1077 }
1078
1079
1080
1081 /*
1082  * Display a textual vCard
1083  * (Converts to a vCard object and then calls the actual display function)
1084  * Set 'full' to nonzero to display the whole card instead of a one-liner.
1085  * Or, if "storename" is non-NULL, just store the person's name in that
1086  * buffer instead of displaying the card at all.
1087  *
1088  * vcard_source the buffer containing the vcard text
1089  * alpha        Display only if name begins with this letter of the alphabet
1090  * full         Display the full vCard (otherwise just the display name)
1091  * storename    If not NULL, also store the display name here
1092  * msgnum       Citadel message pointer
1093  */
1094 void display_vcard(StrBuf *Target, 
1095                    wc_mime_attachment *Mime, 
1096                    char alpha, 
1097                    int full, 
1098                    char **storename, 
1099                    long msgnum) 
1100 {
1101         struct vCard *v;
1102         char *name;
1103         StrBuf *Buf;
1104         StrBuf *Buf2;
1105         char this_alpha = 0;
1106
1107         v = VCardLoad(Mime->Data);
1108
1109         if (v == NULL) return;
1110
1111         name = vcard_get_prop(v, "n", 1, 0, 0);
1112         if (name != NULL) {
1113                 Buf = NewStrBufPlain(name, -1);
1114                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
1115                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
1116                 this_alpha = ChrPtr(Buf)[0];
1117                 FreeStrBuf(&Buf);
1118                 FreeStrBuf(&Buf2);
1119         }
1120
1121         if (storename != NULL) {
1122                 fetchname_parsed_vcard(v, storename);
1123         }
1124         else if ((alpha == 0) || 
1125                  ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) || 
1126                  ((!isalpha(alpha)) && (!isalpha(this_alpha)))
1127                 ) 
1128         {
1129                 if (ibstr("x") == 1) {
1130                         new_vcard (Target, v, full, Mime);
1131                 } else {
1132                         display_parsed_vcard(Target, v, full, Mime);
1133                 }
1134         }
1135
1136         vcard_free(v);
1137 }
1138
1139
1140
1141 /*
1142  * Render the address book using info we gathered during the scan
1143  *
1144  * addrbook     the addressbook to render
1145  * num_ab       the number of the addressbook
1146  */
1147 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
1148         int i = 0;
1149         int displayed = 0;
1150         int bg = 0;
1151         static int NAMESPERPAGE = 60;
1152         int num_pages = 0;
1153         int tabfirst = 0;
1154         char tabfirst_label[64];
1155         int tablast = 0;
1156         char tablast_label[64];
1157         char this_tablabel[64];
1158         int page = 0;
1159         char **tablabels;
1160
1161         if (num_ab == 0) {
1162                 wc_printf("<br><br><br><div align=\"center\"><i>");
1163                 wc_printf(_("This address book is empty."));
1164                 wc_printf("</i></div>\n");
1165                 return;
1166         }
1167
1168         if (num_ab > 1) {
1169                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
1170         }
1171
1172         num_pages = (num_ab / NAMESPERPAGE) + 1;
1173
1174         tablabels = malloc(num_pages * sizeof (char *));
1175         if (tablabels == NULL) {
1176                 wc_printf("<br><br><br><div align=\"center\"><i>");
1177                 wc_printf(_("An internal error has occurred."));
1178                 wc_printf("</i></div>\n");
1179                 return;
1180         }
1181
1182         for (i=0; i<num_pages; ++i) {
1183                 tabfirst = i * NAMESPERPAGE;
1184                 tablast = tabfirst + NAMESPERPAGE - 1;
1185                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
1186                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
1187                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
1188                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
1189                 tablabels[i] = strdup(this_tablabel);
1190         }
1191
1192         tabbed_dialog(num_pages, tablabels);
1193         page = (-1);
1194
1195         for (i=0; i<num_ab; ++i) {
1196
1197                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
1198                         page = (i / NAMESPERPAGE);
1199                         if (page > 0) {
1200                                 wc_printf("</tr></table>\n");
1201                                 end_tab(page-1, num_pages);
1202                         }
1203                         begin_tab(page, num_pages);
1204                         wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
1205                         displayed = 0;
1206                 }
1207
1208                 if ((displayed % 4) == 0) {
1209                         if (displayed > 0) {
1210                                 wc_printf("</tr>\n");
1211                         }
1212                         bg = 1 - bg;
1213                         wc_printf("<tr bgcolor=\"#%s\">",
1214                                 (bg ? "dddddd" : "ffffff")
1215                         );
1216                 }
1217         
1218                 wc_printf("<td>");
1219
1220                 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
1221                         addrbook[i].ab_msgnum);
1222                 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
1223                 vcard_n_prettyize(addrbook[i].ab_name);
1224                 escputs(addrbook[i].ab_name);
1225                 wc_printf("</a></td>\n");
1226                 ++displayed;
1227         }
1228
1229         /* Placeholders for empty columns at end */
1230         if ((num_ab % 4) != 0) {
1231                 for (i=0; i<(4-(num_ab % 4)); ++i) {
1232                         wc_printf("<td>&nbsp;</td>");
1233                 }
1234         }
1235
1236         wc_printf("</tr></table>\n");
1237         end_tab((num_pages-1), num_pages);
1238
1239         begin_tab(num_pages, num_pages);
1240         /* FIXME there ought to be something here */
1241         end_tab(num_pages, num_pages);
1242
1243         for (i=0; i<num_pages; ++i) {
1244                 free(tablabels[i]);
1245         }
1246         free(tablabels);
1247 }
1248
1249
1250
1251
1252 /*
1253  * Edit the vCard component of a MIME message.  
1254  * Supply the message number
1255  * and MIME part number to fetch.  Or, specify -1 for the message number
1256  * to start with a blank card.
1257  */
1258 void do_edit_vcard(long msgnum, char *partnum, 
1259                    message_summary *VCMsg,
1260                    wc_mime_attachment *VCAtt,
1261                    const char *return_to, 
1262                    const char *force_room) {
1263         wcsession *WCC = WC;
1264         message_summary *Msg = NULL;
1265         wc_mime_attachment *VCMime = NULL;
1266         struct vCard *v;
1267         int i;
1268         char *key, *value;
1269         char whatuser[256];
1270
1271         char lastname[256];
1272         char firstname[256];
1273         char middlename[256];
1274         char prefix[256];
1275         char suffix[256];
1276         char pobox[256];
1277         char extadr[256];
1278         char street[256];
1279         char city[256];
1280         char state[256];
1281         char zipcode[256];
1282         char country[256];
1283         char hometel[256];
1284         char worktel[256];
1285         char faxtel[256];
1286         char mobiletel[256];
1287         char primary_inetemail[256];
1288         char other_inetemail[SIZ];
1289         char extrafields[SIZ];
1290         char fullname[256];
1291         char title[256];
1292         char org[256];
1293
1294         lastname[0] = 0;
1295         firstname[0] = 0;
1296         middlename[0] = 0;
1297         prefix[0] = 0;
1298         suffix[0] = 0;
1299         pobox[0] = 0;
1300         extadr[0] = 0;
1301         street[0] = 0;
1302         city[0] = 0;
1303         state[0] = 0;
1304         zipcode[0] = 0;
1305         country[0] = 0;
1306         hometel[0] = 0;
1307         worktel[0] = 0;
1308         faxtel[0] = 0;
1309         mobiletel[0] = 0;
1310         primary_inetemail[0] = 0;
1311         other_inetemail[0] = 0;
1312         title[0] = 0;
1313         org[0] = 0;
1314         extrafields[0] = 0;
1315         fullname[0] = 0;
1316
1317         safestrncpy(whatuser, "", sizeof whatuser);
1318
1319         if ((msgnum >= 0) || 
1320             ((VCMsg != NULL) && (VCAtt != NULL)))
1321         {
1322                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1323
1324                         Msg = (message_summary *) malloc(sizeof(message_summary));
1325                         memset(Msg, 0, sizeof(message_summary));
1326                         Msg->msgnum = msgnum;
1327                         VCMime = load_vcard(Msg);
1328                         if (VCMime == NULL) {
1329                                 convenience_page("770000", _("Error"), "");///TODO: important message
1330                                 DestroyMessageSummary(Msg);
1331                                 return;
1332                         }
1333                 
1334                         v = VCardLoad(VCMime->Data);
1335                 }
1336                 else {
1337                         v = VCardLoad(VCAtt->Data);
1338                 }
1339         
1340                 /* Populate the variables for our form */
1341                 i = 0;
1342                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
1343                         char prp[256];  /* property name */
1344                         char prm[256];  /* parameters */
1345
1346                         value = vcard_get_prop(v, "", 0, i++, 0);
1347
1348
1349                         extract_token(prp, key, 0, ';', sizeof prp);
1350                         safestrncpy(prm, key, sizeof prm);
1351                         remove_token(prm, 0, ';');
1352
1353                         if (!strcasecmp(prp, "n")) {
1354                                 extract_token(lastname, value, 0, ';', sizeof lastname);
1355                                 extract_token(firstname, value, 1, ';', sizeof firstname);
1356                                 extract_token(middlename, value, 2, ';', sizeof middlename);
1357                                 extract_token(prefix, value, 3, ';', sizeof prefix);
1358                                 extract_token(suffix, value, 4, ';', sizeof suffix);
1359                         }
1360
1361                         else if (!strcasecmp(prp, "fn")) {
1362                                 safestrncpy(fullname, value, sizeof fullname);
1363                         }
1364
1365                         else if (!strcasecmp(prp, "title")) {
1366                                 safestrncpy(title, value, sizeof title);
1367                         }
1368         
1369                         else if (!strcasecmp(prp, "org")) {
1370                                 safestrncpy(org, value, sizeof org);
1371                         }
1372         
1373                         else if (!strcasecmp(prp, "adr")) {
1374                                 extract_token(pobox, value, 0, ';', sizeof pobox);
1375                                 extract_token(extadr, value, 1, ';', sizeof extadr);
1376                                 extract_token(street, value, 2, ';', sizeof street);
1377                                 extract_token(city, value, 3, ';', sizeof city);
1378                                 extract_token(state, value, 4, ';', sizeof state);
1379                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
1380                                 extract_token(country, value, 6, ';', sizeof country);
1381                         }
1382
1383                         else if (!strcasecmp(prp, "tel")) {
1384
1385                                 if (bmstrcasestr(prm, "home")) {
1386                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1387                                 }
1388                                 else if (bmstrcasestr(prm, "work")) {
1389                                         extract_token(worktel, value, 0, ';', sizeof worktel);
1390                                 }
1391                                 else if (bmstrcasestr(prm, "fax")) {
1392                                         extract_token(faxtel, value, 0, ';', sizeof faxtel);
1393                                 }
1394                                 else if (bmstrcasestr(prm, "cell")) {
1395                                         extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
1396                                 }
1397                                 else {  /* Missing or unknown type; put it in the home phone */
1398                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1399                                 }
1400                         }
1401         
1402                         else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
1403                                 if (primary_inetemail[0] == 0) {
1404                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
1405                                 }
1406                                 else {
1407                                         if (other_inetemail[0] != 0) {
1408                                                 strcat(other_inetemail, "\n");
1409                                         }
1410                                         strcat(other_inetemail, value);
1411                                 }
1412                         }
1413
1414                         /* Unrecognized properties are preserved here so we don't discard them
1415                          * just because the vCard was edited with WebCit.
1416                          */
1417                         else {
1418                                 strcat(extrafields, key);
1419                                 strcat(extrafields, ":");
1420                                 strcat(extrafields, value);
1421                                 strcat(extrafields, "\n");
1422                         }
1423         
1424                 }
1425         
1426                 vcard_free(v);
1427         }
1428
1429         /* Display the form */
1430         output_headers(1, 1, 1, 0, 0, 0);
1431
1432         do_template("box_begin_1");
1433         StrBufAppendBufPlain(WC->WBuf, _("Edit contact information"), -1, 0);
1434         do_template("box_begin_2");
1435
1436         wc_printf("<form method=\"POST\" action=\"submit_vcard\">\n");
1437         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
1438
1439         if (force_room != NULL) {
1440                 wc_printf("<input type=\"hidden\" name=\"force_room\" value=\"");
1441                 escputs(force_room);
1442                 wc_printf("\">\n");
1443         }
1444         else
1445         {
1446                 wc_printf("<input type=\"hidden\" name=\"go\" value=\"");
1447                 StrEscAppend(WCC->WBuf, WCC->CurRoom.name, NULL, 0, 0);
1448                 wc_printf("\">\n");
1449         }
1450
1451         wc_printf("<table class=\"vcard_edit_background\"><tr><td>\n");
1452
1453         wc_printf("<table border=\"0\"><tr>"
1454                 "<td>%s</td>"
1455                 "<td>%s</td>"
1456                 "<td>%s</td>"
1457                 "<td>%s</td>"
1458                 "<td>%s</td></tr>\n",
1459                 _("Prefix"), _("First Name"), _("Middle Name"), _("Last Name"), _("Suffix")
1460         );
1461         wc_printf("<tr><td><input type=\"text\" name=\"prefix\" "
1462                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
1463                 prefix);
1464         wc_printf("<td><input type=\"text\" name=\"firstname\" "
1465                 "value=\"%s\" maxlength=\"29\"></td>",
1466                 firstname);
1467         wc_printf("<td><input type=\"text\" name=\"middlename\" "
1468                 "value=\"%s\" maxlength=\"29\"></td>",
1469                 middlename);
1470         wc_printf("<td><input type=\"text\" name=\"lastname\" "
1471                 "value=\"%s\" maxlength=\"29\"></td>",
1472                 lastname);
1473         wc_printf("<td><input type=\"text\" name=\"suffix\" "
1474                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
1475                 suffix);
1476
1477         wc_printf("<table  class=\"vcard_edit_background_alt\">");
1478         wc_printf("<tr><td>");
1479
1480         wc_printf(_("Display name:"));
1481         wc_printf("<br>"
1482                 "<input type=\"text\" name=\"fullname\" "
1483                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1484                 fullname
1485         );
1486
1487         wc_printf(_("Title:"));
1488         wc_printf("<br>"
1489                 "<input type=\"text\" name=\"title\" "
1490                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1491                 title
1492         );
1493
1494         wc_printf(_("Organization:"));
1495         wc_printf("<br>"
1496                 "<input type=\"text\" name=\"org\" "
1497                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1498                 org
1499         );
1500
1501         wc_printf("</td><td>");
1502
1503         wc_printf("<table border=\"0\">");
1504         wc_printf("<tr><td>");
1505         wc_printf(_("PO box:"));
1506         wc_printf("</td><td>"
1507                 "<input type=\"text\" name=\"pobox\" "
1508                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1509                 pobox);
1510         wc_printf("<tr><td>");
1511         wc_printf(_("Address:"));
1512         wc_printf("</td><td>"
1513                 "<input type=\"text\" name=\"extadr\" "
1514                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1515                 extadr);
1516         wc_printf("<tr><td> </td><td>"
1517                 "<input type=\"text\" name=\"street\" "
1518                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1519                 street);
1520         wc_printf("<tr><td>");
1521         wc_printf(_("City:"));
1522         wc_printf("</td><td>"
1523                 "<input type=\"text\" name=\"city\" "
1524                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1525                 city);
1526         wc_printf("<tr><td>");
1527         wc_printf(_("State:"));
1528         wc_printf("</td><td>"
1529                 "<input type=\"text\" name=\"state\" "
1530                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1531                 state);
1532         wc_printf("<tr><td>");
1533         wc_printf(_("ZIP code:"));
1534         wc_printf("</td><td>"
1535                 "<input type=\"text\" name=\"zipcode\" "
1536                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
1537                 zipcode);
1538         wc_printf("<tr><td>");
1539         wc_printf(_("Country:"));
1540         wc_printf("</td><td>"
1541                 "<input type=\"text\" name=\"country\" "
1542                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
1543                 country);
1544         wc_printf("</table>\n");
1545
1546         wc_printf("</table>\n");
1547
1548         wc_printf("<table border=0><tr><td>");
1549         wc_printf(_("Home telephone:"));
1550         wc_printf("</td>"
1551                 "<td><input type=\"text\" name=\"hometel\" "
1552                 "value=\"%s\" maxlength=\"29\"></td>\n",
1553                 hometel);
1554         wc_printf("<td>");
1555         wc_printf(_("Work telephone:"));
1556         wc_printf("</td>"
1557                 "<td><input type=\"text\" name=\"worktel\" "
1558                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1559                 worktel);
1560         wc_printf("<tr><td>");
1561         wc_printf(_("Mobile telephone:"));
1562         wc_printf("</td>"
1563                 "<td><input type=\"text\" name=\"mobiletel\" "
1564                 "value=\"%s\" maxlength=\"29\"></td>\n",
1565                 mobiletel);
1566         wc_printf("<td>");
1567         wc_printf(_("Fax number:"));
1568         wc_printf("</td>"
1569                 "<td><input type=\"text\" name=\"faxtel\" "
1570                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
1571                 faxtel);
1572
1573         wc_printf("<table class=\"vcard_edit_background_alt\">");
1574         wc_printf("<tr><td>");
1575
1576         wc_printf("<table border=0><TR>"
1577                 "<td valign=top>");
1578         wc_printf(_("Primary Internet e-mail address"));
1579         wc_printf("<br>"
1580                 "<input type=\"text\" name=\"primary_inetemail\" "
1581                 "size=40 maxlength=60 value=\"");
1582         escputs(primary_inetemail);
1583         wc_printf("\"><br>"
1584                 "</td><td valign=top>");
1585         wc_printf(_("Internet e-mail aliases"));
1586         wc_printf("<br>"
1587                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1588         escputs(other_inetemail);
1589         wc_printf("</textarea></td></tr></table>\n");
1590
1591         wc_printf("</td></tr></table>\n");
1592
1593         wc_printf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1594         escputs(extrafields);
1595         wc_printf("\">\n");
1596
1597         wc_printf("<input type=\"hidden\" name=\"return_to\" value=\"");
1598         escputs(return_to);
1599         wc_printf("\">\n");
1600
1601         wc_printf("<div class=\"buttons\">\n"
1602                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1603                 "&nbsp;"
1604                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1605                 "</div></form>\n",
1606                 _("Save changes"),
1607                 _("Cancel")
1608         );
1609         
1610         wc_printf("</td></tr></table>\n");
1611         do_template("box_end");
1612         wDumpContent(1);
1613         if (Msg != NULL) {
1614                 DestroyMessageSummary(Msg);
1615         }
1616 }
1617
1618
1619 /*
1620  *  commit the edits to the citadel server
1621  */
1622 void edit_vcard(void) {
1623         long msgnum;
1624         char *partnum;
1625
1626         msgnum = lbstr("msgnum");
1627         partnum = bstr("partnum");
1628         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1629 }
1630
1631
1632
1633 /*
1634  *  parse edited vcard from the browser
1635  */
1636 void submit_vcard(void) {
1637         struct vCard *v;
1638         char *serialized_vcard;
1639         char buf[SIZ];
1640         StrBuf *Buf;
1641         const StrBuf *ForceRoom;
1642         int i;
1643
1644         if (!havebstr("ok_button")) { 
1645                 readloop(readnew, eUseDefault);
1646                 return;
1647         }
1648
1649         if (havebstr("force_room")) {
1650                 ForceRoom = sbstr("force_room");
1651                 if (gotoroom(ForceRoom) != 200) {
1652                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1653                         AppendImportantMessage(HKEY(": "));
1654                         AppendImportantMessage(SKEY(ForceRoom));
1655                         AppendImportantMessage(HKEY("; "));
1656                         AppendImportantMessage(_("Aborting."), -1);
1657
1658                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1659                                 select_user_to_edit(NULL);
1660                         }
1661                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1662                                 do_welcome();
1663                         }
1664                         else if (!IsEmptyStr(bstr("return_to"))) {
1665                                 http_redirect(bstr("return_to"));
1666                         }
1667                         else {
1668                                 readloop(readnew, eUseDefault);
1669                         }
1670                         return;
1671                 }
1672         }
1673
1674         Buf = NewStrBuf();
1675         serv_write(HKEY("ENT0 1|||4\n"));
1676         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1677         {
1678                 edit_vcard();
1679                 return;
1680         }
1681
1682         /* Make a vCard structure out of the data supplied in the form */
1683         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1684                      bstr("extrafields")
1685         );
1686         v = VCardLoad(Buf);     /* Start with the extra fields */
1687         if (v == NULL) {
1688                 AppendImportantMessage(_("An error has occurred."), -1);
1689                 edit_vcard();
1690                 FreeStrBuf(&Buf);
1691                 return;
1692         }
1693
1694         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1695                 bstr("lastname"),
1696                 bstr("firstname"),
1697                 bstr("middlename"),
1698                 bstr("prefix"),
1699                 bstr("suffix") );
1700         vcard_add_prop(v, "n", buf);
1701         
1702         vcard_add_prop(v, "title", bstr("title"));
1703         vcard_add_prop(v, "fn", bstr("fullname"));
1704         vcard_add_prop(v, "org", bstr("org"));
1705
1706         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1707                 bstr("pobox"),
1708                 bstr("extadr"),
1709                 bstr("street"),
1710                 bstr("city"),
1711                 bstr("state"),
1712                 bstr("zipcode"),
1713                 bstr("country") );
1714         vcard_add_prop(v, "adr", buf);
1715
1716         vcard_add_prop(v, "tel;home", bstr("hometel"));
1717         vcard_add_prop(v, "tel;work", bstr("worktel"));
1718         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1719         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1720         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1721
1722         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1723                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1724                 if (!IsEmptyStr(buf)) {
1725                         vcard_add_prop(v, "email;internet", buf);
1726                 }
1727         }
1728
1729         serialized_vcard = vcard_serialize(v);
1730         vcard_free(v);
1731         if (serialized_vcard == NULL) {
1732                 AppendImportantMessage(_("An error has occurred."), -1);
1733                 edit_vcard();
1734                 FreeStrBuf(&Buf);
1735                 return;
1736         }
1737
1738         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1739         serv_write(HKEY("\n"));
1740         serv_printf("%s\r\n", serialized_vcard);
1741         serv_write(HKEY("000\n"));
1742         free(serialized_vcard);
1743
1744         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1745                 select_user_to_edit(NULL);
1746         }
1747         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1748                 do_welcome();
1749         }
1750         else if (!IsEmptyStr(bstr("return_to"))) {
1751                 http_redirect(bstr("return_to"));
1752         }
1753         else {
1754                 readloop(readnew, eUseDefault);
1755         }
1756         FreeStrBuf(&Buf);
1757 }
1758
1759
1760
1761 /*
1762  * Extract an embedded photo from a vCard for display on the client
1763  */
1764 void display_vcard_photo_img(void)
1765 {
1766         long msgnum = 0L;
1767         StrBuf *vcard;
1768         struct vCard *v;
1769         char *photosrc;
1770         const char *contentType;
1771         wcsession *WCC = WC;
1772
1773         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1774         
1775         vcard = load_mimepart(msgnum,"1");
1776         v = VCardLoad(vcard);
1777         
1778         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1779         FlushStrBuf(WCC->WBuf);
1780         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1781         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1782                 FlushStrBuf(WCC->WBuf);
1783                 
1784                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1785                 output_headers(0, 0, 0, 0, 0, 0);
1786                 hprintf("Content-Type: text/plain\r\n");
1787                 begin_burst();
1788                 wc_printf(_("Could Not decode vcard photo\n"));
1789                 end_burst();
1790                 return;
1791         }
1792         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1793         http_transmit_thing(contentType, 0);
1794         free(v);
1795         free(photosrc);
1796 }
1797
1798 typedef struct _vcardview_struct {
1799         long is_singlecard;
1800         addrbookent *addrbook;
1801         long num_ab;
1802
1803 } vcardview_struct;
1804
1805 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
1806                                  void **ViewSpecific, 
1807                                  long oper, 
1808                                  char *cmd, 
1809                                  long len,
1810                                  char *filter,
1811                                  long flen)
1812 {
1813         vcardview_struct *VS;
1814
1815         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1816         memset(VS, 0, sizeof(vcardview_struct));
1817         *ViewSpecific = (void*)VS;
1818
1819         VS->is_singlecard = ibstr("is_singlecard");
1820         if (VS->is_singlecard != 1) {
1821                 if (oper == do_search) {
1822                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1823                 }
1824                 else {
1825                         strcpy(cmd, "MSGS ALL");
1826                 }
1827                 Stat->maxmsgs = 9999999;
1828         }
1829         return 200;
1830 }
1831
1832 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
1833                             void **ViewSpecific, 
1834                             message_summary* Msg, 
1835                             int is_new, 
1836                             int i)
1837 {
1838         vcardview_struct *VS;
1839         char *ab_name;
1840
1841         VS = (vcardview_struct*) *ViewSpecific;
1842
1843         ab_name = NULL;
1844         fetch_ab_name(Msg, &ab_name);
1845         if (ab_name == NULL) 
1846                 return 0;
1847         ++VS->num_ab;
1848         VS->addrbook = realloc(VS->addrbook,
1849                                (sizeof(addrbookent) * VS->num_ab) );
1850         safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1851                     sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1852         VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1853         free(ab_name);
1854         return 0;
1855 }
1856
1857
1858 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1859 {
1860         const StrBuf *Mime;
1861         vcardview_struct *VS;
1862
1863         VS = (vcardview_struct*) *ViewSpecific;
1864         if (VS->is_singlecard)
1865                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1866         else
1867                 do_addrbook_view(VS->addrbook, VS->num_ab);     /* Render the address book */
1868         return 0;
1869 }
1870
1871 int vcard_Cleanup(void **ViewSpecific)
1872 {
1873         vcardview_struct *VS;
1874
1875         VS = (vcardview_struct*) *ViewSpecific;
1876         wDumpContent(1);
1877         if ((VS != NULL) && 
1878             (VS->addrbook != NULL))
1879                 free(VS->addrbook);
1880         if (VS != NULL) 
1881                 free(VS);
1882         return 0;
1883 }
1884
1885 void 
1886 ServerStartModule_VCARD
1887 (void)
1888 {
1889         ///VCToEnum = NewHash(0, NULL);
1890
1891 }
1892
1893 void 
1894 ServerShutdownModule_VCARD
1895 (void)
1896 {
1897         /// DeleteHash(&VCToEnum);
1898 }
1899
1900 void 
1901 InitModule_VCARD
1902 (void)
1903 {
1904         RegisterCTX(CTX_VCARD);
1905         RegisterCTX(CTX_VCARD_TYPE);
1906         RegisterReadLoopHandlerset(
1907                 VIEW_ADDRESSBOOK,
1908                 vcard_GetParamsGetServerCall,
1909                 NULL,
1910                 NULL,
1911                 NULL, 
1912                 vcard_LoadMsgFromServer,
1913                 vcard_RenderView_or_Tail,
1914                 vcard_Cleanup);
1915         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1916         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1917         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1918 /*
1919         Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1920         Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1921         Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1922         Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1923         Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1924         Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1925         Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1926         Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1927         Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1928         Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1929         Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1930         Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1931         Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1932         Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1933 */
1934 /*
1935         RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1936
1937         REGISTERTokenParamDefine(VC_n);
1938         REGISTERTokenParamDefine(VC_fn);
1939         REGISTERTokenParamDefine(VC_title);
1940         REGISTERTokenParamDefine(VC_org);
1941         REGISTERTokenParamDefine(VC_email);
1942         REGISTERTokenParamDefine(VC_tel);
1943         REGISTERTokenParamDefine(VC_work);
1944         REGISTERTokenParamDefine(VC_home);
1945         REGISTERTokenParamDefine(VC_cell);
1946         REGISTERTokenParamDefine(VC_adr);
1947         REGISTERTokenParamDefine(VC_photo);
1948         REGISTERTokenParamDefine(VC_version);
1949         REGISTERTokenParamDefine(VC_rev);
1950         REGISTERTokenParamDefine(VC_label);
1951 */
1952
1953         {
1954                 StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1955                 DefineToToken   = NewHash(1, lFlathash);
1956                 vcNames         = NewHash(1, lFlathash);
1957                 VCTokenToDefine = NewHash(1, NULL);
1958                 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0);
1959                 FreeStrBuf(&Prefix);
1960         }
1961         RegisterCTX(CTX_VCARD);
1962         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1963         RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1964         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1965         RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1966         REGISTERTokenParamDefine(FlatString);
1967         REGISTERTokenParamDefine(StringCluster);
1968         REGISTERTokenParamDefine(PhoneNumber);
1969         REGISTERTokenParamDefine(EmailAddr);
1970         REGISTERTokenParamDefine(Street);
1971         REGISTERTokenParamDefine(Number);
1972         REGISTERTokenParamDefine(AliasFor);
1973         REGISTERTokenParamDefine(Base64BinaryAttachment);
1974         REGISTERTokenParamDefine(TerminateList);
1975
1976         RegisterConditional("VC:HAVE:TYPE",      1, conditional_VC_Havetype, CTX_VCARD);
1977         RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1978         RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1979 }
1980