Suppress stray display of contact names over address book tabs. Still need to fix...
[citadel.git] / webcit / vcard_edit.c
1 /*
2  * Copyright (c) 1996-2017 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_LIST = CTX_NONE;
19 CtxType CTX_VCARD_TYPE = CTX_NONE;
20 long VCEnumCounter = 0;
21
22 typedef enum _VCStrEnum {
23         FlatString,
24         StringCluster,
25         PhoneNumber,
26         EmailAddr,
27         Address,
28         Street,
29         Number,
30         AliasFor,
31         Base64BinaryAttachment,
32         UnKnown,
33         TerminateList
34 }VCStrEnum;
35 typedef struct vcField vcField;
36 struct vcField {
37         ConstStr STR;
38         VCStrEnum Type;
39         vcField *Sub;
40         long cval;
41         long parentCVal;
42         ConstStr Name;
43 };
44
45 vcField VCStr_Ns [] = {
46         {{HKEY("last")},   FlatString,    NULL, 0, 0, {HKEY("Last Name")}},
47         {{HKEY("first")},  FlatString,    NULL, 0, 0, {HKEY("First Name")}},
48         {{HKEY("middle")}, FlatString,    NULL, 0, 0, {HKEY("Middle Name")}},
49         {{HKEY("prefix")}, FlatString,    NULL, 0, 0, {HKEY("Prefix")}},
50         {{HKEY("suffix")}, FlatString,    NULL, 0, 0, {HKEY("Suffix")}},
51         {{HKEY("")},       TerminateList, NULL, 0, 0, {HKEY("")}}
52 };
53
54 vcField VCStr_Addrs [] = {
55         {{HKEY("POBox")},    Address,       NULL, 0, 0, {HKEY("PO box")}},
56         {{HKEY("extadr")},   Address,       NULL, 0, 0, {HKEY("Address")}},
57         {{HKEY("street")},   Address,       NULL, 0, 0, {HKEY("")}},
58         {{HKEY("city")},     Address,       NULL, 0, 0, {HKEY("City")}},
59         {{HKEY("state")},    Address,       NULL, 0, 0, {HKEY("State")}},
60         {{HKEY("zip")},      Address,       NULL, 0, 0, {HKEY("ZIP code")}},
61         {{HKEY("country")},  Address,       NULL, 0, 0, {HKEY("Country")}},
62         {{HKEY("")},         TerminateList, NULL, 0, 0, {HKEY("")}}
63 };
64
65 vcField VCStrE [] = {
66         {{HKEY("version")},         Number,                 NULL,        0, 0, {HKEY("")}},
67         {{HKEY("rev")},             Number,                 NULL,        0, 0, {HKEY("")}},
68         {{HKEY("label")},           FlatString,             NULL,        0, 0, {HKEY("")}},
69         {{HKEY("uid")},             FlatString,             NULL,        0, 0, {HKEY("")}},
70         {{HKEY("n")},               StringCluster,          VCStr_Ns,    0, 0, {HKEY("")}}, /* N is name, but only if there's no FN already there */
71         {{HKEY("fn")},              FlatString,             NULL,        0, 0, {HKEY("")}}, /* FN (full name) is a true 'display name' field */
72         {{HKEY("title")},           FlatString,             NULL,        0, 0, {HKEY("Title:")}},
73         {{HKEY("org")},             FlatString,             NULL,        0, 0, {HKEY("Organization:")}},/* organization */
74         {{HKEY("email")},           EmailAddr,              NULL,        0, 0, {HKEY("E-mail:")}},
75         {{HKEY("tel")},             PhoneNumber,            NULL,        0, 0, {HKEY("Telephone:")}},
76         {{HKEY("adr")},             StringCluster,          VCStr_Addrs, 0, 0, {HKEY("Address:")}},
77         {{HKEY("photo")},           Base64BinaryAttachment, NULL,        0, 0, {HKEY("Photo:")}},
78         {{HKEY("tel;home")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (home)")}},
79         {{HKEY("tel;work")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (work)")}},
80         {{HKEY("tel;fax")},         PhoneNumber,            NULL,        0, 0, {HKEY(" (fax)")}},
81         {{HKEY("tel;cell")},        PhoneNumber,            NULL,        0, 0, {HKEY(" (cell)")}},
82         {{HKEY("email;internet")},  EmailAddr,              NULL,        0, 0, {HKEY("E-mail:")}},
83         {{HKEY("UNKNOWN")},         UnKnown,                NULL,        0, 0, {HKEY("")}},
84         {{HKEY("")},                TerminateList,          NULL,        0, 0, {HKEY("")}}
85 };
86
87 ConstStr VCStr [] = {
88         {HKEY("")},
89         {HKEY("n")}, /* N is name, but only if there's no FN already there */
90         {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
91         {HKEY("title")},   /* title */
92         {HKEY("org")},    /* organization */
93         {HKEY("email")},
94         {HKEY("tel")},
95         {HKEY("work")},
96         {HKEY("home")},
97         {HKEY("cell")},
98         {HKEY("adr")},
99         {HKEY("photo")},
100         {HKEY("version")},
101         {HKEY("rev")},
102         {HKEY("label")},
103         {HKEY("uid")}
104 };
105
106 /*
107  * Address book entry (keep it short and sweet, it's just a quickie lookup
108  * which we can use to get to the real meat and bones later)
109  */
110 typedef struct _addrbookent {
111         StrBuf *name;
112         HashList *VC;
113         long ab_msgnum;         /* message number of address book entry */
114         StrBuf *msgNoStr;
115 } addrbookent;
116
117 void deleteAbEnt(void *v) {
118         addrbookent *vc = (addrbookent*)v;
119         DeleteHash(&vc->VC);
120         FreeStrBuf(&vc->name);
121         FreeStrBuf(&vc->msgNoStr);
122         free(vc);
123 }
124
125 HashList *DefineToToken = NULL;
126 HashList *VCTokenToDefine = NULL;
127 HashList *vcNames = NULL; /* todo: fill with the name strings */
128 vcField* vcfUnknown = NULL;
129
130 /******************************************************************************
131  *                   initialize vcard structure                               *
132  ******************************************************************************/
133
134 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
135 {
136         if (vf->Type == UnKnown) {
137                 vcfUnknown = vf;
138         }
139         RegisterTokenParamDefine(SKEY(name), vf->cval);
140         Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
141         Put(vcNames, LKEY(vf->cval), NewStrBufPlain(CKEY(vf->Name)), HFreeStrBuf);
142
143         syslog(LOG_DEBUG, "Token: %s -> %ld, %d", 
144                ChrPtr(name),
145                vf->cval, 
146                inTokenCount);
147
148 }
149
150 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer, long parentCVal)
151 {
152         int i = 0;
153         StrBuf *subStr = NewStrBuf();
154         while (vf[i].STR.len > 0) {
155                 FlushStrBuf(subStr);
156                 vf[i].cval = (*enumCounter) ++;
157                 vf[i].parentCVal = parentCVal;
158                 StrBufAppendBuf(subStr, BaseStr, 0);
159                 if (StrLength(subStr) > 0) {
160                         StrBufAppendBufPlain(subStr, HKEY("."), 0);
161                 }
162                 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
163                 if (layer == 0) {
164                         Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
165                 }
166                 switch (vf[i].Type) {
167                 case FlatString:
168                         break;
169                 case StringCluster:
170                 {
171                         autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1, vf[i].cval);
172                 }
173                 break;
174                 case PhoneNumber:
175                         break;
176                 case EmailAddr:
177                         break;
178                 case Street:
179                         break;
180                 case Number:
181                         break;
182                 case AliasFor:
183                         break;
184                 case Base64BinaryAttachment:
185                         break;
186                 case TerminateList:
187                         break;
188                 case Address:
189                         break;
190                 case UnKnown:
191                         break;
192                 }
193                 RegisterVCardToken(&vf[i], subStr, i);
194                 i++;
195         }
196         FreeStrBuf(&subStr);
197 }
198
199 /******************************************************************************
200  *               VCard template functions                                     *
201  ******************************************************************************/
202
203 int preeval_vcard_item(WCTemplateToken *Token)
204 {
205         WCTemplputParams TPP;
206         WCTemplputParams *TP;
207         int searchFieldNo;
208         StrBuf *Target = NULL;
209
210         memset(&TPP, 0, sizeof(WCTemplputParams));
211         TP = &TPP;
212         TP->Tokens = Token;
213         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
214         if (searchFieldNo >= VCEnumCounter) {
215                 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
216                                  "Invalid define");
217                 return 0;
218         }
219         return 1;
220 }
221
222 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
223 {
224         void *vItem;
225         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
226         addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
227         if (GetHash(ab->VC, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
228                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
229         }
230 }
231
232 void tmpl_vcard_context_item(StrBuf *Target, WCTemplputParams *TP)
233 {
234         void *vItem;
235         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
236         addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
237
238         if (t == NULL) {
239                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
240                                  "Missing context");
241                 return;
242         }
243
244         if (GetHash(ab->VC, LKEY(t->cval), &vItem) && (vItem != NULL)) {
245                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 0);
246         }
247         else {
248                 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
249                                  "Doesn't have that key - did you miss to filter in advance?");
250         }
251 }
252 int preeval_vcard_name_str(WCTemplateToken *Token)
253 {
254         WCTemplputParams TPP;
255         WCTemplputParams *TP;
256         int searchFieldNo;
257         StrBuf *Target = NULL;
258
259         memset(&TPP, 0, sizeof(WCTemplputParams));
260         TP = &TPP;
261         TP->Tokens = Token;
262         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
263         if (searchFieldNo >= VCEnumCounter) {
264                 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
265                                  "Invalid define");
266                 return 0;
267         }
268         return 1;
269 }
270
271 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
272 {
273         void *vItem;
274         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
275         /* todo: get descriptive string for this vcard type */
276         if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
277                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
278         }
279         else {
280                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
281                                  "No i18n string for this.");
282                 return;
283         }
284 }
285
286 void tmpl_vcard_msgno(StrBuf *Target, WCTemplputParams *TP)
287 {
288         addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
289         if (ab->msgNoStr == NULL) {
290                 ab->msgNoStr = NewStrBufPlain(NULL, 64);
291         }
292         StrBufPrintf(ab->msgNoStr, "%ld", ab->ab_msgnum);
293         StrBufAppendTemplate(Target, TP, ab->msgNoStr, 0);
294 }
295 void tmpl_vcard_context_name_str(StrBuf *Target, WCTemplputParams *TP)
296 {
297         void *vItem;
298         vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
299
300         if (t == NULL) {
301                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
302                                  "Missing context");
303                 return;
304         }
305         
306         if (GetHash(vcNames, LKEY(t->cval), &vItem) && (vItem != NULL)) {
307                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
308         }
309         else {
310                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
311                                  "No i18n string for this.");
312                 return;
313         }
314 }
315
316 int filter_VC_ByType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
317 {
318         long searchType;
319         long type = 0;
320         void *v;
321         int rc = 0;
322         vcField *vf = (vcField*) Context;
323
324         memcpy(&type, key, sizeof(long));
325         searchType = GetTemplateTokenNumber(Target, TP, IT_ADDT_PARAM(0), 0);
326         
327         if (vf->Type == searchType) {
328                 addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
329                 if (GetHash(ab->VC, LKEY(vf->cval), &v) && v != NULL)
330                         return 1;
331         }
332         return rc;
333 }
334
335 HashList *getContextVcard(StrBuf *Target, WCTemplputParams *TP)
336 {
337         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
338         addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
339
340         if ((vf == NULL) || (ab == NULL)) {
341                 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
342                                  "Need VCard and Vcard type in context");
343                 
344                 return NULL;
345         }
346         return ab->VC;
347 }
348
349 int filter_VC_ByContextType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
350 {
351         long searchType;
352         vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
353
354         memcpy(&searchType, key, sizeof(long));
355         
356         if (vf->cval == searchType) {
357                 return 1;
358         }
359         else {
360                 return 0;
361         }
362 }
363
364 int conditional_VC_Havetype(StrBuf *Target, WCTemplputParams *TP)
365 {
366         addrbookent *ab = (addrbookent*) CTX(CTX_VCARD);
367         long HaveFieldType = GetTemplateTokenNumber(Target, TP, 2, 0);
368         int rc = 0;     
369         void *vVCitem;
370         const char *Key;
371         long len;
372         HashPos *it = GetNewHashPos(ab->VC, 0);
373         while (GetNextHashPos(ab->VC, it, &len, &Key, &vVCitem) && 
374                (vVCitem != NULL)) 
375         {
376                 void *vvcField;
377                 long type = 0;
378                 memcpy(&type, Key, sizeof(long));
379                 if (GetHash(DefineToToken, LKEY(type), &vvcField) &&
380                     (vvcField != NULL))
381                 {
382                         vcField *t = (vcField*) vvcField;
383                         if (t && t->Type == HaveFieldType) {
384                                 rc = 1;
385                                 break;
386                         }
387                 }
388         }
389         DeleteHashPos(&it);
390         return rc;
391 }
392
393 /******************************************************************************
394  *              parse one VCard                                               *
395  ******************************************************************************/
396
397 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
398 {
399         /* if we have some untagged QP, detect it here. */
400         if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
401                 FlushStrBuf(Swap);
402                 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
403                 SwapBuffers(Swap, ThisFieldStr);
404                 FlushStrBuf(Swap);
405         }
406         Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
407 }
408
409 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
410 {
411         StrBuf *Swap = NULL;
412         int i, j, k;
413         char buf[SIZ];
414         int is_qp = 0;
415         int is_b64 = 0;
416         int ntokens, len;
417         StrBuf *thisname = NULL;
418         char firsttoken[SIZ];
419         StrBuf *thisVCToken;
420         void *vField = NULL;
421
422         Swap = NewStrBuf ();
423         thisname = NewStrBuf();
424         thisVCToken = NewStrBufPlain(NULL, 63);
425         for (i=0; i<(v->numprops); ++i) {
426                 FlushStrBuf(thisVCToken);
427                 is_qp = 0;
428                 is_b64 = 0;
429                 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
430                 StrBufPlain(thisname, v->prop[i].name, -1);
431                 StrBufLowerCase(thisname);
432                 
433                 extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
434                 ntokens = num_tokens(ChrPtr(thisname), ';');
435                 for (j=0, k=0; j < ntokens && k < 10; ++j) {
436                         len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
437                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
438                                 is_qp = 1;
439                         }
440                         else if (!strcasecmp(buf, "encoding=base64")) {
441                                 is_b64 = 1;
442                         }
443                         else {
444                                 if (StrLength(thisVCToken) > 0) {
445                                         StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
446                                 }
447                                 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
448                         }
449                 }
450
451                 vField = NULL;  
452                 if ((StrLength(thisVCToken) > 0) &&
453                     GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) && 
454                     (vField != NULL)) {
455                         vcField *thisField = (vcField *)vField;
456                         StrBuf *ThisFieldStr = NULL;
457                         syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
458                         switch (thisField->Type) {
459                         case StringCluster: {
460                                 int j = 0;
461                                 const char *Pos = NULL;
462                                 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
463                                 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
464                                 while (thisField->Sub[j].STR.len > 0) {
465                                         StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
466                                         ThisFieldStr = NewStrBufDup(Buf);
467                                         PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
468                                         j++;
469                                 }
470                                 FreeStrBuf(&thisArray);
471                                 FreeStrBuf(&Buf);
472                         }
473                                 break;
474                         case Address:
475                         case FlatString:
476                         case PhoneNumber:
477                         case EmailAddr:
478                         case Street:
479                         case Number:
480                         case AliasFor:
481                                 /* copy over the payload into a StrBuf */
482                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
483                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
484
485                                 break;
486                         case Base64BinaryAttachment:
487                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
488                                 StrBufDecodeBase64(ThisFieldStr);
489                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
490                                 break;
491                         case TerminateList:
492                         case UnKnown:
493                                 break;
494                         }
495
496                 }
497                 else if (StrLength(thisVCToken) > 0) {
498                         /* Add it to the UNKNOWN field... */
499                         void *pv = NULL;
500                         StrBuf *oldVal;
501                         GetHash(VC, IKEY(vcfUnknown->cval), &pv);
502                         oldVal = (StrBuf*) pv;
503                         if (oldVal == NULL) {
504                                 oldVal = NewStrBuf();
505                                 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
506                         }
507                         else {
508                                 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
509                         }
510
511                         StrBufAppendBuf(oldVal, thisVCToken, 0);
512                         StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
513                         StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
514                         continue;
515                 }
516         }
517         FreeStrBuf(&thisname);
518         FreeStrBuf(&Swap);
519         FreeStrBuf(&thisVCToken);
520 }
521
522 HashList *CtxGetVcardList(StrBuf *Target, WCTemplputParams *TP)
523 {
524         HashList *pb = CTX(CTX_VCARD_LIST);
525         return pb;
526 }
527
528 /******************************************************************************
529  * Extract an embedded photo from a vCard for display on the client           *
530  ******************************************************************************/
531
532 void display_vcard_photo_img(void)
533 {
534         long msgnum = 0L;
535         StrBuf *vcard;
536         struct vCard *v;
537         char *photosrc;
538         const char *contentType;
539         wcsession *WCC = WC;
540
541         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
542         
543         vcard = load_mimepart(msgnum,"1");
544         v = VCardLoad(vcard);
545         
546         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
547         FlushStrBuf(WCC->WBuf);
548         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
549         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
550                 FlushStrBuf(WCC->WBuf);
551                 
552                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
553                 output_headers(0, 0, 0, 0, 0, 0);
554                 hprintf("Content-Type: text/plain\r\n");
555                 begin_burst();
556                 wc_printf(_("Could Not decode vcard photo\n"));
557                 end_burst();
558                 return;
559         }
560         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
561         http_transmit_thing(contentType, 0);
562         free(v);
563         free(photosrc);
564 }
565
566 wc_mime_attachment *load_vcard(message_summary *Msg) 
567 {
568         HashPos  *it;
569         StrBuf *FoundCharset = NewStrBuf();
570         StrBuf *Error;
571         void *vMime;
572         const char *Key;
573         long len;
574         wc_mime_attachment *Mime;
575         wc_mime_attachment *VCMime = NULL;
576
577         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
578         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
579         Msg->MsgBody->msgnum = Msg->msgnum;
580
581         load_message(Msg, FoundCharset, &Error);
582
583         FreeStrBuf(&FoundCharset);
584         /* look up the vcard... */
585         it = GetNewHashPos(Msg->AllAttach, 0);
586         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
587                (vMime != NULL)) 
588         {
589                 Mime = (wc_mime_attachment*) vMime;
590                 if ((strcmp(ChrPtr(Mime->ContentType),
591                            "text/x-vcard") == 0) ||
592                     (strcmp(ChrPtr(Mime->ContentType),
593                             "text/vcard") == 0))
594                 {
595                         VCMime = Mime;
596                         break;
597                 }
598         }
599         DeleteHashPos(&it);
600         if (VCMime == NULL)
601                 return NULL;
602
603         if (VCMime->Data == NULL)
604                 MimeLoadData(VCMime);
605         return VCMime;
606 }
607
608 /*
609  * Edit the vCard component of a MIME message.  
610  * Supply the message number
611  * and MIME part number to fetch.  Or, specify -1 for the message number
612  * to start with a blank card.
613  */
614 void do_edit_vcard(long msgnum, char *partnum, 
615                    message_summary *VCMsg,
616                    wc_mime_attachment *VCAtt,
617                    const char *return_to, 
618                    const char *force_room) {
619         WCTemplputParams SubTP;
620         wcsession *WCC = WC;
621         message_summary *Msg = NULL;
622         wc_mime_attachment *VCMime = NULL;
623         struct vCard *v;
624         char whatuser[256];
625         addrbookent ab;
626
627         memset(&ab, 0, sizeof(addrbookent));
628         ab.VC = NewHash(0, lFlathash);
629         /* Display the form */
630         output_headers(1, 1, 1, 0, 0, 0);
631
632         safestrncpy(whatuser, "", sizeof whatuser);
633
634         if ((msgnum >= 0) || 
635             ((VCMsg != NULL) && (VCAtt != NULL)))
636         {
637                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
638
639                         Msg = (message_summary *) malloc(sizeof(message_summary));
640                         memset(Msg, 0, sizeof(message_summary));
641                         Msg->msgnum = msgnum;
642                         VCMime = load_vcard(Msg);
643                         if (VCMime == NULL) {
644                                 convenience_page("770000", _("Error"), "");/*TODO: important message*/
645                                 DestroyMessageSummary(Msg);
646                                 return;
647                                 DeleteHash(&ab.VC);
648                         }
649                 
650                         v = VCardLoad(VCMime->Data);
651                 }
652                 else {
653                         v = VCardLoad(VCAtt->Data);
654                 }
655
656                 parse_vcard(WCC->WBuf, v, ab.VC, NULL);
657         
658         
659                 vcard_free(v);
660         }
661
662         memset(&SubTP, 0, sizeof(WCTemplputParams));    
663         {
664                 WCTemplputParams *TP = NULL;
665                 WCTemplputParams SubTP;
666
667                 StackContext(TP, &SubTP, &ab, CTX_VCARD, 0, NULL);
668
669                 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
670                 UnStackContext(&SubTP);
671         }
672         DeleteHash(&ab.VC);
673
674
675         wDumpContent(1);
676         if (Msg != NULL) {
677                 DestroyMessageSummary(Msg);
678         }
679 }
680
681
682 /*
683  *  commit the edits to the citadel server
684  */
685 void edit_vcard(void) {
686         long msgnum;
687         char *partnum;
688
689         msgnum = lbstr("msgnum");
690         partnum = bstr("partnum");
691         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
692 }
693
694 /*
695  *  parse edited vcard from the browser
696  */
697 void submit_vcard(void) {
698         struct vCard *v;
699         char *serialized_vcard;
700         StrBuf *Buf;
701         const StrBuf *ForceRoom;
702         HashList* postVcard;
703         HashPos *it, *itSub;
704         const char *Key;
705         long len;
706         void *pv;
707         StrBuf *SubStr;
708         const StrBuf *s;
709         const char *Pos = NULL;
710
711         if (!havebstr("ok_button")) { 
712                 readloop(readnew, eUseDefault);
713                 return;
714         }
715
716         if (havebstr("force_room")) {
717                 ForceRoom = sbstr("force_room");
718                 if (gotoroom(ForceRoom) != 200) {
719                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
720                         AppendImportantMessage(HKEY(": "));
721                         AppendImportantMessage(SKEY(ForceRoom));
722                         AppendImportantMessage(HKEY("; "));
723                         AppendImportantMessage(_("Aborting."), -1);
724
725                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
726                                 select_user_to_edit(NULL);
727                         }
728                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
729                                 do_welcome();
730                         }
731                         else if (!IsEmptyStr(bstr("return_to"))) {
732                                 http_redirect(bstr("return_to"));
733                         }
734                         else {
735                                 readloop(readnew, eUseDefault);
736                         }
737                         return;
738                 }
739         }
740
741         postVcard = getSubStruct(HKEY("VC"));
742         if (postVcard == NULL) {
743                 AppendImportantMessage(_("An error has occurred."), -1);
744                 edit_vcard();
745                 return;/*/// more details*/
746         }
747         
748         Buf = NewStrBuf();
749         serv_write(HKEY("ENT0 1|||4\n"));
750         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
751         {
752                 edit_vcard();
753                 return;
754         }
755         
756         /* Make a vCard structure out of the data supplied in the form */
757         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
758                      bstr("extrafields")
759         );
760         v = VCardLoad(Buf);     /* Start with the extra fields */
761         if (v == NULL) {
762                 AppendImportantMessage(_("An error has occurred."), -1);
763                 edit_vcard();
764                 FreeStrBuf(&Buf);
765                 return;
766         }
767
768         SubStr = NewStrBuf();
769         it = GetNewHashPos(DefineToToken, 0);
770         while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) && 
771                (pv != NULL)) 
772         {
773                 char buf[32];
774                 long blen;
775                 vcField *t = (vcField*) pv;
776
777                 if (t->Sub != NULL){
778                         vcField *Sub;
779                         FlushStrBuf(SubStr);
780                         itSub = GetNewHashPos(DefineToToken, 0);
781                         while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) && 
782                                (pv != NULL)) 
783                         {
784                                 Sub = (vcField*) pv;
785                                 if (Sub->parentCVal == t->cval) {
786                                         if (StrLength(SubStr) > 0)
787                                                 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
788
789
790
791                                         blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
792                                         s = SSubBstr(postVcard, buf, blen);
793                         
794                                         if ((s != NULL) && (StrLength(s) > 0)) {
795                                                 /// todo: utf8 qp
796                                                 StrBufAppendBuf(SubStr, s, 0);
797                                         }
798                                 }
799                         }
800                         if (StrLength(SubStr) > 0) {
801                                 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
802                         }
803                         DeleteHashPos(&itSub);
804                 }
805                 else if (t->parentCVal == 0) {
806                         blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
807                         s = SSubBstr(postVcard, buf, blen);
808                         
809                         if ((s != NULL) && (StrLength(s) > 0)) {
810                                 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
811                         }
812                 }
813         }
814         DeleteHashPos(&it);
815
816         s = sbstr("other_inetemail");
817         if (StrLength(s) > 0) {
818                 FlushStrBuf(SubStr);
819                 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
820                         if (StrLength(SubStr) > 0) {
821                                 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
822                         }
823                 }
824         }
825
826         FreeStrBuf(&SubStr);
827
828
829         serialized_vcard = vcard_serialize(v);
830         vcard_free(v);
831         if (serialized_vcard == NULL) {
832                 AppendImportantMessage(_("An error has occurred."), -1);
833                 edit_vcard();
834                 FreeStrBuf(&Buf);
835                 return;
836         }
837
838         printf("%s", serialized_vcard);
839         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
840         serv_write(HKEY("\n"));
841         serv_printf("%s\r\n", serialized_vcard);
842         serv_write(HKEY("000\n"));
843         free(serialized_vcard);
844
845         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
846                 select_user_to_edit(NULL);
847         }
848         else if (!strcmp(bstr("return_to"), "do_welcome")) {
849                 do_welcome();
850         }
851         else if (!IsEmptyStr(bstr("return_to"))) {
852                 http_redirect(bstr("return_to"));
853         }
854         else {
855                 readloop(readnew, eUseDefault);
856         }
857         FreeStrBuf(&Buf);
858 }
859
860 /******************************************************************************
861  *              Render Addressbooks                                           *
862  ******************************************************************************/
863
864 typedef struct _vcardview_struct {
865         long is_singlecard;
866         HashList *addrbook;
867
868 } vcardview_struct;
869
870 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
871                                  void **ViewSpecific, 
872                                  long oper, 
873                                  char *cmd, 
874                                  long len,
875                                  char *filter,
876                                  long flen)
877 {
878         vcardview_struct *VS;
879
880         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
881         memset(VS, 0, sizeof(vcardview_struct));
882         *ViewSpecific = (void*)VS;
883
884         VS->is_singlecard = ibstr("is_singlecard");
885         if (VS->is_singlecard != 1) {
886                 VS->addrbook = NewHash(0, NULL);
887                 if (oper == do_search) {
888                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
889                 }
890                 else {
891                         strcpy(cmd, "MSGS ALL");
892                 }
893                 Stat->maxmsgs = 9999999;
894         }
895         return 200;
896 }
897
898 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
899                             void **ViewSpecific, 
900                             message_summary* Msg, 
901                             int is_new, 
902                             int i)
903 {
904         wcsession *WCC = WC;
905         WCTemplputParams *TP = NULL;
906         WCTemplputParams SubTP;
907         vcardview_struct *VS;
908         wc_mime_attachment *VCMime = NULL;
909         struct vCard *v;
910         addrbookent* abEntry;
911
912         VS = (vcardview_struct*) *ViewSpecific;
913
914         VCMime = load_vcard(Msg);
915         if (VCMime == NULL)
916                 return 0;
917
918         v = VCardLoad(VCMime->Data);
919
920         if (v == NULL) return 0;
921
922         abEntry = (addrbookent*) malloc(sizeof(addrbookent));
923         memset(abEntry, 0, sizeof(addrbookent));
924         abEntry->name = NewStrBuf();
925         abEntry->VC = NewHash(0, lFlathash);
926         abEntry->ab_msgnum = Msg->msgnum;
927
928         parse_vcard(WCC->WBuf, v, abEntry->VC, VCMime);
929
930         memset(&SubTP, 0, sizeof(WCTemplputParams));    
931         StackContext(TP, &SubTP, abEntry, CTX_VCARD, 0, NULL);
932
933         // No, don't display the name, it just shits all over the screen
934         // DoTemplate(HKEY("vcard_list_name"), WCC->WBuf, &SubTP);
935
936         UnStackContext(&SubTP);
937
938         if (StrLength(abEntry->name) == 0) {
939                 StrBufPlain(abEntry->name, _("(no name)"), -1);
940         }
941
942         vcard_free(v);
943         
944         Put(VS->addrbook, SKEY(abEntry->name), abEntry, deleteAbEnt);
945         return 0;
946 }
947
948
949 /*
950  * Render the address book using info we gathered during the scan
951  *
952  * addrbook     the addressbook to render
953  * num_ab       the number of the addressbook
954  */
955 static int NAMESPERPAGE = 60;
956 void do_addrbook_view(vcardview_struct* VS) {
957         long i = 0;
958         int num_pages = 0;
959         int tabfirst = 0;
960         int tablast = 0;
961         StrBuf **tablabels;
962         int num_ab = GetCount(VS->addrbook);
963         HashList *headlines;
964         wcsession *WCC = WC;
965
966         WCTemplputParams *TP = NULL;
967         WCTemplputParams SubTP;
968
969         memset(&SubTP, 0, sizeof(WCTemplputParams));    
970         
971         if (num_ab == 0) {
972                 do_template("vcard_list_empty");
973                 return;
974         }
975
976         if (num_ab > 1) {
977                 SortByHashKey(VS->addrbook, 1);
978         }
979
980         num_pages = (GetCount(VS->addrbook) / NAMESPERPAGE) + 1;
981
982         tablabels = malloc(num_pages * sizeof (StrBuf *));
983         if (tablabels == NULL) {
984                 return;
985         }
986
987         headlines = NewHash(0, lFlathash);
988         for (i=0; i<num_pages; ++i) {
989                 void *v1 = NULL;
990                 void *v2 = NULL;
991                 long hklen1, hklen2;
992                 const char *c1, *c2;
993                 StrBuf *headline;
994                 addrbookent *a1, *a2;
995
996                 tabfirst = i * NAMESPERPAGE;
997                 tablast = tabfirst + NAMESPERPAGE - 1;
998                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
999
1000                 headline = NewStrBufPlain(NULL, StrLength(v1) + StrLength(v2) + 10);
1001                 if (GetHashAt(VS->addrbook, tabfirst, &hklen1, &c1, &v1)) {
1002                         a1 = (addrbookent*) v1;
1003                         StrBufAppendBuf(headline, a1->name, 0);
1004                         StrBuf_Utf8StrCut(headline, 3);
1005                         if (GetHashAt(VS->addrbook, tablast, &hklen2, &c2, &v2)) {
1006
1007                                 a2 = (addrbookent*) v2;
1008                                 StrBufAppendBufPlain(headline, HKEY(" - "), 0);
1009                                 StrBufAppendBuf(headline, a2->name, 0);
1010                                 StrBuf_Utf8StrCut(headline, 9);
1011                         }
1012                 }
1013                 tablabels[i] = headline;
1014                 Put(headlines, LKEY(i), headline, HFreeStrBuf);
1015         }
1016         StrTabbedDialog(WC->WBuf, num_pages, tablabels);
1017         StackContext(TP, &SubTP, VS->addrbook, CTX_VCARD_LIST, 0, NULL);
1018
1019         DoTemplate(HKEY("vcard_list"), WCC->WBuf, &SubTP);
1020         UnStackContext(&SubTP);
1021         DeleteHash(&headlines);
1022         free(tablabels);
1023         StrBufAppendBufPlain(WCC->WBuf, HKEY("</div>"), 0);/* closes: id=global */
1024 }
1025
1026
1027
1028 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1029 {
1030         const StrBuf *Mime;
1031         vcardview_struct *VS;
1032
1033         VS = (vcardview_struct*) *ViewSpecific;
1034         if (VS->is_singlecard)
1035                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime, NULL);
1036         else
1037                 do_addrbook_view(VS);   /* Render the address book */
1038         return 0;
1039 }
1040
1041 int vcard_Cleanup(void **ViewSpecific)
1042 {
1043         vcardview_struct *VS;
1044
1045         VS = (vcardview_struct*) *ViewSpecific;
1046         wDumpContent(1);
1047         if ((VS != NULL) && 
1048             (VS->addrbook != NULL))
1049                 DeleteHash(&VS->addrbook);
1050         if (VS != NULL) 
1051                 free(VS);
1052
1053         return 0;
1054 }
1055
1056 void render_MIME_VCard(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundCharset)
1057 {
1058         wc_mime_attachment *Mime = (wc_mime_attachment *) CTX(CTX_MIME_ATACH);
1059         wcsession *WCC = WC;
1060         if (StrLength(Mime->Data) == 0)
1061                 MimeLoadData(Mime);
1062         if (StrLength(Mime->Data) > 0) {
1063                 struct vCard *v;
1064                 StrBuf *Buf;
1065
1066                 Buf = NewStrBuf();
1067                 /** If it's my vCard I can edit it */
1068                 if (    (!strcasecmp(ChrPtr(WCC->CurRoom.name), USERCONFIGROOM))
1069                         || ((StrLength(WCC->CurRoom.name) > 11) &&
1070                             (!strcasecmp(&(ChrPtr(WCC->CurRoom.name)[11]), USERCONFIGROOM)))
1071                         || (WCC->CurRoom.view == VIEW_ADDRESSBOOK)
1072                         ) {
1073                         StrBufAppendPrintf(Buf, "<a href=\"edit_vcard?msgnum=%ld?partnum=%s\">",
1074                                 Mime->msgnum, ChrPtr(Mime->PartNum));
1075                         StrBufAppendPrintf(Buf, "[%s]</a>", _("edit"));
1076                 }
1077
1078                 /* In all cases, display the full card */
1079
1080                 v = VCardLoad(Mime->Data);
1081
1082                 if (v != NULL) {
1083                         WCTemplputParams *TP = NULL;
1084                         WCTemplputParams SubTP;
1085                         addrbookent ab;
1086                         memset(&ab, 0, sizeof(addrbookent));
1087
1088                         ab.VC = NewHash(0, lFlathash);
1089                         ab.ab_msgnum = Mime->msgnum;
1090
1091                         parse_vcard(Target, v, ab.VC, Mime);
1092
1093                         memset(&SubTP, 0, sizeof(WCTemplputParams));    
1094                         StackContext(TP, &SubTP, &ab, CTX_VCARD, 0, NULL);
1095
1096                         DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
1097                         UnStackContext(&SubTP);
1098                         DeleteHash(&ab.VC);
1099                         vcard_free(v);
1100
1101                 }
1102                 else {
1103                         StrBufPlain(Buf, _("failed to load vcard"), -1);
1104                 }
1105                 FreeStrBuf(&Mime->Data);
1106                 Mime->Data = Buf;
1107         }
1108
1109 }
1110
1111 void 
1112 ServerStartModule_VCARD
1113 (void)
1114 {
1115 }
1116
1117 void 
1118 ServerShutdownModule_VCARD
1119 (void)
1120 {
1121         DeleteHash(&DefineToToken);
1122         DeleteHash(&vcNames);
1123         DeleteHash(&VCTokenToDefine);
1124 }
1125
1126 void 
1127 InitModule_VCARD
1128 (void)
1129 {
1130         StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1131         DefineToToken   = NewHash(1, lFlathash);
1132         vcNames         = NewHash(1, lFlathash);
1133         VCTokenToDefine = NewHash(1, NULL);
1134         autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1135         FreeStrBuf(&Prefix);
1136
1137         REGISTERTokenParamDefine(NAMESPERPAGE);
1138
1139
1140         RegisterCTX(CTX_VCARD);
1141         RegisterCTX(CTX_VCARD_LIST);
1142         RegisterCTX(CTX_VCARD_TYPE);
1143
1144         RegisterReadLoopHandlerset(
1145                 VIEW_ADDRESSBOOK,
1146                 vcard_GetParamsGetServerCall,
1147                 NULL,
1148                 NULL,
1149                 NULL, 
1150                 vcard_LoadMsgFromServer,
1151                 vcard_RenderView_or_Tail,
1152                 vcard_Cleanup,
1153                 NULL);
1154
1155         RegisterIterator("MAIL:VCARDS", 0, NULL, CtxGetVcardList, NULL, NULL, CTX_VCARD, CTX_VCARD_LIST, IT_NOFLAG);
1156
1157
1158         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1159         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1160         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1161
1162         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1163         RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1164         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1165         RegisterNamespace("VC:MSGNO", 0, 1, tmpl_vcard_msgno, NULL, CTX_VCARD);
1166         RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1167         REGISTERTokenParamDefine(FlatString);
1168         REGISTERTokenParamDefine(StringCluster);
1169         REGISTERTokenParamDefine(PhoneNumber);
1170         REGISTERTokenParamDefine(EmailAddr);
1171         REGISTERTokenParamDefine(Street);
1172         REGISTERTokenParamDefine(Number);
1173         REGISTERTokenParamDefine(AliasFor);
1174         REGISTERTokenParamDefine(Base64BinaryAttachment);
1175         REGISTERTokenParamDefine(TerminateList);
1176         REGISTERTokenParamDefine(Address);
1177
1178         RegisterConditional("VC:HAVE:TYPE",      1, conditional_VC_Havetype, CTX_VCARD);
1179         RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1180         RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1181
1182         RegisterMimeRenderer(HKEY("text/x-vcard"), render_MIME_VCard, 1, 201);
1183         RegisterMimeRenderer(HKEY("text/vcard"), render_MIME_VCard, 1, 200);
1184 }