b3f2cba847d5cc58eb261a9ab213b91e90977af2
[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
363 /*
364  * Helper function for do_addrbook_view()
365  * Converts a name into a three-letter tab label
366  */
367 void nametab(char *tabbuf, long len, char *name) {
368         stresc(tabbuf, len, name, 0, 0);
369         tabbuf[0] = toupper(tabbuf[0]);
370         tabbuf[1] = tolower(tabbuf[1]);
371         tabbuf[2] = tolower(tabbuf[2]);
372         tabbuf[3] = 0;
373 }
374
375 wc_mime_attachment *load_vcard(message_summary *Msg) 
376 {
377         HashPos  *it;
378         StrBuf *FoundCharset = NewStrBuf();
379         StrBuf *Error;
380         void *vMime;
381         const char *Key;
382         long len;
383         wc_mime_attachment *Mime;
384         wc_mime_attachment *VCMime = NULL;
385
386         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
387         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
388         Msg->MsgBody->msgnum = Msg->msgnum;
389
390         load_message(Msg, FoundCharset, &Error);
391
392         FreeStrBuf(&FoundCharset);
393         /* look up the vcard... */
394         it = GetNewHashPos(Msg->AllAttach, 0);
395         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
396                (vMime != NULL)) 
397         {
398                 Mime = (wc_mime_attachment*) vMime;
399                 if ((strcmp(ChrPtr(Mime->ContentType),
400                            "text/x-vcard") == 0) ||
401                     (strcmp(ChrPtr(Mime->ContentType),
402                             "text/vcard") == 0))
403                 {
404                         VCMime = Mime;
405                         break;
406                 }
407         }
408         DeleteHashPos(&it);
409         if (VCMime == NULL)
410                 return NULL;
411
412         if (VCMime->Data == NULL)
413                 MimeLoadData(VCMime);
414         return VCMime;
415 }
416
417
418
419 /*
420  * Turn a vCard "n" (name) field into something displayable.
421  */
422 void vcard_n_prettyize(char *name)
423 {
424         char *original_name;
425         int i, j, len;
426
427         original_name = strdup(name);
428         len = strlen(original_name);
429         for (i=0; i<5; ++i) {
430                 if (len > 0) {
431                         if (original_name[len-1] == ' ') {
432                                 original_name[--len] = 0;
433                         }
434                         if (original_name[len-1] == ';') {
435                                 original_name[--len] = 0;
436                         }
437                 }
438         }
439         strcpy(name, "");
440         j=0;
441         for (i=0; i<len; ++i) {
442                 if (original_name[i] == ';') {
443                         name[j++] = ',';
444                         name[j++] = ' ';                        
445                 }
446                 else {
447                         name[j++] = original_name[i];
448                 }
449         }
450         name[j] = '\0';
451         free(original_name);
452 }
453
454
455 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
456 {
457         /* if we have some untagged QP, detect it here. */
458         if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
459                 StrBuf *b;
460                 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
461                 b = ThisFieldStr;
462                 ThisFieldStr = Swap; 
463                 Swap = b;
464                 FlushStrBuf(Swap);
465         }
466         Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
467 }
468 /*
469  * html print a vcard
470  * display_vcard() calls this after parsing the textual vCard into
471  * our 'struct vCard' data object.
472  *
473  * Set 'full' to nonzero to display the full card, otherwise it will only
474  * show a summary line.
475  *
476  * This code is a bit ugly, so perhaps an explanation is due: we do this
477  * in two passes through the vCard fields.  On the first pass, we process
478  * fields we understand, and then render them in a pretty fashion at the
479  * end.  Then we make a second pass, outputting all the fields we don't
480  * understand in a simple two-column name/value format.
481  * v            the vCard to parse
482  * msgnum       Citadel message pointer
483  */
484 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
485 {
486         StrBuf *Val = NULL;
487         StrBuf *Swap = NULL;
488         int i, j, k;
489         char buf[SIZ];
490         int is_qp = 0;
491         int is_b64 = 0;
492         int ntokens, len;
493         StrBuf *thisname = NULL;
494         char firsttoken[SIZ];
495         StrBuf *thisVCToken;
496         void *vField = NULL;
497
498         Swap = NewStrBuf ();
499         thisname = NewStrBuf();
500         thisVCToken = NewStrBufPlain(NULL, 63);
501         for (i=0; i<(v->numprops); ++i) {
502                 FlushStrBuf(thisVCToken);
503                 is_qp = 0;
504                 is_b64 = 0;
505                 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
506                 StrBufPlain(thisname, v->prop[i].name, -1);
507                 StrBufLowerCase(thisname);
508                 
509                 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
510                 ntokens = num_tokens(ChrPtr(thisname), ';');
511                 for (j=0, k=0; j < ntokens && k < 10; ++j) {
512                         /*int evc[10];*/
513                         
514                         len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
515                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
516                                 is_qp = 1;
517 /*                              remove_token(thisname, j, ';');*/
518                         }
519                         else if (!strcasecmp(buf, "encoding=base64")) {
520                                 is_b64 = 1;
521 /*                              remove_token(thisname, j, ';');*/
522                         }
523                         else{
524                                 if (StrLength(thisVCToken) > 0) {
525                                         StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
526                                 }
527                                 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
528                                 /*
529                                 if (GetHash(VCToEnum, buf, len, &V))
530                                 {
531                                         evc[k] = (int) V;
532
533                                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
534
535                                         syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
536                                         k++;
537                                 }
538 */
539
540                         }
541                 }
542
543                 vField = NULL;  
544                 if ((StrLength(thisVCToken) > 0) &&
545                     GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) && 
546                     (vField != NULL)) {
547                         vcField *thisField = (vcField *)vField;
548                         StrBuf *ThisFieldStr = NULL;
549                         syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
550                         switch (thisField->Type) {
551                         case StringCluster: {
552                                 int j = 0;
553                                 const char *Pos = NULL;
554                                 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
555                                 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
556                                 while (thisField->Sub[j].STR.len > 0) {
557                                         StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
558                                         ThisFieldStr = NewStrBufDup(Buf);
559                                         
560                                         PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
561                                         j++;
562                                 }
563                                 FreeStrBuf(&thisArray);
564                                 FreeStrBuf(&Buf);
565                         }
566                                 break;
567                         case Address:
568                         case FlatString:
569                         case PhoneNumber:
570                         case EmailAddr:
571                         case Street:
572                         case Number:
573                         case AliasFor:
574                                 /* copy over the payload into a StrBuf */
575                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
576                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
577
578                                 break;
579                         case Base64BinaryAttachment:
580                         case TerminateList:
581                         case UnKnown:
582                                 break;
583                         }
584
585                 }
586                 else if (StrLength(thisVCToken) > 0) {
587                         /* Add it to the UNKNOWN field... */
588                         void *pv = NULL;
589                         StrBuf *oldVal;
590                         GetHash(VC, IKEY(vcfUnknown->cval), &pv);
591                         oldVal = (StrBuf*) pv;
592                         if (oldVal == NULL) {
593                                 oldVal = NewStrBuf();
594                                 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
595                         }
596                         else {
597                                 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
598                         }
599
600                         StrBufAppendBuf(oldVal, thisVCToken, 0);
601                         StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
602                         StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
603                         continue;
604                 }
605
606                 /* copy over the payload into a StrBuf */
607                 Val = NewStrBufPlain(v->prop[i].value, -1);
608                         
609                 /* if we have some untagged QP, detect it here. */
610                 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
611                         StrBuf *b;
612                         StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
613                         b = Val;
614                         Val = Swap; 
615                         Swap = b;
616                         FlushStrBuf(Swap);
617                 }
618                 else if (is_b64) {
619                         StrBufDecodeBase64(Val);
620                 }
621 #if 0
622                 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
623                         firsttoken,
624                        ChrPtr(thisname),
625                         ChrPtr(Val),
626                         v->prop[i].value);
627 #endif  
628                 FreeStrBuf(&Val);
629         }
630         FreeStrBuf(&thisname);
631         FreeStrBuf(&Swap);
632         FreeStrBuf(&thisVCToken);
633 }
634
635 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
636 {
637         HashList *VC = CTX(CTX_VCARD);
638         int evc;
639         void *vStr;
640
641         evc = GetTemplateTokenNumber(Target, TP, 0, -1);
642         if (evc != -1)
643         {
644                 if (GetHash(VC, IKEY(evc), &vStr))
645                 {
646                         StrBufAppendTemplate(Target, TP,
647                                              (StrBuf*) vStr,
648                                              1);
649                 }
650         }
651         
652 }
653
654 void display_one_vcard (StrBuf *Target, HashList *VC, const char *tp_name, size_t tp_name_len)
655 {
656         WCTemplputParams *TP = NULL;
657         WCTemplputParams SubTP;
658
659         memset(&SubTP, 0, sizeof(WCTemplputParams));    
660         StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
661
662         DoTemplate(tp_name, tp_name_len, Target, &SubTP);
663         UnStackContext(&SubTP);
664 }
665
666 /*
667  * Extract an embedded photo from a vCard for display on the client
668  */
669 void display_vcard_photo_img(void)
670 {
671         long msgnum = 0L;
672         StrBuf *vcard;
673         struct vCard *v;
674         char *photosrc;
675         const char *contentType;
676         wcsession *WCC = WC;
677
678         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
679         
680         vcard = load_mimepart(msgnum,"1");
681         v = VCardLoad(vcard);
682         
683         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
684         FlushStrBuf(WCC->WBuf);
685         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
686         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
687                 FlushStrBuf(WCC->WBuf);
688                 
689                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
690                 output_headers(0, 0, 0, 0, 0, 0);
691                 hprintf("Content-Type: text/plain\r\n");
692                 begin_burst();
693                 wc_printf(_("Could Not decode vcard photo\n"));
694                 end_burst();
695                 return;
696         }
697         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
698         http_transmit_thing(contentType, 0);
699         free(v);
700         free(photosrc);
701 }
702
703 /*
704  * Edit the vCard component of a MIME message.  
705  * Supply the message number
706  * and MIME part number to fetch.  Or, specify -1 for the message number
707  * to start with a blank card.
708  */
709 void do_edit_vcard(long msgnum, char *partnum, 
710                    message_summary *VCMsg,
711                    wc_mime_attachment *VCAtt,
712                    const char *return_to, 
713                    const char *force_room) {
714         HashList *VC;   WCTemplputParams SubTP;
715         wcsession *WCC = WC;
716         message_summary *Msg = NULL;
717         wc_mime_attachment *VCMime = NULL;
718         struct vCard *v;
719         char whatuser[256];
720         VC = NewHash(0, lFlathash);
721
722         /* Display the form */
723         output_headers(1, 1, 1, 0, 0, 0);
724
725         safestrncpy(whatuser, "", sizeof whatuser);
726
727         if ((msgnum >= 0) || 
728             ((VCMsg != NULL) && (VCAtt != NULL)))
729         {
730                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
731
732                         Msg = (message_summary *) malloc(sizeof(message_summary));
733                         memset(Msg, 0, sizeof(message_summary));
734                         Msg->msgnum = msgnum;
735                         VCMime = load_vcard(Msg);
736                         if (VCMime == NULL) {
737                                 convenience_page("770000", _("Error"), "");/*TODO: important message*/
738                                 DestroyMessageSummary(Msg);
739                                 return;
740                         }
741                 
742                         v = VCardLoad(VCMime->Data);
743                 }
744                 else {
745                         v = VCardLoad(VCAtt->Data);
746                 }
747
748                 parse_vcard(WCC->WBuf, v, VC, NULL);
749         
750         
751                 vcard_free(v);
752         }
753
754         memset(&SubTP, 0, sizeof(WCTemplputParams));    
755
756         {
757                 WCTemplputParams *TP = NULL;
758                 WCTemplputParams SubTP;
759                 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
760
761                 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
762                 UnStackContext(&SubTP);
763         }
764         DeleteHash(&VC);
765
766
767         wDumpContent(1);
768         if (Msg != NULL) {
769                 DestroyMessageSummary(Msg);
770         }
771 }
772
773
774 /*
775  *  commit the edits to the citadel server
776  */
777 void edit_vcard(void) {
778         long msgnum;
779         char *partnum;
780
781         msgnum = lbstr("msgnum");
782         partnum = bstr("partnum");
783         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
784 }
785
786
787
788 /*
789  *  parse edited vcard from the browser
790  */
791 void submit_vcard(void) {
792         struct vCard *v;
793         char *serialized_vcard;
794         StrBuf *Buf;
795         const StrBuf *ForceRoom;
796         HashList* postVcard;
797         HashPos *it, *itSub;
798         const char *Key;
799         long len;
800         void *pv;
801         StrBuf *SubStr;
802         const StrBuf *s;
803         const char *Pos = NULL;
804
805         if (!havebstr("ok_button")) { 
806                 readloop(readnew, eUseDefault);
807                 return;
808         }
809
810         if (havebstr("force_room")) {
811                 ForceRoom = sbstr("force_room");
812                 if (gotoroom(ForceRoom) != 200) {
813                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
814                         AppendImportantMessage(HKEY(": "));
815                         AppendImportantMessage(SKEY(ForceRoom));
816                         AppendImportantMessage(HKEY("; "));
817                         AppendImportantMessage(_("Aborting."), -1);
818
819                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
820                                 select_user_to_edit(NULL);
821                         }
822                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
823                                 do_welcome();
824                         }
825                         else if (!IsEmptyStr(bstr("return_to"))) {
826                                 http_redirect(bstr("return_to"));
827                         }
828                         else {
829                                 readloop(readnew, eUseDefault);
830                         }
831                         return;
832                 }
833         }
834
835         postVcard = getSubStruct(HKEY("VC"));
836         if (postVcard == NULL) {
837                 AppendImportantMessage(_("An error has occurred."), -1);
838                 edit_vcard();
839                 return;/*/// more details*/
840         }
841         
842         Buf = NewStrBuf();
843         serv_write(HKEY("ENT0 1|||4\n"));
844         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
845         {
846                 edit_vcard();
847                 return;
848         }
849         
850         /* Make a vCard structure out of the data supplied in the form */
851         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
852                      bstr("extrafields")
853         );
854         v = VCardLoad(Buf);     /* Start with the extra fields */
855         if (v == NULL) {
856                 AppendImportantMessage(_("An error has occurred."), -1);
857                 edit_vcard();
858                 FreeStrBuf(&Buf);
859                 return;
860         }
861
862         SubStr = NewStrBuf();
863         it = GetNewHashPos(DefineToToken, 0);
864         while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) && 
865                (pv != NULL)) 
866         {
867                 char buf[32];
868                 long blen;
869                 vcField *t = (vcField*) pv;
870
871                 if (t->Sub != NULL){
872                         vcField *Sub;
873                         FlushStrBuf(SubStr);
874                         itSub = GetNewHashPos(DefineToToken, 0);
875                         while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) && 
876                                (pv != NULL)) 
877                         {
878                                 Sub = (vcField*) pv;
879                                 if (Sub->parentCVal == t->cval) {
880                                         if (StrLength(SubStr) > 0)
881                                                 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
882
883
884
885                                         blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
886                                         s = SSubBstr(postVcard, buf, blen);
887                         
888                                         if ((s != NULL) && (StrLength(s) > 0)) {
889                                                 /// todo: utf8 qp
890                                                 StrBufAppendBuf(SubStr, s, 0);
891                                         }
892                                 }
893                         }
894                         if (StrLength(SubStr) > 0) {
895                                 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
896                         }
897                         DeleteHashPos(&itSub);
898                 }
899                 else if (t->parentCVal == 0) {
900                         blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
901                         s = SSubBstr(postVcard, buf, blen);
902                         
903                         if ((s != NULL) && (StrLength(s) > 0)) {
904                                 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
905                         }
906                 }
907         }
908         DeleteHashPos(&it);
909
910         s = sbstr("other_inetemail");
911         if (StrLength(s) > 0) {
912                 FlushStrBuf(SubStr);
913                 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
914                         if (StrLength(SubStr) > 0) {
915                                 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
916                         }
917                 }
918         }
919
920         FreeStrBuf(&SubStr);
921
922
923         serialized_vcard = vcard_serialize(v);
924         vcard_free(v);
925         if (serialized_vcard == NULL) {
926                 AppendImportantMessage(_("An error has occurred."), -1);
927                 edit_vcard();
928                 FreeStrBuf(&Buf);
929                 return;
930         }
931
932         printf("%s", serialized_vcard);
933         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
934         serv_write(HKEY("\n"));
935         serv_printf("%s\r\n", serialized_vcard);
936         serv_write(HKEY("000\n"));
937         free(serialized_vcard);
938
939         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
940                 select_user_to_edit(NULL);
941         }
942         else if (!strcmp(bstr("return_to"), "do_welcome")) {
943                 do_welcome();
944         }
945         else if (!IsEmptyStr(bstr("return_to"))) {
946                 http_redirect(bstr("return_to"));
947         }
948         else {
949                 readloop(readnew, eUseDefault);
950         }
951         FreeStrBuf(&Buf);
952 }
953
954
955
956 /*
957  * Address book entry (keep it short and sweet, it's just a quickie lookup
958  * which we can use to get to the real meat and bones later)
959  */
960 typedef struct _addrbookent {
961         StrBuf *name;
962         HashList *VC;
963         long ab_msgnum;         /* message number of address book entry */
964 } addrbookent;
965
966 void deleteAbEnt(void *v) {
967         addrbookent *vc = (addrbookent*)v;
968         DeleteHash(&vc->VC);
969         FreeStrBuf(&vc->name);
970 }
971
972 typedef struct _vcardview_struct {
973         long is_singlecard;
974         HashList *addrbook;
975
976 } vcardview_struct;
977
978 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
979                                  void **ViewSpecific, 
980                                  long oper, 
981                                  char *cmd, 
982                                  long len,
983                                  char *filter,
984                                  long flen)
985 {
986         vcardview_struct *VS;
987
988         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
989         memset(VS, 0, sizeof(vcardview_struct));
990         *ViewSpecific = (void*)VS;
991
992         VS->is_singlecard = ibstr("is_singlecard");
993         if (VS->is_singlecard != 1) {
994                 VS->addrbook = NewHash(0, NULL);
995                 if (oper == do_search) {
996                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
997                 }
998                 else {
999                         strcpy(cmd, "MSGS ALL");
1000                 }
1001                 Stat->maxmsgs = 9999999;
1002         }
1003         return 200;
1004 }
1005
1006 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
1007                             void **ViewSpecific, 
1008                             message_summary* Msg, 
1009                             int is_new, 
1010                             int i)
1011 {
1012         vcardview_struct *VS;
1013         wc_mime_attachment *VCMime = NULL;
1014         struct vCard *v;
1015         addrbookent* abEntry;
1016
1017         VS = (vcardview_struct*) *ViewSpecific;
1018
1019         VCMime = load_vcard(Msg);
1020         if (VCMime == NULL)
1021                 return 0;
1022
1023         v = VCardLoad(VCMime->Data);
1024
1025         if (v == NULL) return 0;
1026
1027         abEntry = (addrbookent*) malloc(sizeof(addrbookent));
1028         memset(abEntry, 0, sizeof(addrbookent));
1029         abEntry->name = NewStrBuf();
1030         abEntry->VC = NewHash(0, lFlathash);
1031         abEntry->ab_msgnum = Msg->msgnum;
1032         parse_vcard(WC->WBuf, v, abEntry->VC, VCMime);
1033
1034         display_one_vcard(abEntry->name, abEntry->VC, HKEY("vcard_list_name"));
1035
1036         if (StrLength(abEntry->name) == 0) {
1037                 StrBufPlain(abEntry->name, _("(no name)"), -1);
1038         }
1039
1040         vcard_free(v);
1041         
1042         Put(VS->addrbook, SKEY(abEntry->name), abEntry, deleteAbEnt);
1043         return 0;
1044 }
1045
1046
1047 /*
1048  * Render the address book using info we gathered during the scan
1049  *
1050  * addrbook     the addressbook to render
1051  * num_ab       the number of the addressbook
1052  */
1053 void do_addrbook_view(vcardview_struct* VS) {
1054         long i = 0;
1055         int displayed = 0;
1056         int bg = 0;
1057         static int NAMESPERPAGE = 60;
1058         int num_pages = 0;
1059         int tabfirst = 0;
1060         int tablast = 0;
1061         int page = 0;
1062         char **tablabels;
1063         int num_ab = GetCount(VS->addrbook);
1064         HashList *headlines;
1065         HashPos *it;
1066         wcsession *WCC = WC;
1067
1068         WCTemplputParams *TP = NULL;
1069         WCTemplputParams SubTP;
1070
1071         memset(&SubTP, 0, sizeof(WCTemplputParams));    
1072         
1073         if (num_ab == 0) {
1074                 do_template("vcard_list_empty");
1075                 return;
1076         }
1077
1078         if (num_ab > 1) {
1079                 ///SortByHashKey(VS->addrbook, 0);
1080         }
1081
1082         num_pages = (GetCount(VS->addrbook) / NAMESPERPAGE) + 1;
1083
1084         tablabels = malloc(num_pages * sizeof (char *));
1085         if (tablabels == NULL) {
1086                 return;
1087         }
1088
1089         headlines = NewHash(0, lFlathash);
1090         for (i=0; i<num_pages; ++i) {
1091                 void *v1 = NULL;
1092                 void *v2 = NULL;
1093                 long hklen1, hklen2;
1094                 const char *c1, *c2;
1095                 StrBuf *headline;
1096                 addrbookent *a1, *a2;
1097
1098                 tabfirst = i * NAMESPERPAGE;
1099                 tablast = tabfirst + NAMESPERPAGE - 1;
1100                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
1101
1102                 headline = NewStrBufPlain(NULL, StrLength(v1) + StrLength(v2) + 10);
1103                 if (GetHashAt(VS->addrbook, tabfirst, &hklen1, &c1, &v1)) {
1104                         a1 = (addrbookent*) v1;
1105                         StrBufAppendBuf(headline, a1->name, 0);
1106
1107                         if (GetHashAt(VS->addrbook, tablast, &hklen2, &c2, &v2)) {
1108
1109                                 a2 = (addrbookent*) v2;
1110                                 StrBufAppendBufPlain(headline, HKEY(" - "), 0);
1111                                 StrBufAppendBuf(headline, a2->name, 0);
1112                         }
1113                 }
1114                 Put(headlines, LKEY(i), headline, HFreeStrBuf);
1115         }
1116
1117         tabbed_dialog(num_pages, tablabels);
1118         page = (-1);
1119
1120         it = GetNewHashPos(VS->addrbook, 0);
1121         for (i=0; i<num_ab; ++i) {
1122                 void *v;
1123                 long hklen;
1124                 const char *key;
1125                 addrbookent *abEnt;
1126                 GetNextHashPos(VS->addrbook, it, &hklen, &key, &v);
1127                 if (v == NULL)
1128                         continue;
1129                 abEnt = (addrbookent *) v;
1130                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
1131                         page = (i / NAMESPERPAGE);
1132                         if (page > 0) {
1133                                 do_template("vcard_list_section_end");
1134                                 end_tab(page-1, num_pages);
1135                         }
1136                         begin_tab(page, num_pages);
1137                         do_template("vcard_list_section_start");
1138                         displayed = 0;
1139                 }
1140
1141                 if ((displayed % 4) == 0) {
1142                         if (displayed > 0) {
1143                                 do_template("vcard_list_row_end");
1144                         }
1145                         do_template("vcard_list_row_start");
1146                         bg = 1 - bg;
1147                 }
1148         
1149
1150                 StackContext(TP, &SubTP, abEnt->VC, CTX_VCARD, 0, NULL);
1151
1152                 DoTemplate(HKEY("vcard_list_entry"), WCC->WBuf, &SubTP);
1153                 UnStackContext(&SubTP);
1154
1155                 ++displayed;
1156         }
1157         DeleteHashPos(&it);
1158
1159         /* Placeholders for empty columns at end */
1160         if ((num_ab % 4) != 0) {
1161                 for (i=0; i<(4-(num_ab % 4)); ++i) {
1162                         do_template("vcard_list_cell_end");
1163                 }
1164         }
1165         
1166         do_template("vcard_list_section_end");
1167         end_tab((num_pages-1), num_pages);
1168
1169         begin_tab(num_pages, num_pages);
1170         /* FIXME there ought to be something here */
1171         end_tab(num_pages, num_pages);
1172
1173         DeleteHash(&headlines);
1174         wDumpContent(1);
1175 }
1176
1177
1178
1179 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1180 {
1181         const StrBuf *Mime;
1182         vcardview_struct *VS;
1183
1184         VS = (vcardview_struct*) *ViewSpecific;
1185         if (VS->is_singlecard)
1186                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1187         else
1188                 do_addrbook_view(VS);   /* Render the address book */
1189         return 0;
1190 }
1191
1192 int vcard_Cleanup(void **ViewSpecific)
1193 {
1194         vcardview_struct *VS;
1195
1196         VS = (vcardview_struct*) *ViewSpecific;
1197         wDumpContent(1);
1198         if ((VS != NULL) && 
1199             (VS->addrbook != NULL))
1200                 DeleteHash(&VS->addrbook);
1201         if (VS != NULL) 
1202                 free(VS);
1203
1204         return 0;
1205 }
1206
1207 void render_MIME_VCard(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundCharset)
1208 {
1209         wc_mime_attachment *Mime = (wc_mime_attachment *) CTX(CTX_MIME_ATACH);
1210         wcsession *WCC = WC;
1211         if (StrLength(Mime->Data) == 0)
1212                 MimeLoadData(Mime);
1213         if (StrLength(Mime->Data) > 0) {
1214                 struct vCard *v;
1215                 StrBuf *Buf;
1216
1217                 Buf = NewStrBuf();
1218                 /** If it's my vCard I can edit it */
1219                 if (    (!strcasecmp(ChrPtr(WCC->CurRoom.name), USERCONFIGROOM))
1220                         || ((StrLength(WCC->CurRoom.name) > 11) &&
1221                             (!strcasecmp(&(ChrPtr(WCC->CurRoom.name)[11]), USERCONFIGROOM)))
1222                         || (WCC->CurRoom.view == VIEW_ADDRESSBOOK)
1223                         ) {
1224                         StrBufAppendPrintf(Buf, "<a href=\"edit_vcard?msgnum=%ld?partnum=%s\">",
1225                                 Mime->msgnum, ChrPtr(Mime->PartNum));
1226                         StrBufAppendPrintf(Buf, "[%s]</a>", _("edit"));
1227                 }
1228
1229                 /* In all cases, display the full card */
1230
1231                 v = VCardLoad(Mime->Data);
1232
1233                 if (v != NULL) {
1234                         HashList *VC;
1235                         
1236                         VC = NewHash(0, lFlathash);
1237                         parse_vcard(Target, v, VC, Mime);
1238                         display_one_vcard (Target, VC, HKEY("vcard_msg_display"));
1239                         DeleteHash(&VC);
1240
1241                 }
1242                 else {
1243                         StrBufPlain(Buf, _("failed to load vcard"), -1);
1244                 }
1245                 FreeStrBuf(&Mime->Data);
1246                 Mime->Data = Buf;
1247         }
1248
1249 }
1250
1251 void 
1252 ServerStartModule_VCARD
1253 (void)
1254 {
1255         ///VCToEnum = NewHash(0, NULL);
1256
1257 }
1258
1259 void 
1260 ServerShutdownModule_VCARD
1261 (void)
1262 {
1263         DeleteHash(&DefineToToken);
1264         DeleteHash(&vcNames);
1265         DeleteHash(&VCTokenToDefine);
1266         /// DeleteHash(&VCToEnum);
1267 }
1268
1269 void 
1270 InitModule_VCARD
1271 (void)
1272 {
1273         StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1274         DefineToToken   = NewHash(1, lFlathash);
1275         vcNames         = NewHash(1, lFlathash);
1276         VCTokenToDefine = NewHash(1, NULL);
1277         autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1278         FreeStrBuf(&Prefix);
1279         RegisterCTX(CTX_VCARD);
1280         RegisterCTX(CTX_VCARD_TYPE);
1281         RegisterReadLoopHandlerset(
1282                 VIEW_ADDRESSBOOK,
1283                 vcard_GetParamsGetServerCall,
1284                 NULL,
1285                 NULL,
1286                 NULL, 
1287                 vcard_LoadMsgFromServer,
1288                 vcard_RenderView_or_Tail,
1289                 vcard_Cleanup);
1290         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1291         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1292         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1293
1294         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1295         RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1296         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1297         RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1298         REGISTERTokenParamDefine(FlatString);
1299         REGISTERTokenParamDefine(StringCluster);
1300         REGISTERTokenParamDefine(PhoneNumber);
1301         REGISTERTokenParamDefine(EmailAddr);
1302         REGISTERTokenParamDefine(Street);
1303         REGISTERTokenParamDefine(Number);
1304         REGISTERTokenParamDefine(AliasFor);
1305         REGISTERTokenParamDefine(Base64BinaryAttachment);
1306         REGISTERTokenParamDefine(TerminateList);
1307         REGISTERTokenParamDefine(Address);
1308
1309         RegisterConditional("VC:HAVE:TYPE",      1, conditional_VC_Havetype, CTX_VCARD);
1310         RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1311         RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1312
1313         RegisterMimeRenderer(HKEY("text/x-vcard"), render_MIME_VCard, 1, 201);
1314         RegisterMimeRenderer(HKEY("text/vcard"), render_MIME_VCard, 1, 200);
1315 }