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