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