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