608d74025c5c943b7e4d3ab1d88d229adadc2175
[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_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                 /*len = */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                                         
468                                         PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
469                                         j++;
470                                 }
471                                 FreeStrBuf(&thisArray);
472                                 FreeStrBuf(&Buf);
473                         }
474                                 break;
475                         case Address:
476                         case FlatString:
477                         case PhoneNumber:
478                         case EmailAddr:
479                         case Street:
480                         case Number:
481                         case AliasFor:
482                                 /* copy over the payload into a StrBuf */
483                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
484                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
485
486                                 break;
487                         case Base64BinaryAttachment:
488                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
489                                 StrBufDecodeBase64(ThisFieldStr);
490                                 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
491                                 break;
492                         case TerminateList:
493                         case UnKnown:
494                                 break;
495                         }
496
497                 }
498                 else if (StrLength(thisVCToken) > 0) {
499                         /* Add it to the UNKNOWN field... */
500                         void *pv = NULL;
501                         StrBuf *oldVal;
502                         GetHash(VC, IKEY(vcfUnknown->cval), &pv);
503                         oldVal = (StrBuf*) pv;
504                         if (oldVal == NULL) {
505                                 oldVal = NewStrBuf();
506                                 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
507                         }
508                         else {
509                                 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
510                         }
511
512                         StrBufAppendBuf(oldVal, thisVCToken, 0);
513                         StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
514                         StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
515                         continue;
516                 }
517         }
518         FreeStrBuf(&thisname);
519         FreeStrBuf(&Swap);
520         FreeStrBuf(&thisVCToken);
521 }
522
523 HashList *CtxGetVcardList(StrBuf *Target, WCTemplputParams *TP)
524 {
525         HashList *pb = CTX(CTX_VCARD_LIST);
526         return pb;
527 }
528
529 /******************************************************************************
530  * Extract an embedded photo from a vCard for display on the client           *
531  ******************************************************************************/
532
533 void display_vcard_photo_img(void)
534 {
535         long msgnum = 0L;
536         StrBuf *vcard;
537         struct vCard *v;
538         char *photosrc;
539         const char *contentType;
540         wcsession *WCC = WC;
541
542         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
543         
544         vcard = load_mimepart(msgnum,"1");
545         v = VCardLoad(vcard);
546         
547         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
548         FlushStrBuf(WCC->WBuf);
549         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
550         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
551                 FlushStrBuf(WCC->WBuf);
552                 
553                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
554                 output_headers(0, 0, 0, 0, 0, 0);
555                 hprintf("Content-Type: text/plain\r\n");
556                 begin_burst();
557                 wc_printf(_("Could Not decode vcard photo\n"));
558                 end_burst();
559                 return;
560         }
561         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
562         http_transmit_thing(contentType, 0);
563         free(v);
564         free(photosrc);
565 }
566
567 wc_mime_attachment *load_vcard(message_summary *Msg) 
568 {
569         HashPos  *it;
570         StrBuf *FoundCharset = NewStrBuf();
571         StrBuf *Error;
572         void *vMime;
573         const char *Key;
574         long len;
575         wc_mime_attachment *Mime;
576         wc_mime_attachment *VCMime = NULL;
577
578         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
579         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
580         Msg->MsgBody->msgnum = Msg->msgnum;
581
582         load_message(Msg, FoundCharset, &Error);
583
584         FreeStrBuf(&FoundCharset);
585         /* look up the vcard... */
586         it = GetNewHashPos(Msg->AllAttach, 0);
587         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
588                (vMime != NULL)) 
589         {
590                 Mime = (wc_mime_attachment*) vMime;
591                 if ((strcmp(ChrPtr(Mime->ContentType),
592                            "text/x-vcard") == 0) ||
593                     (strcmp(ChrPtr(Mime->ContentType),
594                             "text/vcard") == 0))
595                 {
596                         VCMime = Mime;
597                         break;
598                 }
599         }
600         DeleteHashPos(&it);
601         if (VCMime == NULL)
602                 return NULL;
603
604         if (VCMime->Data == NULL)
605                 MimeLoadData(VCMime);
606         return VCMime;
607 }
608
609 /*
610  * Edit the vCard component of a MIME message.  
611  * Supply the message number
612  * and MIME part number to fetch.  Or, specify -1 for the message number
613  * to start with a blank card.
614  */
615 void do_edit_vcard(long msgnum, char *partnum, 
616                    message_summary *VCMsg,
617                    wc_mime_attachment *VCAtt,
618                    const char *return_to, 
619                    const char *force_room) {
620         WCTemplputParams SubTP;
621         wcsession *WCC = WC;
622         message_summary *Msg = NULL;
623         wc_mime_attachment *VCMime = NULL;
624         struct vCard *v;
625         char whatuser[256];
626         addrbookent ab;
627
628         memset(&ab, 0, sizeof(addrbookent));
629         ab.VC = NewHash(0, lFlathash);
630         /* Display the form */
631         output_headers(1, 1, 1, 0, 0, 0);
632
633         safestrncpy(whatuser, "", sizeof whatuser);
634
635         if ((msgnum >= 0) || 
636             ((VCMsg != NULL) && (VCAtt != NULL)))
637         {
638                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
639
640                         Msg = (message_summary *) malloc(sizeof(message_summary));
641                         memset(Msg, 0, sizeof(message_summary));
642                         Msg->msgnum = msgnum;
643                         VCMime = load_vcard(Msg);
644                         if (VCMime == NULL) {
645                                 convenience_page("770000", _("Error"), "");/*TODO: important message*/
646                                 DestroyMessageSummary(Msg);
647                                 return;
648                                 DeleteHash(&ab.VC);
649                         }
650                 
651                         v = VCardLoad(VCMime->Data);
652                 }
653                 else {
654                         v = VCardLoad(VCAtt->Data);
655                 }
656
657                 parse_vcard(WCC->WBuf, v, ab.VC, NULL);
658         
659         
660                 vcard_free(v);
661         }
662
663         memset(&SubTP, 0, sizeof(WCTemplputParams));    
664         {
665                 WCTemplputParams *TP = NULL;
666                 WCTemplputParams SubTP;
667
668                 StackContext(TP, &SubTP, &ab, CTX_VCARD, 0, NULL);
669
670                 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
671                 UnStackContext(&SubTP);
672         }
673         DeleteHash(&ab.VC);
674
675
676         wDumpContent(1);
677         if (Msg != NULL) {
678                 DestroyMessageSummary(Msg);
679         }
680 }
681
682
683 /*
684  *  commit the edits to the citadel server
685  */
686 void edit_vcard(void) {
687         long msgnum;
688         char *partnum;
689
690         msgnum = lbstr("msgnum");
691         partnum = bstr("partnum");
692         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
693 }
694
695 /*
696  *  parse edited vcard from the browser
697  */
698 void submit_vcard(void) {
699         struct vCard *v;
700         char *serialized_vcard;
701         StrBuf *Buf;
702         const StrBuf *ForceRoom;
703         HashList* postVcard;
704         HashPos *it, *itSub;
705         const char *Key;
706         long len;
707         void *pv;
708         StrBuf *SubStr;
709         const StrBuf *s;
710         const char *Pos = NULL;
711
712         if (!havebstr("ok_button")) { 
713                 readloop(readnew, eUseDefault);
714                 return;
715         }
716
717         if (havebstr("force_room")) {
718                 ForceRoom = sbstr("force_room");
719                 if (gotoroom(ForceRoom) != 200) {
720                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
721                         AppendImportantMessage(HKEY(": "));
722                         AppendImportantMessage(SKEY(ForceRoom));
723                         AppendImportantMessage(HKEY("; "));
724                         AppendImportantMessage(_("Aborting."), -1);
725
726                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
727                                 select_user_to_edit(NULL);
728                         }
729                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
730                                 do_welcome();
731                         }
732                         else if (!IsEmptyStr(bstr("return_to"))) {
733                                 http_redirect(bstr("return_to"));
734                         }
735                         else {
736                                 readloop(readnew, eUseDefault);
737                         }
738                         return;
739                 }
740         }
741
742         postVcard = getSubStruct(HKEY("VC"));
743         if (postVcard == NULL) {
744                 AppendImportantMessage(_("An error has occurred."), -1);
745                 edit_vcard();
746                 return;/*/// more details*/
747         }
748         
749         Buf = NewStrBuf();
750         serv_write(HKEY("ENT0 1|||4\n"));
751         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
752         {
753                 edit_vcard();
754                 return;
755         }
756         
757         /* Make a vCard structure out of the data supplied in the form */
758         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
759                      bstr("extrafields")
760         );
761         v = VCardLoad(Buf);     /* Start with the extra fields */
762         if (v == NULL) {
763                 AppendImportantMessage(_("An error has occurred."), -1);
764                 edit_vcard();
765                 FreeStrBuf(&Buf);
766                 return;
767         }
768
769         SubStr = NewStrBuf();
770         it = GetNewHashPos(DefineToToken, 0);
771         while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) && 
772                (pv != NULL)) 
773         {
774                 char buf[32];
775                 long blen;
776                 vcField *t = (vcField*) pv;
777
778                 if (t->Sub != NULL){
779                         vcField *Sub;
780                         FlushStrBuf(SubStr);
781                         itSub = GetNewHashPos(DefineToToken, 0);
782                         while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) && 
783                                (pv != NULL)) 
784                         {
785                                 Sub = (vcField*) pv;
786                                 if (Sub->parentCVal == t->cval) {
787                                         if (StrLength(SubStr) > 0)
788                                                 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
789
790
791
792                                         blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
793                                         s = SSubBstr(postVcard, buf, blen);
794                         
795                                         if ((s != NULL) && (StrLength(s) > 0)) {
796                                                 /// todo: utf8 qp
797                                                 StrBufAppendBuf(SubStr, s, 0);
798                                         }
799                                 }
800                         }
801                         if (StrLength(SubStr) > 0) {
802                                 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
803                         }
804                         DeleteHashPos(&itSub);
805                 }
806                 else if (t->parentCVal == 0) {
807                         blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
808                         s = SSubBstr(postVcard, buf, blen);
809                         
810                         if ((s != NULL) && (StrLength(s) > 0)) {
811                                 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
812                         }
813                 }
814         }
815         DeleteHashPos(&it);
816
817         s = sbstr("other_inetemail");
818         if (StrLength(s) > 0) {
819                 FlushStrBuf(SubStr);
820                 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
821                         if (StrLength(SubStr) > 0) {
822                                 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
823                         }
824                 }
825         }
826
827         FreeStrBuf(&SubStr);
828
829
830         serialized_vcard = vcard_serialize(v);
831         vcard_free(v);
832         if (serialized_vcard == NULL) {
833                 AppendImportantMessage(_("An error has occurred."), -1);
834                 edit_vcard();
835                 FreeStrBuf(&Buf);
836                 return;
837         }
838
839         printf("%s", serialized_vcard);
840         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
841         serv_write(HKEY("\n"));
842         serv_printf("%s\r\n", serialized_vcard);
843         serv_write(HKEY("000\n"));
844         free(serialized_vcard);
845
846         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
847                 select_user_to_edit(NULL);
848         }
849         else if (!strcmp(bstr("return_to"), "do_welcome")) {
850                 do_welcome();
851         }
852         else if (!IsEmptyStr(bstr("return_to"))) {
853                 http_redirect(bstr("return_to"));
854         }
855         else {
856                 readloop(readnew, eUseDefault);
857         }
858         FreeStrBuf(&Buf);
859 }
860
861 /******************************************************************************
862  *              Render Addressbooks                                           *
863  ******************************************************************************/
864
865 typedef struct _vcardview_struct {
866         long is_singlecard;
867         HashList *addrbook;
868
869 } vcardview_struct;
870
871 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
872                                  void **ViewSpecific, 
873                                  long oper, 
874                                  char *cmd, 
875                                  long len,
876                                  char *filter,
877                                  long flen)
878 {
879         vcardview_struct *VS;
880
881         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
882         memset(VS, 0, sizeof(vcardview_struct));
883         *ViewSpecific = (void*)VS;
884
885         VS->is_singlecard = ibstr("is_singlecard");
886         if (VS->is_singlecard != 1) {
887                 VS->addrbook = NewHash(0, NULL);
888                 if (oper == do_search) {
889                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
890                 }
891                 else {
892                         strcpy(cmd, "MSGS ALL");
893                 }
894                 Stat->maxmsgs = 9999999;
895         }
896         return 200;
897 }
898
899 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
900                             void **ViewSpecific, 
901                             message_summary* Msg, 
902                             int is_new, 
903                             int i)
904 {
905         wcsession *WCC = WC;
906         WCTemplputParams *TP = NULL;
907         WCTemplputParams SubTP;
908         vcardview_struct *VS;
909         wc_mime_attachment *VCMime = NULL;
910         struct vCard *v;
911         addrbookent* abEntry;
912
913         VS = (vcardview_struct*) *ViewSpecific;
914
915         VCMime = load_vcard(Msg);
916         if (VCMime == NULL)
917                 return 0;
918
919         v = VCardLoad(VCMime->Data);
920
921         if (v == NULL) return 0;
922
923         abEntry = (addrbookent*) malloc(sizeof(addrbookent));
924         memset(abEntry, 0, sizeof(addrbookent));
925         abEntry->name = NewStrBuf();
926         abEntry->VC = NewHash(0, lFlathash);
927         abEntry->ab_msgnum = Msg->msgnum;
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         DoTemplate(HKEY("vcard_list_name"), WCC->WBuf, &SubTP);
934         UnStackContext(&SubTP);
935
936         if (StrLength(abEntry->name) == 0) {
937                 StrBufPlain(abEntry->name, _("(no name)"), -1);
938         }
939
940         vcard_free(v);
941         
942         Put(VS->addrbook, SKEY(abEntry->name), abEntry, deleteAbEnt);
943         return 0;
944 }
945
946
947 /*
948  * Render the address book using info we gathered during the scan
949  *
950  * addrbook     the addressbook to render
951  * num_ab       the number of the addressbook
952  */
953 static int NAMESPERPAGE = 60;
954 void do_addrbook_view(vcardview_struct* VS) {
955         long i = 0;
956         int num_pages = 0;
957         int tabfirst = 0;
958         int tablast = 0;
959         StrBuf **tablabels;
960         int num_ab = GetCount(VS->addrbook);
961         HashList *headlines;
962         wcsession *WCC = WC;
963
964         WCTemplputParams *TP = NULL;
965         WCTemplputParams SubTP;
966
967         memset(&SubTP, 0, sizeof(WCTemplputParams));    
968         
969         if (num_ab == 0) {
970                 do_template("vcard_list_empty");
971                 return;
972         }
973
974         if (num_ab > 1) {
975                 SortByHashKey(VS->addrbook, 1);
976         }
977
978         num_pages = (GetCount(VS->addrbook) / NAMESPERPAGE) + 1;
979
980         tablabels = malloc(num_pages * sizeof (StrBuf *));
981         if (tablabels == NULL) {
982                 return;
983         }
984
985         headlines = NewHash(0, lFlathash);
986         for (i=0; i<num_pages; ++i) {
987                 void *v1 = NULL;
988                 void *v2 = NULL;
989                 long hklen1, hklen2;
990                 const char *c1, *c2;
991                 StrBuf *headline;
992                 addrbookent *a1, *a2;
993
994                 tabfirst = i * NAMESPERPAGE;
995                 tablast = tabfirst + NAMESPERPAGE - 1;
996                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
997
998                 headline = NewStrBufPlain(NULL, StrLength(v1) + StrLength(v2) + 10);
999                 if (GetHashAt(VS->addrbook, tabfirst, &hklen1, &c1, &v1)) {
1000                         a1 = (addrbookent*) v1;
1001                         StrBufAppendBuf(headline, a1->name, 0);
1002                         StrBuf_Utf8StrCut(headline, 3);
1003                         if (GetHashAt(VS->addrbook, tablast, &hklen2, &c2, &v2)) {
1004
1005                                 a2 = (addrbookent*) v2;
1006                                 StrBufAppendBufPlain(headline, HKEY(" - "), 0);
1007                                 StrBufAppendBuf(headline, a2->name, 0);
1008                                 StrBuf_Utf8StrCut(headline, 9);
1009                         }
1010                 }
1011                 tablabels[i] = headline;
1012                 Put(headlines, LKEY(i), headline, HFreeStrBuf);
1013         }
1014         StrTabbedDialog(WC->WBuf, num_pages, tablabels);
1015         StackContext(TP, &SubTP, VS->addrbook, CTX_VCARD_LIST, 0, NULL);
1016
1017         DoTemplate(HKEY("vcard_list"), WCC->WBuf, &SubTP);
1018         UnStackContext(&SubTP);
1019         DeleteHash(&headlines);
1020         free(tablabels);
1021         StrBufAppendBufPlain(WCC->WBuf, HKEY("</div>"), 0);/* closes: id=global */
1022 }
1023
1024
1025
1026 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1027 {
1028         const StrBuf *Mime;
1029         vcardview_struct *VS;
1030
1031         VS = (vcardview_struct*) *ViewSpecific;
1032         if (VS->is_singlecard)
1033                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime, NULL);
1034         else
1035                 do_addrbook_view(VS);   /* Render the address book */
1036         return 0;
1037 }
1038
1039 int vcard_Cleanup(void **ViewSpecific)
1040 {
1041         vcardview_struct *VS;
1042
1043         VS = (vcardview_struct*) *ViewSpecific;
1044         wDumpContent(1);
1045         if ((VS != NULL) && 
1046             (VS->addrbook != NULL))
1047                 DeleteHash(&VS->addrbook);
1048         if (VS != NULL) 
1049                 free(VS);
1050
1051         return 0;
1052 }
1053
1054 void render_MIME_VCard(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundCharset)
1055 {
1056         wc_mime_attachment *Mime = (wc_mime_attachment *) CTX(CTX_MIME_ATACH);
1057         wcsession *WCC = WC;
1058         if (StrLength(Mime->Data) == 0)
1059                 MimeLoadData(Mime);
1060         if (StrLength(Mime->Data) > 0) {
1061                 struct vCard *v;
1062                 StrBuf *Buf;
1063
1064                 Buf = NewStrBuf();
1065                 /** If it's my vCard I can edit it */
1066                 if (    (!strcasecmp(ChrPtr(WCC->CurRoom.name), USERCONFIGROOM))
1067                         || ((StrLength(WCC->CurRoom.name) > 11) &&
1068                             (!strcasecmp(&(ChrPtr(WCC->CurRoom.name)[11]), USERCONFIGROOM)))
1069                         || (WCC->CurRoom.view == VIEW_ADDRESSBOOK)
1070                         ) {
1071                         StrBufAppendPrintf(Buf, "<a href=\"edit_vcard?msgnum=%ld?partnum=%s\">",
1072                                 Mime->msgnum, ChrPtr(Mime->PartNum));
1073                         StrBufAppendPrintf(Buf, "[%s]</a>", _("edit"));
1074                 }
1075
1076                 /* In all cases, display the full card */
1077
1078                 v = VCardLoad(Mime->Data);
1079
1080                 if (v != NULL) {
1081                         WCTemplputParams *TP = NULL;
1082                         WCTemplputParams SubTP;
1083                         addrbookent ab;
1084                         memset(&ab, 0, sizeof(addrbookent));
1085
1086                         ab.VC = NewHash(0, lFlathash);
1087                         ab.ab_msgnum = Mime->msgnum;
1088
1089                         parse_vcard(Target, v, ab.VC, Mime);
1090
1091                         memset(&SubTP, 0, sizeof(WCTemplputParams));    
1092                         StackContext(TP, &SubTP, &ab, CTX_VCARD, 0, NULL);
1093
1094                         DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
1095                         UnStackContext(&SubTP);
1096                         DeleteHash(&ab.VC);
1097                         vcard_free(v);
1098
1099                 }
1100                 else {
1101                         StrBufPlain(Buf, _("failed to load vcard"), -1);
1102                 }
1103                 FreeStrBuf(&Mime->Data);
1104                 Mime->Data = Buf;
1105         }
1106
1107 }
1108
1109 void 
1110 ServerStartModule_VCARD
1111 (void)
1112 {
1113 }
1114
1115 void 
1116 ServerShutdownModule_VCARD
1117 (void)
1118 {
1119         DeleteHash(&DefineToToken);
1120         DeleteHash(&vcNames);
1121         DeleteHash(&VCTokenToDefine);
1122 }
1123
1124 void 
1125 InitModule_VCARD
1126 (void)
1127 {
1128         StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1129         DefineToToken   = NewHash(1, lFlathash);
1130         vcNames         = NewHash(1, lFlathash);
1131         VCTokenToDefine = NewHash(1, NULL);
1132         autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1133         FreeStrBuf(&Prefix);
1134
1135         REGISTERTokenParamDefine(NAMESPERPAGE);
1136
1137
1138         RegisterCTX(CTX_VCARD);
1139         RegisterCTX(CTX_VCARD_LIST);
1140         RegisterCTX(CTX_VCARD_TYPE);
1141
1142         RegisterReadLoopHandlerset(
1143                 VIEW_ADDRESSBOOK,
1144                 vcard_GetParamsGetServerCall,
1145                 NULL,
1146                 NULL,
1147                 NULL, 
1148                 vcard_LoadMsgFromServer,
1149                 vcard_RenderView_or_Tail,
1150                 vcard_Cleanup);
1151
1152         RegisterIterator("MAIL:VCARDS", 0, NULL, CtxGetVcardList, NULL, NULL, CTX_VCARD, CTX_VCARD_LIST, IT_NOFLAG);
1153
1154
1155         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1156         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1157         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1158
1159         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1160         RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1161         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1162         RegisterNamespace("VC:MSGNO", 0, 1, tmpl_vcard_msgno, NULL, CTX_VCARD);
1163         RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1164         REGISTERTokenParamDefine(FlatString);
1165         REGISTERTokenParamDefine(StringCluster);
1166         REGISTERTokenParamDefine(PhoneNumber);
1167         REGISTERTokenParamDefine(EmailAddr);
1168         REGISTERTokenParamDefine(Street);
1169         REGISTERTokenParamDefine(Number);
1170         REGISTERTokenParamDefine(AliasFor);
1171         REGISTERTokenParamDefine(Base64BinaryAttachment);
1172         REGISTERTokenParamDefine(TerminateList);
1173         REGISTERTokenParamDefine(Address);
1174
1175         RegisterConditional("VC:HAVE:TYPE",      1, conditional_VC_Havetype, CTX_VCARD);
1176         RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1177         RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1178
1179         RegisterMimeRenderer(HKEY("text/x-vcard"), render_MIME_VCard, 1, 201);
1180         RegisterMimeRenderer(HKEY("text/vcard"), render_MIME_VCard, 1, 200);
1181 }