448d1ebcff8815e057723786fd560b1d77e67afa
[citadel.git] / webcit / vcard_edit.c
1 /*
2  * Copyright (c) 1996-2012 by the citadel.org team
3  *
4  * This program is open source software.  You can redistribute it and/or
5  * modify it under the terms of the GNU General Public License, version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12
13 #include "webcit.h"
14 #include "webserver.h"
15 #include "calendar.h"
16
17 CtxType CTX_VCARD = CTX_NONE;
18 long VCEnumCounter = 0;
19
20 typedef enum _VCStrEnum {
21         FlatString,
22         StringCluster,
23         PhoneNumber,
24         EmailAddr,
25         Street,
26         Number,
27         AliasFor,
28         Base64BinaryAttachment,
29         TerminateList
30 }VCStrEnum;
31 typedef struct vcField vcField;
32 struct vcField {
33         ConstStr STR;
34         VCStrEnum Type;
35         vcField *Sub;
36         long cval;
37 };
38
39 vcField VCStr_Ns [] = {
40         {{HKEY("last")},   FlatString, NULL, 0},
41         {{HKEY("first")},  FlatString, NULL, 0},
42         {{HKEY("middle")}, FlatString, NULL, 0},
43         {{HKEY("prefix")}, FlatString, NULL, 0},
44         {{HKEY("suffix")}, FlatString, NULL, 0},
45         {{HKEY("")},       TerminateList, NULL, 0}
46 };
47
48 vcField VCStr_Addrs [] = {
49         {{HKEY("POBox")},   FlatString, NULL, 0},
50         {{HKEY("address")},  FlatString, NULL, 0},
51         {{HKEY("address2")}, FlatString, NULL, 0},
52         {{HKEY("city")}, FlatString, NULL, 0},
53         {{HKEY("state")}, FlatString, NULL, 0},
54         {{HKEY("zip")}, FlatString, NULL, 0},
55         {{HKEY("country")}, FlatString, NULL, 0},
56         {{HKEY("")},       TerminateList, NULL, 0}
57 };
58
59 vcField VCStrE [] = {
60         {{HKEY("n")},               StringCluster, VCStr_Ns, 0}, /* N is name, but only if there's no FN already there */
61         {{HKEY("fn")},              FlatString, NULL, 0}, /* FN (full name) is a true 'display name' field */
62         {{HKEY("title")},           FlatString, NULL, 0},   /* title */
63         {{HKEY("org")},             FlatString, NULL, 0},    /* organization */
64         {{HKEY("email")},           EmailAddr, NULL, 0},
65         {{HKEY("tel")},             PhoneNumber, NULL, 0},
66         {{HKEY("adr")},             StringCluster, VCStr_Addrs, 0},
67         {{HKEY("photo")},           Base64BinaryAttachment, NULL, 0},
68         {{HKEY("version")},         Number, NULL, 0},
69         {{HKEY("rev")},             Number, NULL, 0},
70         {{HKEY("label")},           FlatString, NULL, 0},
71         {{HKEY("uid")},             FlatString, NULL, 0},
72         {{HKEY("tel;home")},        PhoneNumber, NULL, 0},
73         {{HKEY("tel;work")},        PhoneNumber, NULL, 0},
74         {{HKEY("tel;fax")},         PhoneNumber, NULL, 0},
75         {{HKEY("tel;cell")},        PhoneNumber, NULL, 0},
76         {{HKEY("email;internet")},  EmailAddr, NULL, 0},
77         {{HKEY("")},        TerminateList, NULL, 0}
78 };
79
80 ConstStr VCStr [] = {
81         {HKEY("")},
82         {HKEY("n")}, /* N is name, but only if there's no FN already there */
83         {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
84         {HKEY("title")},   /* title */
85         {HKEY("org")},    /* organization */
86         {HKEY("email")},
87         {HKEY("tel")},
88         {HKEY("work")},
89         {HKEY("home")},
90         {HKEY("cell")},
91         {HKEY("adr")},
92         {HKEY("photo")},
93         {HKEY("version")},
94         {HKEY("rev")},
95         {HKEY("label")},
96         {HKEY("uid")}
97 };
98
99
100 HashList *DefineToToken = NULL;
101 HashList *VCTokenToDefine = NULL;
102 HashList *vcNames = NULL; /* todo: fill with the name strings */
103 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
104 {
105         RegisterTokenParamDefine(SKEY(name), vf->cval);
106         Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
107         syslog(LOG_DEBUG, "Token: %s -> %d, %d", 
108                ChrPtr(name),
109                vf->cval, 
110                inTokenCount);
111
112 }
113
114 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer)
115 {
116         int i = 0;
117         while (vf[i].STR.len > 0) {
118                 StrBuf *subStr = NewStrBuf();
119                 vf[i].cval = (*enumCounter) ++;
120                 StrBufAppendBuf(subStr, BaseStr, 0);
121                 if (StrLength(subStr) > 0) {
122                         StrBufAppendBufPlain(subStr, HKEY("."), 0);
123                 }
124                 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
125                 if (layer == 0) {
126                         Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
127                 }
128                 switch (vf[i].Type) {
129                 case FlatString:
130                         break;
131                 case StringCluster:
132                 {
133                         autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1);
134                         i++;
135                         continue;
136                 }
137                 break;
138                 case PhoneNumber:
139                         break;
140                 case EmailAddr:
141                         break;
142                 case Street:
143                         break;
144                 case Number:
145                         break;
146                 case AliasFor:
147                         break;
148                 case Base64BinaryAttachment:
149                         break;
150                 case TerminateList:
151                         break;
152                 }
153                 RegisterVCardToken(&vf[i], subStr, i);
154                 i++;
155         }
156 }
157
158 int preeval_vcard_item(WCTemplateToken *Token)
159 {
160         WCTemplputParams TPP;
161         WCTemplputParams *TP;
162         int searchFieldNo;
163         StrBuf *Target = NULL;
164
165         memset(&TPP, 0, sizeof(WCTemplputParams));
166         TP = &TPP;
167         TP->Tokens = Token;
168         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
169         if (searchFieldNo >= VCEnumCounter) {
170                 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
171                                  "Invalid define");
172                 return 0;
173         }
174         return 1;
175 }
176
177 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
178 {
179         void *vItem;
180         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
181         HashList *vc = (HashList*) CTX(CTX_VCARD);
182         if (GetHash(vc, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
183                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
184         }
185 }
186
187 int preeval_vcard_name_str(WCTemplateToken *Token)
188 {
189         WCTemplputParams TPP;
190         WCTemplputParams *TP;
191         int searchFieldNo;
192         StrBuf *Target = NULL;
193
194         memset(&TPP, 0, sizeof(WCTemplputParams));
195         TP = &TPP;
196         TP->Tokens = Token;
197         searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
198         if (searchFieldNo >= VCEnumCounter) {
199                 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
200                                  "Invalid define");
201                 return 0;
202         }
203         return 1;
204 }
205
206 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
207 {
208         void *vItem;
209         long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
210         /* todo: get descriptive string for this vcard type */
211         if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
212                 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
213         }
214 }
215
216
217 /*
218  * Record compare function for sorting address book indices
219  */
220 int abcmp(const void *ab1, const void *ab2) {
221         return(strcasecmp(
222                 (((const addrbookent *)ab1)->ab_name),
223                 (((const addrbookent *)ab2)->ab_name)
224         ));
225 }
226
227
228 /*
229  * Helper function for do_addrbook_view()
230  * Converts a name into a three-letter tab label
231  */
232 void nametab(char *tabbuf, long len, char *name) {
233         stresc(tabbuf, len, name, 0, 0);
234         tabbuf[0] = toupper(tabbuf[0]);
235         tabbuf[1] = tolower(tabbuf[1]);
236         tabbuf[2] = tolower(tabbuf[2]);
237         tabbuf[3] = 0;
238 }
239
240
241 /*
242  * If it's an old "Firstname Lastname" style record, try to convert it.
243  */
244 void lastfirst_firstlast(char *namebuf) {
245         char firstname[SIZ];
246         char lastname[SIZ];
247         int i;
248
249         if (namebuf == NULL) return;
250         if (strchr(namebuf, ';') != NULL) return;
251
252         i = num_tokens(namebuf, ' ');
253         if (i < 2) return;
254
255         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
256         remove_token(namebuf, i-1, ' ');
257         strcpy(firstname, namebuf);
258         sprintf(namebuf, "%s; %s", lastname, firstname);
259 }
260
261
262
263 wc_mime_attachment *load_vcard(message_summary *Msg) 
264 {
265         HashPos  *it;
266         StrBuf *FoundCharset = NewStrBuf();
267         StrBuf *Error;
268         void *vMime;
269         const char *Key;
270         long len;
271         wc_mime_attachment *Mime;
272         wc_mime_attachment *VCMime = NULL;
273
274         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
275         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
276         Msg->MsgBody->msgnum = Msg->msgnum;
277
278         load_message(Msg, FoundCharset, &Error);
279
280         FreeStrBuf(&FoundCharset);
281         /* look up the vcard... */
282         it = GetNewHashPos(Msg->AllAttach, 0);
283         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
284                (vMime != NULL)) 
285         {
286                 Mime = (wc_mime_attachment*) vMime;
287                 if ((strcmp(ChrPtr(Mime->ContentType),
288                            "text/x-vcard") == 0) ||
289                     (strcmp(ChrPtr(Mime->ContentType),
290                             "text/vcard") == 0))
291                 {
292                         VCMime = Mime;
293                         break;
294                 }
295         }
296         DeleteHashPos(&it);
297         if (VCMime == NULL)
298                 return NULL;
299
300         if (VCMime->Data == NULL)
301                 MimeLoadData(VCMime);
302         return VCMime;
303 }
304
305 /*
306  * fetch the display name off a vCard
307  */
308 void fetch_ab_name(message_summary *Msg, char **namebuf) {
309         long len;
310         int i;
311         wc_mime_attachment *VCMime = NULL;
312
313         if (namebuf == NULL) return;
314
315         VCMime = load_vcard(Msg);
316         if (VCMime == NULL)
317                 return;
318
319         /* Grab the name off the card */
320         display_vcard(WC->WBuf, VCMime, 0, 0, namebuf, Msg->msgnum);
321
322         if (*namebuf != NULL) {
323                 lastfirst_firstlast(*namebuf);
324                 striplt(*namebuf);
325                 len = strlen(*namebuf);
326                 for (i=0; i<len; ++i) {
327                         if ((*namebuf)[i] != ';') return;
328                 }
329                 free (*namebuf);
330                 (*namebuf) = strdup(_("(no name)"));
331         }
332         else {
333                 (*namebuf) = strdup(_("(no name)"));
334         }
335 }
336
337
338
339 /*
340  * Turn a vCard "n" (name) field into something displayable.
341  */
342 void vcard_n_prettyize(char *name)
343 {
344         char *original_name;
345         int i, j, len;
346
347         original_name = strdup(name);
348         len = strlen(original_name);
349         for (i=0; i<5; ++i) {
350                 if (len > 0) {
351                         if (original_name[len-1] == ' ') {
352                                 original_name[--len] = 0;
353                         }
354                         if (original_name[len-1] == ';') {
355                                 original_name[--len] = 0;
356                         }
357                 }
358         }
359         strcpy(name, "");
360         j=0;
361         for (i=0; i<len; ++i) {
362                 if (original_name[i] == ';') {
363                         name[j++] = ',';
364                         name[j++] = ' ';                        
365                 }
366                 else {
367                         name[j++] = original_name[i];
368                 }
369         }
370         name[j] = '\0';
371         free(original_name);
372 }
373
374
375
376
377 /*
378  * preparse a vcard name
379  * display_vcard() calls this after parsing the textual vCard into
380  * our 'struct vCard' data object.
381  * This gets called instead of display_parsed_vcard() if we are only looking
382  * to extract the person's name instead of displaying the card.
383  */
384 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
385         char *name;
386         char *prop;
387         char buf[SIZ];
388         int j, n, len;
389         int is_qp = 0;
390         int is_b64 = 0;
391
392         *storename = NULL;
393
394         name = vcard_get_prop(v, "n", 1, 0, 0);
395         if (name != NULL) {
396                 len = strlen(name);
397                 prop = vcard_get_prop(v, "n", 1, 0, 1);
398                 n = num_tokens(prop, ';');
399
400                 for (j=0; j<n; ++j) {
401                         extract_token(buf, prop, j, ';', sizeof buf);
402                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
403                                 is_qp = 1;
404                         }
405                         if (!strcasecmp(buf, "encoding=base64")) {
406                                 is_b64 = 1;
407                         }
408                 }
409                 if (is_qp) {
410                         /* %ff can become 6 bytes in utf8  */
411                         *storename = malloc(len * 2 + 3); 
412                         j = CtdlDecodeQuotedPrintable(
413                                 *storename, name,
414                                 len);
415                         (*storename)[j] = 0;
416                 }
417                 else if (is_b64) {
418                         /* ff will become one byte.. */
419                         *storename = malloc(len + 50);
420                         CtdlDecodeBase64(
421                                 *storename, name,
422                                 len);
423                 }
424                 else {
425                         size_t len;
426
427                         len = strlen (name);
428                         
429                         *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
430                         memcpy(*storename, name, len + 1);
431                 }
432                 /* vcard_n_prettyize(storename); */
433         }
434
435 }
436
437
438
439 /*
440  * html print a vcard
441  * display_vcard() calls this after parsing the textual vCard into
442  * our 'struct vCard' data object.
443  *
444  * Set 'full' to nonzero to display the full card, otherwise it will only
445  * show a summary line.
446  *
447  * This code is a bit ugly, so perhaps an explanation is due: we do this
448  * in two passes through the vCard fields.  On the first pass, we process
449  * fields we understand, and then render them in a pretty fashion at the
450  * end.  Then we make a second pass, outputting all the fields we don't
451  * understand in a simple two-column name/value format.
452  * v            the vCard to display
453  * full         display all items of the vcard?
454  * msgnum       Citadel message pointer
455  */
456 void display_parsed_vcard(StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
457 {
458         int i, j;
459         char buf[SIZ];
460         char *name;
461         int is_qp = 0;
462         int is_b64 = 0;
463         char *thisname, *thisvalue;
464         char firsttoken[SIZ];
465         int pass;
466
467         char fullname[SIZ];
468         char title[SIZ];
469         char org[SIZ];
470         char phone[SIZ];
471         char mailto[SIZ];
472
473         strcpy(fullname, "");
474         strcpy(phone, "");
475         strcpy(mailto, "");
476         strcpy(title, "");
477         strcpy(org, "");
478
479         if (!full) {
480                 StrBufAppendPrintf(Target, "<td>");
481                 name = vcard_get_prop(v, "fn", 1, 0, 0);
482                 if (name != NULL) {
483                         StrEscAppend(Target, NULL, name, 0, 0);
484                 }
485                 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
486                         strcpy(fullname, name);
487                         vcard_n_prettyize(fullname);
488                         StrEscAppend(Target, NULL, fullname, 0, 0);
489                 }
490                 else {
491                         StrBufAppendPrintf(Target, "&nbsp;");
492                 }
493                 StrBufAppendPrintf(Target, "</td>");
494                 return;
495         }
496
497         StrBufAppendPrintf(Target, "<div align=\"center\">"
498                 "<table bgcolor=\"#aaaaaa\" width=\"50%%\">");
499         for (pass=1; pass<=2; ++pass) {
500
501                 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
502                         int len;
503                         thisname = strdup(v->prop[i].name);
504                         extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
505         
506                         for (j=0; j<num_tokens(thisname, ';'); ++j) {
507                                 extract_token(buf, thisname, j, ';', sizeof buf);
508                                 if (!strcasecmp(buf, "encoding=quoted-printable")) {
509                                         is_qp = 1;
510                                         remove_token(thisname, j, ';');
511                                 }
512                                 if (!strcasecmp(buf, "encoding=base64")) {
513                                         is_b64 = 1;
514                                         remove_token(thisname, j, ';');
515                                 }
516                         }
517                         
518                         len = strlen(v->prop[i].value);
519                         /* if we have some untagged QP, detect it here. */
520                         if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL))
521                                 utf8ify_rfc822_string(&v->prop[i].value);
522
523                         if (is_qp) {
524                                 /* %ff can become 6 bytes in utf8 */
525                                 thisvalue = malloc(len * 2 + 3); 
526                                 j = CtdlDecodeQuotedPrintable(
527                                         thisvalue, v->prop[i].value,
528                                         len);
529                                 thisvalue[j] = 0;
530                         }
531                         else if (is_b64) {
532                                 /* ff will become one byte.. */
533                                 thisvalue = malloc(len + 50);
534                                 CtdlDecodeBase64(
535                                         thisvalue, v->prop[i].value,
536                                         strlen(v->prop[i].value) );
537                         }
538                         else {
539                                 thisvalue = strdup(v->prop[i].value);
540                         }
541         
542                         /* Various fields we may encounter ***/
543         
544                         /* N is name, but only if there's no FN already there */
545                         if (!strcasecmp(firsttoken, "n")) {
546                                 if (IsEmptyStr(fullname)) {
547                                         strcpy(fullname, thisvalue);
548                                         vcard_n_prettyize(fullname);
549                                 }
550                         }
551         
552                         /* FN (full name) is a true 'display name' field */
553                         else if (!strcasecmp(firsttoken, "fn")) {
554                                 strcpy(fullname, thisvalue);
555                         }
556
557                         /* title */
558                         else if (!strcasecmp(firsttoken, "title")) {
559                                 strcpy(title, thisvalue);
560                         }
561         
562                         /* organization */
563                         else if (!strcasecmp(firsttoken, "org")) {
564                                 strcpy(org, thisvalue);
565                         }
566         
567                         else if (!strcasecmp(firsttoken, "email")) {
568                                 size_t len;
569                                 if (!IsEmptyStr(mailto)) strcat(mailto, "<br>");
570                                 strcat(mailto,
571                                         "<a href=\"display_enter"
572                                         "?force_room=_MAIL_?recp=");
573
574                                 len = strlen(mailto);
575                                 urlesc(&mailto[len], SIZ - len, "\"");
576                                 len = strlen(mailto);
577                                 urlesc(&mailto[len], SIZ - len,  fullname);
578                                 len = strlen(mailto);
579                                 urlesc(&mailto[len], SIZ - len, "\" <");
580                                 len = strlen(mailto);
581                                 urlesc(&mailto[len], SIZ - len, thisvalue);
582                                 len = strlen(mailto);
583                                 urlesc(&mailto[len], SIZ - len, ">");
584
585                                 strcat(mailto, "\">");
586                                 len = strlen(mailto);
587                                 stresc(mailto+len, SIZ - len, thisvalue, 1, 1);
588                                 strcat(mailto, "</A>");
589                         }
590                         else if (!strcasecmp(firsttoken, "tel")) {
591                                 if (!IsEmptyStr(phone)) strcat(phone, "<br>");
592                                 strcat(phone, thisvalue);
593                                 for (j=0; j<num_tokens(thisname, ';'); ++j) {
594                                         extract_token(buf, thisname, j, ';', sizeof buf);
595                                         if (!strcasecmp(buf, "tel"))
596                                                 strcat(phone, "");
597                                         else if (!strcasecmp(buf, "work"))
598                                                 strcat(phone, _(" (work)"));
599                                         else if (!strcasecmp(buf, "home"))
600                                                 strcat(phone, _(" (home)"));
601                                         else if (!strcasecmp(buf, "cell"))
602                                                 strcat(phone, _(" (cell)"));
603                                         else {
604                                                 strcat(phone, " (");
605                                                 strcat(phone, buf);
606                                                 strcat(phone, ")");
607                                         }
608                                 }
609                         }
610                         else if (!strcasecmp(firsttoken, "adr")) {
611                                 if (pass == 2) {
612                                         StrBufAppendPrintf(Target, "<tr><td>");
613                                         StrBufAppendPrintf(Target, _("Address:"));
614                                         StrBufAppendPrintf(Target, "</td><td>");
615                                         for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
616                                                 extract_token(buf, thisvalue, j, ';', sizeof buf);
617                                                 if (!IsEmptyStr(buf)) {
618                                                         StrEscAppend(Target, NULL, buf, 0, 0);
619                                                         if (j<3) StrBufAppendPrintf(Target, "<br>");
620                                                         else StrBufAppendPrintf(Target, " ");
621                                                 }
622                                         }
623                                         StrBufAppendPrintf(Target, "</td></tr>\n");
624                                 }
625                         }
626                         /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { 
627                                 // Only output on second pass
628                                 StrBufAppendPrintf(Target, "<tr><td>");
629                                 StrBufAppendPrintf(Target, _("Photo:"));
630                                 StrBufAppendPrintf(Target, "</td><td>");
631                                 StrBufAppendPrintf(Target, "<img src=\"/vcardphoto/%ld/\" alt=\"Contact photo\"/>",msgnum);
632                                 StrBufAppendPrintf(Target, "</td></tr>\n");
633                         } */
634                         else if (!strcasecmp(firsttoken, "version")) {
635                                 /* ignore */
636                         }
637                         else if (!strcasecmp(firsttoken, "rev")) {
638                                 /* ignore */
639                         }
640                         else if (!strcasecmp(firsttoken, "label")) {
641                                 /* ignore */
642                         }
643                         else {
644
645                                 /*** Don't show extra fields.  They're ugly.
646                                 if (pass == 2) {
647                                         StrBufAppendPrintf(Target, "<TR><TD>");
648                                         StrEscAppend(Target, NULL, thisname, 0, 0);
649                                         StrBufAppendPrintf(Target, "</TD><TD>");
650                                         StrEscAppend(Target, NULL, thisvalue, 0, 0);
651                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
652                                 }
653                                 ***/
654                         }
655         
656                         free(thisname);
657                         free(thisvalue);
658                 }
659         
660                 if (pass == 1) {
661                         StrBufAppendPrintf(Target, "<tr bgcolor=\"#aaaaaa\">"
662       "<td colspan=2 bgcolor=\"#ffffff\">"
663       "<img align=\"center\" src=\"static/webcit_icons/essen/32x32/contact.png\">"
664       "<font size=\"+1\"><b>");
665                         StrEscAppend(Target, NULL, fullname, 0, 0);
666                         StrBufAppendPrintf(Target, "</b></font>");
667                         if (!IsEmptyStr(title)) {
668                                 StrBufAppendPrintf(Target, "<div align=\"right>\"");
669                                 StrEscAppend(Target, NULL, title, 0, 0);
670                                 StrBufAppendPrintf(Target, "</div>");
671                         }
672                         if (!IsEmptyStr(org)) {
673                                 StrBufAppendPrintf(Target, "<div align=\"right\">");
674                                 StrEscAppend(Target, NULL, org, 0, 0);
675                                 StrBufAppendPrintf(Target, "</div>");
676                         }
677                         StrBufAppendPrintf(Target, "</td></tr>\n");
678                 
679                         if (!IsEmptyStr(phone)) {
680                                 StrBufAppendPrintf(Target, "<tr><td>");
681                                 StrBufAppendPrintf(Target, _("Telephone:"));
682                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", phone);
683                         }
684                         if (!IsEmptyStr(mailto)) {
685                                 StrBufAppendPrintf(Target, "<tr><td>");
686                                 StrBufAppendPrintf(Target, _("E-mail:"));
687                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", mailto);
688                         }
689                 }
690
691         }
692
693         StrBufAppendPrintf(Target, "</table></div>\n");
694 }
695
696
697 void PutVcardItem(vcField *thisField, HashList *thisVC, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
698 {
699         /* if we have some untagged QP, detect it here. */
700         if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
701                 StrBuf *b;
702                 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
703                 b = ThisFieldStr;
704                 ThisFieldStr = Swap; 
705                 Swap = b;
706                 FlushStrBuf(Swap);
707         }
708         Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
709 }
710 /*
711  * html print a vcard
712  * display_vcard() calls this after parsing the textual vCard into
713  * our 'struct vCard' data object.
714  *
715  * Set 'full' to nonzero to display the full card, otherwise it will only
716  * show a summary line.
717  *
718  * This code is a bit ugly, so perhaps an explanation is due: we do this
719  * in two passes through the vCard fields.  On the first pass, we process
720  * fields we understand, and then render them in a pretty fashion at the
721  * end.  Then we make a second pass, outputting all the fields we don't
722  * understand in a simple two-column name/value format.
723  * v            the vCard to display
724  * full         display all items of the vcard?
725  * msgnum       Citadel message pointer
726  */
727 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, int full, wc_mime_attachment *Mime)
728 {
729         StrBuf *Val = NULL;
730         StrBuf *Swap = NULL;
731         int i, j, k;
732         char buf[20]; //SIZ];
733         int is_qp = 0;
734         int is_b64 = 0;
735         int ntokens, len;
736         StrBuf *thisname = NULL;
737         char firsttoken[20]; ///SIZ];
738         void *V;
739         HashList *thisVC;
740         StrBuf *thisVCToken;
741         void *vField = NULL;
742
743         thisVC = NewHash(1, lFlathash);
744         Swap = NewStrBuf ();
745         thisname = NewStrBuf();
746         thisVCToken = NewStrBufPlain(NULL, 63);
747         for (i=0; i<(v->numprops); ++i) {
748                 FlushStrBuf(thisVCToken);
749                 is_qp = 0;
750                 is_b64 = 0;
751                 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
752                 StrBufPlain(thisname, v->prop[i].name, -1);
753                 StrBufLowerCase(thisname);
754                 
755                 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
756                 ntokens = num_tokens(ChrPtr(thisname), ';');
757                 for (j=0, k=0; j < ntokens && k < 10; ++j) {
758                         int evc[10];
759                         
760                         len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
761                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
762                                 is_qp = 1;
763 /*                              remove_token(thisname, j, ';');*/
764                         }
765                         else if (!strcasecmp(buf, "encoding=base64")) {
766                                 is_b64 = 1;
767 /*                              remove_token(thisname, j, ';');*/
768                         }
769                         else{
770                                 if (StrLength(thisVCToken) > 0) {
771                                         StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
772                                 }
773                                 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
774                                 /*
775                                 if (GetHash(VCToEnum, buf, len, &V))
776                                 {
777                                         evc[k] = (int) V;
778
779                                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
780
781                                         syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
782                                         k++;
783                                 }
784 */
785
786                         }
787                 }
788
789                 vField = NULL;  
790                 if ((StrLength(thisVCToken) > 0) &&
791                     GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) && 
792                     (vField != NULL)) {
793                         vcField *thisField = (vcField *)vField;
794                         StrBuf *ThisFieldStr = NULL;
795                         syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
796                         switch (thisField->Type) {
797                         case StringCluster: {
798                                 int i = 0;
799                                 const char *Pos = NULL;
800
801                                 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
802                                 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
803                                 while (thisField->Sub[i].STR.len > 0) {
804                                         StrBufExtract_NextToken(thisArray, Buf, &Pos, ';');
805                                         ThisFieldStr = NewStrBufDup(Buf);
806                                         
807                                         PutVcardItem(&thisField->Sub[i], thisVC, ThisFieldStr, is_qp, Swap);
808                                         i++;
809                                 }
810                         }
811                                 break;
812                         case FlatString:
813                         case PhoneNumber:
814                         case EmailAddr:
815                         case Street:
816                         case Number:
817                         case AliasFor:
818                                 /* copy over the payload into a StrBuf */
819                                 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
820                                 PutVcardItem(thisField, thisVC, ThisFieldStr, is_qp, Swap);
821
822                                 break;
823                         case Base64BinaryAttachment:
824                         case TerminateList:
825                                 break;
826                         }
827
828                 }
829                 /* copy over the payload into a StrBuf */
830                 Val = NewStrBufPlain(v->prop[i].value, -1);
831                         
832                 /* if we have some untagged QP, detect it here. */
833                 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
834                         StrBuf *b;
835                         StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
836                         b = Val;
837                         Val = Swap; 
838                         Swap = b;
839                         FlushStrBuf(Swap);
840                 }
841                 else if (is_b64) {
842                         StrBufDecodeBase64(Val);
843
844                 }
845 #if 0
846                 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
847                         firsttoken,
848                        ChrPtr(thisname),
849                         ChrPtr(Val),
850                         v->prop[i].value);
851                 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
852                 {
853                         eVC evc = (eVC) V;
854                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
855                         syslog(LOG_DEBUG, "[%ul]\n", evc);
856                         Val = NULL;
857                 }
858                 else
859                         syslog(LOG_DEBUG, "[]\n");
860 /*
861 TODO: check for layer II
862                 else 
863                 {
864                         long max = num_tokens(thisname, ';');
865                         firsttoken[len] = '_';
866
867                         for (j = 0; j < max; j++) {
868 //                      firsttoken[len]
869
870                                 extract_token(buf, thisname, j, ';', sizeof (buf));
871                                         if (!strcasecmp(buf, "tel"))
872                                                 strcat(phone, "");
873                                         else if (!strcasecmp(buf, "work"))
874                                                 strcat(phone, _(" (work)"));
875                                         else if (!strcasecmp(buf, "home"))
876                                                 strcat(phone, _(" (home)"));
877                                         else if (!strcasecmp(buf, "cell"))
878                                                 strcat(phone, _(" (cell)"));
879                                         else {
880                                                 strcat(phone, " (");
881                                                 strcat(phone, buf);
882                                                 strcat(phone, ")");
883                                         }
884                                 }
885                         }
886
887                 }
888 */
889 #endif  
890                 FreeStrBuf(&Val);
891                 ////free(thisname);
892                 /// thisname = NULL;
893         }
894
895
896         {
897                 WCTemplputParams *TP = NULL;
898                 WCTemplputParams SubTP;
899                 FlushStrBuf(Target);
900                 StackContext(TP, &SubTP, thisVC, CTX_VCARD, 0, NULL);
901                 {
902                         DoTemplate(HKEY("test_vcard"), Target, &SubTP);
903                 }
904                 UnStackContext(&SubTP);
905         }
906         printf("%s\n", ChrPtr(Target));
907         FreeStrBuf(&thisVCToken);
908         DeleteHash(&thisVC);/// todo
909 }
910
911 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
912 {
913         HashList *VC = CTX(CTX_VCARD);
914         int evc;
915         void *vStr;
916
917         evc = GetTemplateTokenNumber(Target, TP, 0, -1);
918         if (evc != -1)
919         {
920                 if (GetHash(VC, IKEY(evc), &vStr))
921                 {
922                         StrBufAppendTemplate(Target, TP,
923                                              (StrBuf*) vStr,
924                                              1);
925                 }
926         }
927         
928 }
929
930 void new_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
931 {
932         HashList *VC;
933         WCTemplputParams SubTP;
934
935         memset(&SubTP, 0, sizeof(WCTemplputParams));    
936
937
938         VC = NewHash(0, Flathash);
939         parse_vcard(Target, v, VC, full, Mime);
940
941         SubTP.Filter.ContextType = CTX_VCARD;
942         SubTP.Context = VC;
943
944         DoTemplate(HKEY("test_vcard"), Target, &SubTP);
945         DeleteHash(&VC);
946 }
947
948
949
950 /*
951  * Display a textual vCard
952  * (Converts to a vCard object and then calls the actual display function)
953  * Set 'full' to nonzero to display the whole card instead of a one-liner.
954  * Or, if "storename" is non-NULL, just store the person's name in that
955  * buffer instead of displaying the card at all.
956  *
957  * vcard_source the buffer containing the vcard text
958  * alpha        Display only if name begins with this letter of the alphabet
959  * full         Display the full vCard (otherwise just the display name)
960  * storename    If not NULL, also store the display name here
961  * msgnum       Citadel message pointer
962  */
963 void display_vcard(StrBuf *Target, 
964                    wc_mime_attachment *Mime, 
965                    char alpha, 
966                    int full, 
967                    char **storename, 
968                    long msgnum) 
969 {
970         struct vCard *v;
971         char *name;
972         StrBuf *Buf;
973         StrBuf *Buf2;
974         char this_alpha = 0;
975
976         v = VCardLoad(Mime->Data);
977
978         if (v == NULL) return;
979
980         name = vcard_get_prop(v, "n", 1, 0, 0);
981         if (name != NULL) {
982                 Buf = NewStrBufPlain(name, -1);
983                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
984                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
985                 this_alpha = ChrPtr(Buf)[0];
986                 FreeStrBuf(&Buf);
987                 FreeStrBuf(&Buf2);
988         }
989
990         if (storename != NULL) {
991                 fetchname_parsed_vcard(v, storename);
992         }
993         else if ((alpha == 0) || 
994                  ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) || 
995                  ((!isalpha(alpha)) && (!isalpha(this_alpha)))
996                 ) 
997         {
998 #define XXX_XXX 1
999 #ifdef XXX_XXX
1000                 new_vcard (Target, v, full, Mime);
1001 #else
1002                 display_parsed_vcard(Target, v, full, Mime);
1003 #endif          
1004         }
1005
1006         vcard_free(v);
1007 }
1008
1009
1010
1011 /*
1012  * Render the address book using info we gathered during the scan
1013  *
1014  * addrbook     the addressbook to render
1015  * num_ab       the number of the addressbook
1016  */
1017 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
1018         int i = 0;
1019         int displayed = 0;
1020         int bg = 0;
1021         static int NAMESPERPAGE = 60;
1022         int num_pages = 0;
1023         int tabfirst = 0;
1024         char tabfirst_label[64];
1025         int tablast = 0;
1026         char tablast_label[64];
1027         char this_tablabel[64];
1028         int page = 0;
1029         char **tablabels;
1030
1031         if (num_ab == 0) {
1032                 wc_printf("<br><br><br><div align=\"center\"><i>");
1033                 wc_printf(_("This address book is empty."));
1034                 wc_printf("</i></div>\n");
1035                 return;
1036         }
1037
1038         if (num_ab > 1) {
1039                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
1040         }
1041
1042         num_pages = (num_ab / NAMESPERPAGE) + 1;
1043
1044         tablabels = malloc(num_pages * sizeof (char *));
1045         if (tablabels == NULL) {
1046                 wc_printf("<br><br><br><div align=\"center\"><i>");
1047                 wc_printf(_("An internal error has occurred."));
1048                 wc_printf("</i></div>\n");
1049                 return;
1050         }
1051
1052         for (i=0; i<num_pages; ++i) {
1053                 tabfirst = i * NAMESPERPAGE;
1054                 tablast = tabfirst + NAMESPERPAGE - 1;
1055                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
1056                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
1057                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
1058                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
1059                 tablabels[i] = strdup(this_tablabel);
1060         }
1061
1062         tabbed_dialog(num_pages, tablabels);
1063         page = (-1);
1064
1065         for (i=0; i<num_ab; ++i) {
1066
1067                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
1068                         page = (i / NAMESPERPAGE);
1069                         if (page > 0) {
1070                                 wc_printf("</tr></table>\n");
1071                                 end_tab(page-1, num_pages);
1072                         }
1073                         begin_tab(page, num_pages);
1074                         wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
1075                         displayed = 0;
1076                 }
1077
1078                 if ((displayed % 4) == 0) {
1079                         if (displayed > 0) {
1080                                 wc_printf("</tr>\n");
1081                         }
1082                         bg = 1 - bg;
1083                         wc_printf("<tr bgcolor=\"#%s\">",
1084                                 (bg ? "dddddd" : "ffffff")
1085                         );
1086                 }
1087         
1088                 wc_printf("<td>");
1089
1090                 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
1091                         addrbook[i].ab_msgnum);
1092                 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
1093                 vcard_n_prettyize(addrbook[i].ab_name);
1094                 escputs(addrbook[i].ab_name);
1095                 wc_printf("</a></td>\n");
1096                 ++displayed;
1097         }
1098
1099         /* Placeholders for empty columns at end */
1100         if ((num_ab % 4) != 0) {
1101                 for (i=0; i<(4-(num_ab % 4)); ++i) {
1102                         wc_printf("<td>&nbsp;</td>");
1103                 }
1104         }
1105
1106         wc_printf("</tr></table>\n");
1107         end_tab((num_pages-1), num_pages);
1108
1109         begin_tab(num_pages, num_pages);
1110         /* FIXME there ought to be something here */
1111         end_tab(num_pages, num_pages);
1112
1113         for (i=0; i<num_pages; ++i) {
1114                 free(tablabels[i]);
1115         }
1116         free(tablabels);
1117 }
1118
1119
1120
1121
1122 /*
1123  * Edit the vCard component of a MIME message.  
1124  * Supply the message number
1125  * and MIME part number to fetch.  Or, specify -1 for the message number
1126  * to start with a blank card.
1127  */
1128 void do_edit_vcard(long msgnum, char *partnum, 
1129                    message_summary *VCMsg,
1130                    wc_mime_attachment *VCAtt,
1131                    const char *return_to, 
1132                    const char *force_room) {
1133         wcsession *WCC = WC;
1134         message_summary *Msg = NULL;
1135         wc_mime_attachment *VCMime = NULL;
1136         struct vCard *v;
1137         int i;
1138         char *key, *value;
1139         char whatuser[256];
1140
1141         char lastname[256];
1142         char firstname[256];
1143         char middlename[256];
1144         char prefix[256];
1145         char suffix[256];
1146         char pobox[256];
1147         char extadr[256];
1148         char street[256];
1149         char city[256];
1150         char state[256];
1151         char zipcode[256];
1152         char country[256];
1153         char hometel[256];
1154         char worktel[256];
1155         char faxtel[256];
1156         char mobiletel[256];
1157         char primary_inetemail[256];
1158         char other_inetemail[SIZ];
1159         char extrafields[SIZ];
1160         char fullname[256];
1161         char title[256];
1162         char org[256];
1163
1164         lastname[0] = 0;
1165         firstname[0] = 0;
1166         middlename[0] = 0;
1167         prefix[0] = 0;
1168         suffix[0] = 0;
1169         pobox[0] = 0;
1170         extadr[0] = 0;
1171         street[0] = 0;
1172         city[0] = 0;
1173         state[0] = 0;
1174         zipcode[0] = 0;
1175         country[0] = 0;
1176         hometel[0] = 0;
1177         worktel[0] = 0;
1178         faxtel[0] = 0;
1179         mobiletel[0] = 0;
1180         primary_inetemail[0] = 0;
1181         other_inetemail[0] = 0;
1182         title[0] = 0;
1183         org[0] = 0;
1184         extrafields[0] = 0;
1185         fullname[0] = 0;
1186
1187         safestrncpy(whatuser, "", sizeof whatuser);
1188
1189         if ((msgnum >= 0) || 
1190             ((VCMsg != NULL) && (VCAtt != NULL)))
1191         {
1192                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1193
1194                         Msg = (message_summary *) malloc(sizeof(message_summary));
1195                         memset(Msg, 0, sizeof(message_summary));
1196                         Msg->msgnum = msgnum;
1197                         VCMime = load_vcard(Msg);
1198                         if (VCMime == NULL) {
1199                                 convenience_page("770000", _("Error"), "");///TODO: important message
1200                                 DestroyMessageSummary(Msg);
1201                                 return;
1202                         }
1203                 
1204                         v = VCardLoad(VCMime->Data);
1205                 }
1206                 else {
1207                         v = VCardLoad(VCAtt->Data);
1208                 }
1209         
1210                 /* Populate the variables for our form */
1211                 i = 0;
1212                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
1213                         char prp[256];  /* property name */
1214                         char prm[256];  /* parameters */
1215
1216                         value = vcard_get_prop(v, "", 0, i++, 0);
1217
1218
1219                         extract_token(prp, key, 0, ';', sizeof prp);
1220                         safestrncpy(prm, key, sizeof prm);
1221                         remove_token(prm, 0, ';');
1222
1223                         if (!strcasecmp(prp, "n")) {
1224                                 extract_token(lastname, value, 0, ';', sizeof lastname);
1225                                 extract_token(firstname, value, 1, ';', sizeof firstname);
1226                                 extract_token(middlename, value, 2, ';', sizeof middlename);
1227                                 extract_token(prefix, value, 3, ';', sizeof prefix);
1228                                 extract_token(suffix, value, 4, ';', sizeof suffix);
1229                         }
1230
1231                         else if (!strcasecmp(prp, "fn")) {
1232                                 safestrncpy(fullname, value, sizeof fullname);
1233                         }
1234
1235                         else if (!strcasecmp(prp, "title")) {
1236                                 safestrncpy(title, value, sizeof title);
1237                         }
1238         
1239                         else if (!strcasecmp(prp, "org")) {
1240                                 safestrncpy(org, value, sizeof org);
1241                         }
1242         
1243                         else if (!strcasecmp(prp, "adr")) {
1244                                 extract_token(pobox, value, 0, ';', sizeof pobox);
1245                                 extract_token(extadr, value, 1, ';', sizeof extadr);
1246                                 extract_token(street, value, 2, ';', sizeof street);
1247                                 extract_token(city, value, 3, ';', sizeof city);
1248                                 extract_token(state, value, 4, ';', sizeof state);
1249                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
1250                                 extract_token(country, value, 6, ';', sizeof country);
1251                         }
1252
1253                         else if (!strcasecmp(prp, "tel")) {
1254
1255                                 if (bmstrcasestr(prm, "home")) {
1256                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1257                                 }
1258                                 else if (bmstrcasestr(prm, "work")) {
1259                                         extract_token(worktel, value, 0, ';', sizeof worktel);
1260                                 }
1261                                 else if (bmstrcasestr(prm, "fax")) {
1262                                         extract_token(faxtel, value, 0, ';', sizeof faxtel);
1263                                 }
1264                                 else if (bmstrcasestr(prm, "cell")) {
1265                                         extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
1266                                 }
1267                                 else {  /* Missing or unknown type; put it in the home phone */
1268                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1269                                 }
1270                         }
1271         
1272                         else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
1273                                 if (primary_inetemail[0] == 0) {
1274                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
1275                                 }
1276                                 else {
1277                                         if (other_inetemail[0] != 0) {
1278                                                 strcat(other_inetemail, "\n");
1279                                         }
1280                                         strcat(other_inetemail, value);
1281                                 }
1282                         }
1283
1284                         /* Unrecognized properties are preserved here so we don't discard them
1285                          * just because the vCard was edited with WebCit.
1286                          */
1287                         else {
1288                                 strcat(extrafields, key);
1289                                 strcat(extrafields, ":");
1290                                 strcat(extrafields, value);
1291                                 strcat(extrafields, "\n");
1292                         }
1293         
1294                 }
1295         
1296                 vcard_free(v);
1297         }
1298
1299         /* Display the form */
1300         output_headers(1, 1, 1, 0, 0, 0);
1301
1302         do_template("box_begin_1");
1303         StrBufAppendBufPlain(WC->WBuf, _("Edit contact information"), -1, 0);
1304         do_template("box_begin_2");
1305
1306         wc_printf("<form method=\"POST\" action=\"submit_vcard\">\n");
1307         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
1308
1309         if (force_room != NULL) {
1310                 wc_printf("<input type=\"hidden\" name=\"force_room\" value=\"");
1311                 escputs(force_room);
1312                 wc_printf("\">\n");
1313         }
1314         else
1315         {
1316                 wc_printf("<input type=\"hidden\" name=\"go\" value=\"");
1317                 StrEscAppend(WCC->WBuf, WCC->CurRoom.name, NULL, 0, 0);
1318                 wc_printf("\">\n");
1319         }
1320
1321         wc_printf("<table class=\"vcard_edit_background\"><tr><td>\n");
1322
1323         wc_printf("<table border=\"0\"><tr>"
1324                 "<td>%s</td>"
1325                 "<td>%s</td>"
1326                 "<td>%s</td>"
1327                 "<td>%s</td>"
1328                 "<td>%s</td></tr>\n",
1329                 _("Prefix"), _("First Name"), _("Middle Name"), _("Last Name"), _("Suffix")
1330         );
1331         wc_printf("<tr><td><input type=\"text\" name=\"prefix\" "
1332                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
1333                 prefix);
1334         wc_printf("<td><input type=\"text\" name=\"firstname\" "
1335                 "value=\"%s\" maxlength=\"29\"></td>",
1336                 firstname);
1337         wc_printf("<td><input type=\"text\" name=\"middlename\" "
1338                 "value=\"%s\" maxlength=\"29\"></td>",
1339                 middlename);
1340         wc_printf("<td><input type=\"text\" name=\"lastname\" "
1341                 "value=\"%s\" maxlength=\"29\"></td>",
1342                 lastname);
1343         wc_printf("<td><input type=\"text\" name=\"suffix\" "
1344                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
1345                 suffix);
1346
1347         wc_printf("<table  class=\"vcard_edit_background_alt\">");
1348         wc_printf("<tr><td>");
1349
1350         wc_printf(_("Display name:"));
1351         wc_printf("<br>"
1352                 "<input type=\"text\" name=\"fullname\" "
1353                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1354                 fullname
1355         );
1356
1357         wc_printf(_("Title:"));
1358         wc_printf("<br>"
1359                 "<input type=\"text\" name=\"title\" "
1360                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1361                 title
1362         );
1363
1364         wc_printf(_("Organization:"));
1365         wc_printf("<br>"
1366                 "<input type=\"text\" name=\"org\" "
1367                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1368                 org
1369         );
1370
1371         wc_printf("</td><td>");
1372
1373         wc_printf("<table border=\"0\">");
1374         wc_printf("<tr><td>");
1375         wc_printf(_("PO box:"));
1376         wc_printf("</td><td>"
1377                 "<input type=\"text\" name=\"pobox\" "
1378                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1379                 pobox);
1380         wc_printf("<tr><td>");
1381         wc_printf(_("Address:"));
1382         wc_printf("</td><td>"
1383                 "<input type=\"text\" name=\"extadr\" "
1384                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1385                 extadr);
1386         wc_printf("<tr><td> </td><td>"
1387                 "<input type=\"text\" name=\"street\" "
1388                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1389                 street);
1390         wc_printf("<tr><td>");
1391         wc_printf(_("City:"));
1392         wc_printf("</td><td>"
1393                 "<input type=\"text\" name=\"city\" "
1394                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1395                 city);
1396         wc_printf("<tr><td>");
1397         wc_printf(_("State:"));
1398         wc_printf("</td><td>"
1399                 "<input type=\"text\" name=\"state\" "
1400                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1401                 state);
1402         wc_printf("<tr><td>");
1403         wc_printf(_("ZIP code:"));
1404         wc_printf("</td><td>"
1405                 "<input type=\"text\" name=\"zipcode\" "
1406                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
1407                 zipcode);
1408         wc_printf("<tr><td>");
1409         wc_printf(_("Country:"));
1410         wc_printf("</td><td>"
1411                 "<input type=\"text\" name=\"country\" "
1412                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
1413                 country);
1414         wc_printf("</table>\n");
1415
1416         wc_printf("</table>\n");
1417
1418         wc_printf("<table border=0><tr><td>");
1419         wc_printf(_("Home telephone:"));
1420         wc_printf("</td>"
1421                 "<td><input type=\"text\" name=\"hometel\" "
1422                 "value=\"%s\" maxlength=\"29\"></td>\n",
1423                 hometel);
1424         wc_printf("<td>");
1425         wc_printf(_("Work telephone:"));
1426         wc_printf("</td>"
1427                 "<td><input type=\"text\" name=\"worktel\" "
1428                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1429                 worktel);
1430         wc_printf("<tr><td>");
1431         wc_printf(_("Mobile telephone:"));
1432         wc_printf("</td>"
1433                 "<td><input type=\"text\" name=\"mobiletel\" "
1434                 "value=\"%s\" maxlength=\"29\"></td>\n",
1435                 mobiletel);
1436         wc_printf("<td>");
1437         wc_printf(_("Fax number:"));
1438         wc_printf("</td>"
1439                 "<td><input type=\"text\" name=\"faxtel\" "
1440                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
1441                 faxtel);
1442
1443         wc_printf("<table class=\"vcard_edit_background_alt\">");
1444         wc_printf("<tr><td>");
1445
1446         wc_printf("<table border=0><TR>"
1447                 "<td valign=top>");
1448         wc_printf(_("Primary Internet e-mail address"));
1449         wc_printf("<br>"
1450                 "<input type=\"text\" name=\"primary_inetemail\" "
1451                 "size=40 maxlength=60 value=\"");
1452         escputs(primary_inetemail);
1453         wc_printf("\"><br>"
1454                 "</td><td valign=top>");
1455         wc_printf(_("Internet e-mail aliases"));
1456         wc_printf("<br>"
1457                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1458         escputs(other_inetemail);
1459         wc_printf("</textarea></td></tr></table>\n");
1460
1461         wc_printf("</td></tr></table>\n");
1462
1463         wc_printf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1464         escputs(extrafields);
1465         wc_printf("\">\n");
1466
1467         wc_printf("<input type=\"hidden\" name=\"return_to\" value=\"");
1468         escputs(return_to);
1469         wc_printf("\">\n");
1470
1471         wc_printf("<div class=\"buttons\">\n"
1472                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1473                 "&nbsp;"
1474                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1475                 "</div></form>\n",
1476                 _("Save changes"),
1477                 _("Cancel")
1478         );
1479         
1480         wc_printf("</td></tr></table>\n");
1481         do_template("box_end");
1482         wDumpContent(1);
1483         if (Msg != NULL) {
1484                 DestroyMessageSummary(Msg);
1485         }
1486 }
1487
1488
1489 /*
1490  *  commit the edits to the citadel server
1491  */
1492 void edit_vcard(void) {
1493         long msgnum;
1494         char *partnum;
1495
1496         msgnum = lbstr("msgnum");
1497         partnum = bstr("partnum");
1498         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1499 }
1500
1501
1502
1503 /*
1504  *  parse edited vcard from the browser
1505  */
1506 void submit_vcard(void) {
1507         struct vCard *v;
1508         char *serialized_vcard;
1509         char buf[SIZ];
1510         StrBuf *Buf;
1511         const StrBuf *ForceRoom;
1512         int i;
1513
1514         if (!havebstr("ok_button")) { 
1515                 readloop(readnew, eUseDefault);
1516                 return;
1517         }
1518
1519         if (havebstr("force_room")) {
1520                 ForceRoom = sbstr("force_room");
1521                 if (gotoroom(ForceRoom) != 200) {
1522                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1523                         AppendImportantMessage(HKEY(": "));
1524                         AppendImportantMessage(SKEY(ForceRoom));
1525                         AppendImportantMessage(HKEY("; "));
1526                         AppendImportantMessage(_("Aborting."), -1);
1527
1528                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1529                                 select_user_to_edit(NULL);
1530                         }
1531                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1532                                 do_welcome();
1533                         }
1534                         else if (!IsEmptyStr(bstr("return_to"))) {
1535                                 http_redirect(bstr("return_to"));
1536                         }
1537                         else {
1538                                 readloop(readnew, eUseDefault);
1539                         }
1540                         return;
1541                 }
1542         }
1543
1544         Buf = NewStrBuf();
1545         serv_write(HKEY("ENT0 1|||4\n"));
1546         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1547         {
1548                 edit_vcard();
1549                 return;
1550         }
1551
1552         /* Make a vCard structure out of the data supplied in the form */
1553         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1554                      bstr("extrafields")
1555         );
1556         v = VCardLoad(Buf);     /* Start with the extra fields */
1557         if (v == NULL) {
1558                 AppendImportantMessage(_("An error has occurred."), -1);
1559                 edit_vcard();
1560                 FreeStrBuf(&Buf);
1561                 return;
1562         }
1563
1564         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1565                 bstr("lastname"),
1566                 bstr("firstname"),
1567                 bstr("middlename"),
1568                 bstr("prefix"),
1569                 bstr("suffix") );
1570         vcard_add_prop(v, "n", buf);
1571         
1572         vcard_add_prop(v, "title", bstr("title"));
1573         vcard_add_prop(v, "fn", bstr("fullname"));
1574         vcard_add_prop(v, "org", bstr("org"));
1575
1576         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1577                 bstr("pobox"),
1578                 bstr("extadr"),
1579                 bstr("street"),
1580                 bstr("city"),
1581                 bstr("state"),
1582                 bstr("zipcode"),
1583                 bstr("country") );
1584         vcard_add_prop(v, "adr", buf);
1585
1586         vcard_add_prop(v, "tel;home", bstr("hometel"));
1587         vcard_add_prop(v, "tel;work", bstr("worktel"));
1588         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1589         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1590         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1591
1592         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1593                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1594                 if (!IsEmptyStr(buf)) {
1595                         vcard_add_prop(v, "email;internet", buf);
1596                 }
1597         }
1598
1599         serialized_vcard = vcard_serialize(v);
1600         vcard_free(v);
1601         if (serialized_vcard == NULL) {
1602                 AppendImportantMessage(_("An error has occurred."), -1);
1603                 edit_vcard();
1604                 FreeStrBuf(&Buf);
1605                 return;
1606         }
1607
1608         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1609         serv_write(HKEY("\n"));
1610         serv_printf("%s\r\n", serialized_vcard);
1611         serv_write(HKEY("000\n"));
1612         free(serialized_vcard);
1613
1614         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1615                 select_user_to_edit(NULL);
1616         }
1617         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1618                 do_welcome();
1619         }
1620         else if (!IsEmptyStr(bstr("return_to"))) {
1621                 http_redirect(bstr("return_to"));
1622         }
1623         else {
1624                 readloop(readnew, eUseDefault);
1625         }
1626         FreeStrBuf(&Buf);
1627 }
1628
1629
1630
1631 /*
1632  * Extract an embedded photo from a vCard for display on the client
1633  */
1634 void display_vcard_photo_img(void)
1635 {
1636         long msgnum = 0L;
1637         StrBuf *vcard;
1638         struct vCard *v;
1639         char *photosrc;
1640         const char *contentType;
1641         wcsession *WCC = WC;
1642
1643         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1644         
1645         vcard = load_mimepart(msgnum,"1");
1646         v = VCardLoad(vcard);
1647         
1648         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1649         FlushStrBuf(WCC->WBuf);
1650         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1651         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1652                 FlushStrBuf(WCC->WBuf);
1653                 
1654                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1655                 output_headers(0, 0, 0, 0, 0, 0);
1656                 hprintf("Content-Type: text/plain\r\n");
1657                 begin_burst();
1658                 wc_printf(_("Could Not decode vcard photo\n"));
1659                 end_burst();
1660                 return;
1661         }
1662         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1663         http_transmit_thing(contentType, 0);
1664         free(v);
1665         free(photosrc);
1666 }
1667
1668 typedef struct _vcardview_struct {
1669         long is_singlecard;
1670         addrbookent *addrbook;
1671         long num_ab;
1672
1673 } vcardview_struct;
1674
1675 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat, 
1676                                  void **ViewSpecific, 
1677                                  long oper, 
1678                                  char *cmd, 
1679                                  long len,
1680                                  char *filter,
1681                                  long flen)
1682 {
1683         vcardview_struct *VS;
1684
1685         VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1686         memset(VS, 0, sizeof(vcardview_struct));
1687         *ViewSpecific = (void*)VS;
1688
1689         VS->is_singlecard = ibstr("is_singlecard");
1690         if (VS->is_singlecard != 1) {
1691                 if (oper == do_search) {
1692                         snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1693                 }
1694                 else {
1695                         strcpy(cmd, "MSGS ALL");
1696                 }
1697                 Stat->maxmsgs = 9999999;
1698         }
1699         return 200;
1700 }
1701
1702 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat, 
1703                             void **ViewSpecific, 
1704                             message_summary* Msg, 
1705                             int is_new, 
1706                             int i)
1707 {
1708         vcardview_struct *VS;
1709         char *ab_name;
1710
1711         VS = (vcardview_struct*) *ViewSpecific;
1712
1713         ab_name = NULL;
1714         fetch_ab_name(Msg, &ab_name);
1715         if (ab_name == NULL) 
1716                 return 0;
1717         ++VS->num_ab;
1718         VS->addrbook = realloc(VS->addrbook,
1719                                (sizeof(addrbookent) * VS->num_ab) );
1720         safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1721                     sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1722         VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1723         free(ab_name);
1724         return 0;
1725 }
1726
1727
1728 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1729 {
1730         const StrBuf *Mime;
1731         vcardview_struct *VS;
1732
1733         VS = (vcardview_struct*) *ViewSpecific;
1734         if (VS->is_singlecard)
1735                 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1736         else
1737                 do_addrbook_view(VS->addrbook, VS->num_ab);     /* Render the address book */
1738         return 0;
1739 }
1740
1741 int vcard_Cleanup(void **ViewSpecific)
1742 {
1743         vcardview_struct *VS;
1744
1745         VS = (vcardview_struct*) *ViewSpecific;
1746         wDumpContent(1);
1747         if ((VS != NULL) && 
1748             (VS->addrbook != NULL))
1749                 free(VS->addrbook);
1750         if (VS != NULL) 
1751                 free(VS);
1752         return 0;
1753 }
1754
1755 void 
1756 ServerStartModule_VCARD
1757 (void)
1758 {
1759         ///VCToEnum = NewHash(0, NULL);
1760
1761 }
1762
1763 void 
1764 ServerShutdownModule_VCARD
1765 (void)
1766 {
1767         /// DeleteHash(&VCToEnum);
1768 }
1769
1770 void 
1771 InitModule_VCARD
1772 (void)
1773 {
1774         RegisterCTX(CTX_VCARD);
1775         RegisterReadLoopHandlerset(
1776                 VIEW_ADDRESSBOOK,
1777                 vcard_GetParamsGetServerCall,
1778                 NULL,
1779                 NULL,
1780                 NULL, 
1781                 vcard_LoadMsgFromServer,
1782                 vcard_RenderView_or_Tail,
1783                 vcard_Cleanup);
1784         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1785         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1786         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1787 /*
1788         Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1789         Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1790         Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1791         Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1792         Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1793         Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1794         Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1795         Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1796         Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1797         Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1798         Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1799         Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1800         Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1801         Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1802 */
1803 /*
1804         RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1805
1806         REGISTERTokenParamDefine(VC_n);
1807         REGISTERTokenParamDefine(VC_fn);
1808         REGISTERTokenParamDefine(VC_title);
1809         REGISTERTokenParamDefine(VC_org);
1810         REGISTERTokenParamDefine(VC_email);
1811         REGISTERTokenParamDefine(VC_tel);
1812         REGISTERTokenParamDefine(VC_work);
1813         REGISTERTokenParamDefine(VC_home);
1814         REGISTERTokenParamDefine(VC_cell);
1815         REGISTERTokenParamDefine(VC_adr);
1816         REGISTERTokenParamDefine(VC_photo);
1817         REGISTERTokenParamDefine(VC_version);
1818         REGISTERTokenParamDefine(VC_rev);
1819         REGISTERTokenParamDefine(VC_label);
1820 */
1821
1822         {
1823                 StrBuf *Prefix  = NewStrBufPlain(HKEY("VC:"));
1824                 DefineToToken   = NewHash(1, lFlathash);
1825                 VCTokenToDefine = NewHash(1, NULL);
1826                 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0);
1827                 FreeStrBuf(&Prefix);
1828         }
1829         RegisterCTX(CTX_VCARD);
1830         RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1831         RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1832 }
1833