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