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