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