Readloop remove special cases
[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/webcit_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         int is_qp = 0;
565         int is_b64 = 0;
566         StrBuf *thisname = NULL;
567         char firsttoken[SIZ];
568         void *V;
569
570         Swap = NewStrBuf ();
571         thisname = NewStrBuf();
572         for (i=0; i<(v->numprops); ++i) {
573                 is_qp = 0;
574                 is_b64 = 0;
575                 StrBufPlain(thisname, v->prop[i].name, -1);
576                 StrBufLowerCase(thisname);
577                 
578                 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
579                 
580                 for (j=0; j<num_tokens(ChrPtr(thisname), ';'); ++j) {
581                         extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
582                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
583                                 is_qp = 1;
584 /*                              remove_token(thisname, j, ';');*/
585                         }
586                         if (!strcasecmp(buf, "encoding=base64")) {
587                                 is_b64 = 1;
588 /*                              remove_token(thisname, j, ';');*/
589                         }
590                 }
591                 
592                 /* copy over the payload into a StrBuf */
593                 Val = NewStrBufPlain(v->prop[i].value, -1);
594                         
595                 /* if we have some untagged QP, detect it here. */
596                 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
597                         StrBuf *b;
598                         StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
599                         b = Val;
600                         Val = Swap; 
601                         Swap = b;
602                         FlushStrBuf(Swap);
603                 }
604                 else if (is_b64) {
605                         StrBufDecodeBase64(Val);
606
607                 }
608                 syslog(1, "%s [%s][%s]",
609                         firsttoken,
610                         ChrPtr(Val),
611                         v->prop[i].value);
612                 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
613                 {
614                         eVC evc = (eVC) V;
615                         Put(VC, IKEY(evc), Val, HFreeStrBuf);
616                         syslog(1, "[%ul]\n", evc);
617                         Val = NULL;
618                 }
619                 else
620                         syslog(1, "[]\n");
621 /*
622 TODO: check for layer II
623                 else 
624                 {
625                         long max = num_tokens(thisname, ';');
626                         firsttoken[len] = '_';
627
628                         for (j = 0; j < max; j++) {
629 //                      firsttoken[len]
630
631                                 extract_token(buf, thisname, j, ';', sizeof (buf));
632                                         if (!strcasecmp(buf, "tel"))
633                                                 strcat(phone, "");
634                                         else if (!strcasecmp(buf, "work"))
635                                                 strcat(phone, _(" (work)"));
636                                         else if (!strcasecmp(buf, "home"))
637                                                 strcat(phone, _(" (home)"));
638                                         else if (!strcasecmp(buf, "cell"))
639                                                 strcat(phone, _(" (cell)"));
640                                         else {
641                                                 strcat(phone, " (");
642                                                 strcat(phone, buf);
643                                                 strcat(phone, ")");
644                                         }
645                                 }
646                         }
647
648                 }
649 */
650         
651                 FreeStrBuf(&Val);
652                 free(thisname);
653                 thisname = NULL;
654         }
655
656 }
657
658 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
659 {
660         HashList *VC = CTX;
661         eVC evc;
662         void *vStr;
663
664         evc = GetTemplateTokenNumber(Target, TP, 0, -1);
665         if (evc != -1)
666         {
667                 if (GetHash(VC, IKEY(evc), &vStr))
668                 {
669                         StrBufAppendTemplate(Target, TP,
670                                              (StrBuf*) vStr,
671                                              1);
672                 }
673         }
674         
675 }
676
677 void new_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
678 {
679         HashList *VC;
680         WCTemplputParams SubTP;
681
682         memset(&SubTP, 0, sizeof(WCTemplputParams));    
683
684
685         VC = NewHash(0, Flathash);
686         parse_vcard(Target, v, VC, full, Mime);
687
688         SubTP.Filter.ContextType = CTX_VCARD;
689         SubTP.Context = VC;
690
691         DoTemplate(HKEY("test_vcard"), Target, &SubTP);
692         DeleteHash(&VC);
693 }
694
695
696
697 /*
698  * Display a textual vCard
699  * (Converts to a vCard object and then calls the actual display function)
700  * Set 'full' to nonzero to display the whole card instead of a one-liner.
701  * Or, if "storename" is non-NULL, just store the person's name in that
702  * buffer instead of displaying the card at all.
703  *
704  * vcard_source the buffer containing the vcard text
705  * alpha        Display only if name begins with this letter of the alphabet
706  * full         Display the full vCard (otherwise just the display name)
707  * storename    If not NULL, also store the display name here
708  * msgnum       Citadel message pointer
709  */
710 void display_vcard(StrBuf *Target, 
711                    wc_mime_attachment *Mime, 
712                    char alpha, 
713                    int full, 
714                    char **storename, 
715                    long msgnum) 
716 {
717         struct vCard *v;
718         char *name;
719         StrBuf *Buf;
720         StrBuf *Buf2;
721         char this_alpha = 0;
722
723         v = VCardLoad(Mime->Data);
724
725         if (v == NULL) return;
726
727         name = vcard_get_prop(v, "n", 1, 0, 0);
728         if (name != NULL) {
729                 Buf = NewStrBufPlain(name, -1);
730                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
731                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
732                 this_alpha = ChrPtr(Buf)[0];
733                 FreeStrBuf(&Buf);
734                 FreeStrBuf(&Buf2);
735         }
736
737         if (storename != NULL) {
738                 fetchname_parsed_vcard(v, storename);
739         }
740         else if ((alpha == 0) || 
741                  ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) || 
742                  ((!isalpha(alpha)) && (!isalpha(this_alpha)))
743                 ) 
744         {
745 #ifdef XXX_XXX
746                 new_vcard (Target, v, full, Mime);
747 #else
748                 display_parsed_vcard(Target, v, full, Mime);
749 #endif          
750         }
751
752         vcard_free(v);
753 }
754
755
756
757 /*
758  * Render the address book using info we gathered during the scan
759  *
760  * addrbook     the addressbook to render
761  * num_ab       the number of the addressbook
762  */
763 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
764         int i = 0;
765         int displayed = 0;
766         int bg = 0;
767         static int NAMESPERPAGE = 60;
768         int num_pages = 0;
769         int tabfirst = 0;
770         char tabfirst_label[64];
771         int tablast = 0;
772         char tablast_label[64];
773         char this_tablabel[64];
774         int page = 0;
775         char **tablabels;
776
777         if (num_ab == 0) {
778                 wc_printf("<br><br><br><div align=\"center\"><i>");
779                 wc_printf(_("This address book is empty."));
780                 wc_printf("</i></div>\n");
781                 return;
782         }
783
784         if (num_ab > 1) {
785                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
786         }
787
788         num_pages = (num_ab / NAMESPERPAGE) + 1;
789
790         tablabels = malloc(num_pages * sizeof (char *));
791         if (tablabels == NULL) {
792                 wc_printf("<br><br><br><div align=\"center\"><i>");
793                 wc_printf(_("An internal error has occurred."));
794                 wc_printf("</i></div>\n");
795                 return;
796         }
797
798         for (i=0; i<num_pages; ++i) {
799                 tabfirst = i * NAMESPERPAGE;
800                 tablast = tabfirst + NAMESPERPAGE - 1;
801                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
802                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
803                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
804                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
805                 tablabels[i] = strdup(this_tablabel);
806         }
807
808         tabbed_dialog(num_pages, tablabels);
809         page = (-1);
810
811         for (i=0; i<num_ab; ++i) {
812
813                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
814                         page = (i / NAMESPERPAGE);
815                         if (page > 0) {
816                                 wc_printf("</tr></table>\n");
817                                 end_tab(page-1, num_pages);
818                         }
819                         begin_tab(page, num_pages);
820                         wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
821                         displayed = 0;
822                 }
823
824                 if ((displayed % 4) == 0) {
825                         if (displayed > 0) {
826                                 wc_printf("</tr>\n");
827                         }
828                         bg = 1 - bg;
829                         wc_printf("<tr bgcolor=\"#%s\">",
830                                 (bg ? "dddddd" : "ffffff")
831                         );
832                 }
833         
834                 wc_printf("<td>");
835
836                 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
837                         addrbook[i].ab_msgnum);
838                 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
839                 vcard_n_prettyize(addrbook[i].ab_name);
840                 escputs(addrbook[i].ab_name);
841                 wc_printf("</a></td>\n");
842                 ++displayed;
843         }
844
845         /* Placeholders for empty columns at end */
846         if ((num_ab % 4) != 0) {
847                 for (i=0; i<(4-(num_ab % 4)); ++i) {
848                         wc_printf("<td>&nbsp;</td>");
849                 }
850         }
851
852         wc_printf("</tr></table>\n");
853         end_tab((num_pages-1), num_pages);
854
855         begin_tab(num_pages, num_pages);
856         /* FIXME there ought to be something here */
857         end_tab(num_pages, num_pages);
858
859         for (i=0; i<num_pages; ++i) {
860                 free(tablabels[i]);
861         }
862         free(tablabels);
863 }
864
865
866
867
868 /*
869  * Edit the vCard component of a MIME message.  
870  * Supply the message number
871  * and MIME part number to fetch.  Or, specify -1 for the message number
872  * to start with a blank card.
873  */
874 void do_edit_vcard(long msgnum, char *partnum, 
875                    message_summary *VCMsg,
876                    wc_mime_attachment *VCAtt,
877                    const char *return_to, 
878                    const char *force_room) {
879         message_summary *Msg = NULL;
880         wc_mime_attachment *VCMime = NULL;
881         struct vCard *v;
882         int i;
883         char *key, *value;
884         char whatuser[256];
885
886         char lastname[256];
887         char firstname[256];
888         char middlename[256];
889         char prefix[256];
890         char suffix[256];
891         char pobox[256];
892         char extadr[256];
893         char street[256];
894         char city[256];
895         char state[256];
896         char zipcode[256];
897         char country[256];
898         char hometel[256];
899         char worktel[256];
900         char faxtel[256];
901         char mobiletel[256];
902         char primary_inetemail[256];
903         char other_inetemail[SIZ];
904         char extrafields[SIZ];
905         char fullname[256];
906         char title[256];
907         char org[256];
908
909         lastname[0] = 0;
910         firstname[0] = 0;
911         middlename[0] = 0;
912         prefix[0] = 0;
913         suffix[0] = 0;
914         pobox[0] = 0;
915         extadr[0] = 0;
916         street[0] = 0;
917         city[0] = 0;
918         state[0] = 0;
919         zipcode[0] = 0;
920         country[0] = 0;
921         hometel[0] = 0;
922         worktel[0] = 0;
923         faxtel[0] = 0;
924         mobiletel[0] = 0;
925         primary_inetemail[0] = 0;
926         other_inetemail[0] = 0;
927         title[0] = 0;
928         org[0] = 0;
929         extrafields[0] = 0;
930         fullname[0] = 0;
931
932         safestrncpy(whatuser, "", sizeof whatuser);
933
934         if ((msgnum >= 0) || 
935             ((VCMsg != NULL) && (VCAtt != NULL)))
936         {
937                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
938
939                         Msg = (message_summary *) malloc(sizeof(message_summary));
940                         memset(Msg, 0, sizeof(message_summary));
941                         Msg->msgnum = msgnum;
942                         VCMime = load_vcard(Msg);
943                         if (VCMime == NULL) {
944                                 convenience_page("770000", _("Error"), "");///TODO: important message
945                                 DestroyMessageSummary(Msg);
946                                 return;
947                         }
948                 
949                         v = VCardLoad(VCMime->Data);
950                 }
951                 else {
952                         v = VCardLoad(VCAtt->Data);
953                 }
954         
955                 /* Populate the variables for our form */
956                 i = 0;
957                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
958                         char prp[256];  /* property name */
959                         char prm[256];  /* parameters */
960
961                         value = vcard_get_prop(v, "", 0, i++, 0);
962
963
964                         extract_token(prp, key, 0, ';', sizeof prp);
965                         safestrncpy(prm, key, sizeof prm);
966                         remove_token(prm, 0, ';');
967
968                         if (!strcasecmp(prp, "n")) {
969                                 extract_token(lastname, value, 0, ';', sizeof lastname);
970                                 extract_token(firstname, value, 1, ';', sizeof firstname);
971                                 extract_token(middlename, value, 2, ';', sizeof middlename);
972                                 extract_token(prefix, value, 3, ';', sizeof prefix);
973                                 extract_token(suffix, value, 4, ';', sizeof suffix);
974                         }
975
976                         else if (!strcasecmp(prp, "fn")) {
977                                 safestrncpy(fullname, value, sizeof fullname);
978                         }
979
980                         else if (!strcasecmp(prp, "title")) {
981                                 safestrncpy(title, value, sizeof title);
982                         }
983         
984                         else if (!strcasecmp(prp, "org")) {
985                                 safestrncpy(org, value, sizeof org);
986                         }
987         
988                         else if (!strcasecmp(prp, "adr")) {
989                                 extract_token(pobox, value, 0, ';', sizeof pobox);
990                                 extract_token(extadr, value, 1, ';', sizeof extadr);
991                                 extract_token(street, value, 2, ';', sizeof street);
992                                 extract_token(city, value, 3, ';', sizeof city);
993                                 extract_token(state, value, 4, ';', sizeof state);
994                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
995                                 extract_token(country, value, 6, ';', sizeof country);
996                         }
997
998                         else if (!strcasecmp(prp, "tel")) {
999
1000                                 if (bmstrcasestr(prm, "home")) {
1001                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1002                                 }
1003                                 else if (bmstrcasestr(prm, "work")) {
1004                                         extract_token(worktel, value, 0, ';', sizeof worktel);
1005                                 }
1006                                 else if (bmstrcasestr(prm, "fax")) {
1007                                         extract_token(faxtel, value, 0, ';', sizeof faxtel);
1008                                 }
1009                                 else if (bmstrcasestr(prm, "cell")) {
1010                                         extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
1011                                 }
1012                                 else {  /* Missing or unknown type; put it in the home phone */
1013                                         extract_token(hometel, value, 0, ';', sizeof hometel);
1014                                 }
1015                         }
1016         
1017                         else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
1018                                 if (primary_inetemail[0] == 0) {
1019                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
1020                                 }
1021                                 else {
1022                                         if (other_inetemail[0] != 0) {
1023                                                 strcat(other_inetemail, "\n");
1024                                         }
1025                                         strcat(other_inetemail, value);
1026                                 }
1027                         }
1028
1029                         /* Unrecognized properties are preserved here so we don't discard them
1030                          * just because the vCard was edited with WebCit.
1031                          */
1032                         else {
1033                                 strcat(extrafields, key);
1034                                 strcat(extrafields, ":");
1035                                 strcat(extrafields, value);
1036                                 strcat(extrafields, "\n");
1037                         }
1038         
1039                 }
1040         
1041                 vcard_free(v);
1042         }
1043
1044         /* Display the form */
1045         output_headers(1, 1, 1, 0, 0, 0);
1046
1047         do_template("box_begin_1");
1048         StrBufAppendBufPlain(WC->WBuf, _("Edit contact information"), -1, 0);
1049         do_template("box_begin_2");
1050
1051         wc_printf("<form method=\"POST\" action=\"submit_vcard\">\n");
1052         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
1053
1054         if (force_room != NULL) {
1055                 wc_printf("<input type=\"hidden\" name=\"force_room\" value=\"");
1056                 escputs(force_room);
1057                 wc_printf("\">\n");
1058         }
1059
1060         wc_printf("<table class=\"vcard_edit_background\"><tr><td>\n");
1061
1062         wc_printf("<table border=\"0\"><tr>"
1063                 "<td>%s</td>"
1064                 "<td>%s</td>"
1065                 "<td>%s</td>"
1066                 "<td>%s</td>"
1067                 "<td>%s</td></tr>\n",
1068                 _("Prefix"), _("First Name"), _("Middle Name"), _("Last Name"), _("Suffix")
1069         );
1070         wc_printf("<tr><td><input type=\"text\" name=\"prefix\" "
1071                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
1072                 prefix);
1073         wc_printf("<td><input type=\"text\" name=\"firstname\" "
1074                 "value=\"%s\" maxlength=\"29\"></td>",
1075                 firstname);
1076         wc_printf("<td><input type=\"text\" name=\"middlename\" "
1077                 "value=\"%s\" maxlength=\"29\"></td>",
1078                 middlename);
1079         wc_printf("<td><input type=\"text\" name=\"lastname\" "
1080                 "value=\"%s\" maxlength=\"29\"></td>",
1081                 lastname);
1082         wc_printf("<td><input type=\"text\" name=\"suffix\" "
1083                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
1084                 suffix);
1085
1086         wc_printf("<table  class=\"vcard_edit_background_alt\">");
1087         wc_printf("<tr><td>");
1088
1089         wc_printf(_("Display name:"));
1090         wc_printf("<br>"
1091                 "<input type=\"text\" name=\"fullname\" "
1092                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1093                 fullname
1094         );
1095
1096         wc_printf(_("Title:"));
1097         wc_printf("<br>"
1098                 "<input type=\"text\" name=\"title\" "
1099                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1100                 title
1101         );
1102
1103         wc_printf(_("Organization:"));
1104         wc_printf("<br>"
1105                 "<input type=\"text\" name=\"org\" "
1106                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1107                 org
1108         );
1109
1110         wc_printf("</td><td>");
1111
1112         wc_printf("<table border=\"0\">");
1113         wc_printf("<tr><td>");
1114         wc_printf(_("PO box:"));
1115         wc_printf("</td><td>"
1116                 "<input type=\"text\" name=\"pobox\" "
1117                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1118                 pobox);
1119         wc_printf("<tr><td>");
1120         wc_printf(_("Address:"));
1121         wc_printf("</td><td>"
1122                 "<input type=\"text\" name=\"extadr\" "
1123                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1124                 extadr);
1125         wc_printf("<tr><td> </td><td>"
1126                 "<input type=\"text\" name=\"street\" "
1127                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1128                 street);
1129         wc_printf("<tr><td>");
1130         wc_printf(_("City:"));
1131         wc_printf("</td><td>"
1132                 "<input type=\"text\" name=\"city\" "
1133                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1134                 city);
1135         wc_printf("<tr><td>");
1136         wc_printf(_("State:"));
1137         wc_printf("</td><td>"
1138                 "<input type=\"text\" name=\"state\" "
1139                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1140                 state);
1141         wc_printf("<tr><td>");
1142         wc_printf(_("ZIP code:"));
1143         wc_printf("</td><td>"
1144                 "<input type=\"text\" name=\"zipcode\" "
1145                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
1146                 zipcode);
1147         wc_printf("<tr><td>");
1148         wc_printf(_("Country:"));
1149         wc_printf("</td><td>"
1150                 "<input type=\"text\" name=\"country\" "
1151                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
1152                 country);
1153         wc_printf("</table>\n");
1154
1155         wc_printf("</table>\n");
1156
1157         wc_printf("<table border=0><tr><td>");
1158         wc_printf(_("Home telephone:"));
1159         wc_printf("</td>"
1160                 "<td><input type=\"text\" name=\"hometel\" "
1161                 "value=\"%s\" maxlength=\"29\"></td>\n",
1162                 hometel);
1163         wc_printf("<td>");
1164         wc_printf(_("Work telephone:"));
1165         wc_printf("</td>"
1166                 "<td><input type=\"text\" name=\"worktel\" "
1167                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1168                 worktel);
1169         wc_printf("<tr><td>");
1170         wc_printf(_("Mobile telephone:"));
1171         wc_printf("</td>"
1172                 "<td><input type=\"text\" name=\"mobiletel\" "
1173                 "value=\"%s\" maxlength=\"29\"></td>\n",
1174                 mobiletel);
1175         wc_printf("<td>");
1176         wc_printf(_("Fax number:"));
1177         wc_printf("</td>"
1178                 "<td><input type=\"text\" name=\"faxtel\" "
1179                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
1180                 faxtel);
1181
1182         wc_printf("<table class=\"vcard_edit_background_alt\">");
1183         wc_printf("<tr><td>");
1184
1185         wc_printf("<table border=0><TR>"
1186                 "<td valign=top>");
1187         wc_printf(_("Primary Internet e-mail address"));
1188         wc_printf("<br>"
1189                 "<input type=\"text\" name=\"primary_inetemail\" "
1190                 "size=40 maxlength=60 value=\"");
1191         escputs(primary_inetemail);
1192         wc_printf("\"><br>"
1193                 "</td><td valign=top>");
1194         wc_printf(_("Internet e-mail aliases"));
1195         wc_printf("<br>"
1196                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1197         escputs(other_inetemail);
1198         wc_printf("</textarea></td></tr></table>\n");
1199
1200         wc_printf("</td></tr></table>\n");
1201
1202         wc_printf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1203         escputs(extrafields);
1204         wc_printf("\">\n");
1205
1206         wc_printf("<input type=\"hidden\" name=\"return_to\" value=\"");
1207         escputs(return_to);
1208         wc_printf("\">\n");
1209
1210         wc_printf("<div class=\"buttons\">\n"
1211                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1212                 "&nbsp;"
1213                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1214                 "</div></form>\n",
1215                 _("Save changes"),
1216                 _("Cancel")
1217         );
1218         
1219         wc_printf("</td></tr></table>\n");
1220         do_template("box_end");
1221         wDumpContent(1);
1222         if (Msg != NULL) {
1223                 DestroyMessageSummary(Msg);
1224         }
1225 }
1226
1227
1228 /*
1229  *  commit the edits to the citadel server
1230  */
1231 void edit_vcard(void) {
1232         long msgnum;
1233         char *partnum;
1234
1235         msgnum = lbstr("msgnum");
1236         partnum = bstr("partnum");
1237         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1238 }
1239
1240
1241
1242 /*
1243  *  parse edited vcard from the browser
1244  */
1245 void submit_vcard(void) {
1246         struct vCard *v;
1247         char *serialized_vcard;
1248         char buf[SIZ];
1249         StrBuf *Buf;
1250         const StrBuf *ForceRoom;
1251         int i;
1252
1253         if (!havebstr("ok_button")) { 
1254                 readloop(readnew, eUseDefault);
1255                 return;
1256         }
1257
1258         if (havebstr("force_room")) {
1259                 ForceRoom = sbstr("force_room");
1260                 if (gotoroom(ForceRoom) != 200) {
1261                         AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1262                         AppendImportantMessage(HKEY(": "));
1263                         AppendImportantMessage(SKEY(ForceRoom));
1264                         AppendImportantMessage(HKEY("; "));
1265                         AppendImportantMessage(_("Aborting."), -1);
1266
1267                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1268                                 select_user_to_edit(NULL);
1269                         }
1270                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1271                                 do_welcome();
1272                         }
1273                         else if (!IsEmptyStr(bstr("return_to"))) {
1274                                 http_redirect(bstr("return_to"));
1275                         }
1276                         else {
1277                                 readloop(readnew, eUseDefault);
1278                         }
1279                         return;
1280                 }
1281         }
1282
1283         Buf = NewStrBuf();
1284         serv_write(HKEY("ENT0 1|||4||||||1\n"));
1285         if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1286         {
1287                 edit_vcard();
1288                 return;
1289         }
1290
1291         /* Make a vCard structure out of the data supplied in the form */
1292         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1293                      bstr("extrafields")
1294         );
1295         v = VCardLoad(Buf);     /* Start with the extra fields */
1296         if (v == NULL) {
1297                 AppendImportantMessage(_("An error has occurred."), -1);
1298                 edit_vcard();
1299                 FreeStrBuf(&Buf);
1300                 return;
1301         }
1302
1303         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1304                 bstr("lastname"),
1305                 bstr("firstname"),
1306                 bstr("middlename"),
1307                 bstr("prefix"),
1308                 bstr("suffix") );
1309         vcard_add_prop(v, "n", buf);
1310         
1311         vcard_add_prop(v, "title", bstr("title"));
1312         vcard_add_prop(v, "fn", bstr("fullname"));
1313         vcard_add_prop(v, "org", bstr("org"));
1314
1315         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1316                 bstr("pobox"),
1317                 bstr("extadr"),
1318                 bstr("street"),
1319                 bstr("city"),
1320                 bstr("state"),
1321                 bstr("zipcode"),
1322                 bstr("country") );
1323         vcard_add_prop(v, "adr", buf);
1324
1325         vcard_add_prop(v, "tel;home", bstr("hometel"));
1326         vcard_add_prop(v, "tel;work", bstr("worktel"));
1327         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1328         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1329         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1330
1331         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1332                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1333                 if (!IsEmptyStr(buf)) {
1334                         vcard_add_prop(v, "email;internet", buf);
1335                 }
1336         }
1337
1338         serialized_vcard = vcard_serialize(v);
1339         vcard_free(v);
1340         if (serialized_vcard == NULL) {
1341                 AppendImportantMessage(_("An error has occurred."), -1);
1342                 edit_vcard();
1343                 FreeStrBuf(&Buf);
1344                 return;
1345         }
1346
1347         serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1348         serv_write(HKEY("\n"));
1349         serv_printf("%s\r\n", serialized_vcard);
1350         serv_write(HKEY("000\n"));
1351         free(serialized_vcard);
1352
1353         StrBuf_ServGetln(Buf);
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         RegisterReadLoopHandlerset(
1516                 VIEW_ADDRESSBOOK,
1517                 vcard_GetParamsGetServerCall,
1518                 NULL,
1519                 NULL,
1520                 NULL, 
1521                 vcard_LoadMsgFromServer,
1522                 vcard_RenderView_or_Tail,
1523                 vcard_Cleanup);
1524         WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1525         WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1526         WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1527
1528         Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1529         Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1530         Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1531         Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1532         Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1533         Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1534         Put(VCToEnum, HKEY("tel_tel"), (void*)VC_tel_tel, reference_free_handler);
1535         Put(VCToEnum, HKEY("tel_work"), (void*)VC_tel_work, reference_free_handler);
1536         Put(VCToEnum, HKEY("tel_home"), (void*)VC_tel_home, reference_free_handler);
1537         Put(VCToEnum, HKEY("tel_cell"), (void*)VC_tel_cell, reference_free_handler);
1538         Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1539         Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1540         Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1541         Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1542         Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1543
1544
1545         RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1546
1547         REGISTERTokenParamDefine(VC_n);
1548         REGISTERTokenParamDefine(VC_fn);
1549         REGISTERTokenParamDefine(VC_title);
1550         REGISTERTokenParamDefine(VC_org);
1551         REGISTERTokenParamDefine(VC_email);
1552         REGISTERTokenParamDefine(VC_tel);
1553         REGISTERTokenParamDefine(VC_tel_tel);
1554         REGISTERTokenParamDefine(VC_tel_work);
1555         REGISTERTokenParamDefine(VC_tel_home);
1556         REGISTERTokenParamDefine(VC_tel_cell);
1557         REGISTERTokenParamDefine(VC_adr);
1558         REGISTERTokenParamDefine(VC_photo);
1559         REGISTERTokenParamDefine(VC_version);
1560         REGISTERTokenParamDefine(VC_rev);
1561         REGISTERTokenParamDefine(VC_label);
1562
1563 }
1564