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