* do_edit_vcard() contained a 'StrBuf *Buf' which was unused in the entire function...
[citadel.git] / webcit / vcard_edit.c
1 /*
2  * $Id$
3  */
4
5 #include "webcit.h"
6
7
8 /*
9  * Record compare function for sorting address book indices
10  */
11 int abcmp(const void *ab1, const void *ab2) {
12         return(strcasecmp(
13                 (((const addrbookent *)ab1)->ab_name),
14                 (((const addrbookent *)ab2)->ab_name)
15         ));
16 }
17
18
19 /*
20  * Helper function for do_addrbook_view()
21  * Converts a name into a three-letter tab label
22  */
23 void nametab(char *tabbuf, long len, char *name) {
24         stresc(tabbuf, len, name, 0, 0);
25         tabbuf[0] = toupper(tabbuf[0]);
26         tabbuf[1] = tolower(tabbuf[1]);
27         tabbuf[2] = tolower(tabbuf[2]);
28         tabbuf[3] = 0;
29 }
30
31
32 /*
33  * display the adressbook overview
34  */
35 void display_addressbook(long msgnum, char alpha) {
36         //char buf[SIZ];
37         /* char mime_partnum[SIZ]; */
38         ///char mime_disposition[SIZ];
39         //int mime_length;
40         char vcard_partnum[SIZ];
41         StrBuf *vcard_source = NULL;
42         message_summary summ;////TODO: this will leak
43
44         memset(&summ, 0, sizeof(summ));
45         // safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
46         // Load Message headers
47         if (!IsEmptyStr(vcard_partnum)) {
48                 vcard_source = load_mimepart(msgnum, vcard_partnum);
49                 if (vcard_source != NULL) {
50
51                         /* Display the summary line */
52                         display_vcard(WC->WBuf, vcard_source, alpha, 0, NULL, msgnum);
53
54                         /* If it's my vCard I can edit it */
55                         if (    (!strcasecmp(ChrPtr(WC->wc_roomname), USERCONFIGROOM))
56                                 || (!strcasecmp(&(ChrPtr(WC->wc_roomname)[11]), USERCONFIGROOM))
57                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
58                         ) {
59                                 wprintf("<a href=\"edit_vcard?"
60                                         "msgnum=%ld&partnum=%s\">",
61                                         msgnum, vcard_partnum);
62                                 wprintf("[%s]</a>", _("edit"));
63                         }
64
65                         FreeStrBuf(&vcard_source);
66                 }
67         }
68
69 }
70
71
72
73 /*
74  * If it's an old "Firstname Lastname" style record, try to convert it.
75  */
76 void lastfirst_firstlast(char *namebuf) {
77         char firstname[SIZ];
78         char lastname[SIZ];
79         int i;
80
81         if (namebuf == NULL) return;
82         if (strchr(namebuf, ';') != NULL) return;
83
84         i = num_tokens(namebuf, ' ');
85         if (i < 2) return;
86
87         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
88         remove_token(namebuf, i-1, ' ');
89         strcpy(firstname, namebuf);
90         sprintf(namebuf, "%s; %s", lastname, firstname);
91 }
92
93
94
95 wc_mime_attachment *load_vcard(message_summary *Msg) 
96 {
97         HashPos  *it;
98         StrBuf *FoundCharset = NewStrBuf();
99         StrBuf *Error;
100         void *vMime;
101         const char *Key;
102         long len;
103         wc_mime_attachment *Mime;
104         wc_mime_attachment *VCMime = NULL;
105
106         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
107         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
108         Msg->MsgBody->msgnum = Msg->msgnum;
109
110         load_message(Msg, FoundCharset, &Error);
111
112         FreeStrBuf(&FoundCharset);
113         /* look up the vcard... */
114         it = GetNewHashPos(Msg->AllAttach, 0);
115         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
116                (vMime != NULL)) 
117         {
118                 Mime = (wc_mime_attachment*) vMime;
119                 if ((strcmp(ChrPtr(Mime->ContentType),
120                            "text/x-vcard") == 0) ||
121                     (strcmp(ChrPtr(Mime->ContentType),
122                             "text/vcard") == 0))
123                 {
124                         VCMime = Mime;
125                         break;
126                 }
127         }
128         DeleteHashPos(&it);
129         if (VCMime == NULL)
130                 return NULL;
131
132         MimeLoadData(VCMime);
133         return VCMime;
134 }
135
136 /*
137  * fetch the display name off a vCard
138  */
139 void fetch_ab_name(message_summary *Msg, char **namebuf) {
140         long len;
141         int i;
142         wc_mime_attachment *VCMime = NULL;
143
144         if (namebuf == NULL) return;
145
146         VCMime = load_vcard(Msg);
147         if (VCMime == NULL)
148                 return;
149
150         /* Grab the name off the card */
151         display_vcard(WC->WBuf, VCMime->Data, 0, 0, namebuf, Msg->msgnum);
152
153         if (*namebuf != NULL) {
154                 lastfirst_firstlast(*namebuf);
155                 striplt(*namebuf);
156                 len = strlen(*namebuf);
157                 for (i=0; i<len; ++i) {
158                         if ((*namebuf)[i] != ';') return;
159                 }
160                 free (*namebuf);
161                 (*namebuf) = strdup(_("(no name)"));
162         }
163         else {
164                 (*namebuf) = strdup(_("(no name)"));
165         }
166 }
167
168
169
170 /*
171  * Turn a vCard "n" (name) field into something displayable.
172  */
173 void vcard_n_prettyize(char *name)
174 {
175         char *original_name;
176         int i, j, len;
177
178         original_name = strdup(name);
179         len = strlen(original_name);
180         for (i=0; i<5; ++i) {
181                 if (len > 0) {
182                         if (original_name[len-1] == ' ') {
183                                 original_name[--len] = 0;
184                         }
185                         if (original_name[len-1] == ';') {
186                                 original_name[--len] = 0;
187                         }
188                 }
189         }
190         strcpy(name, "");
191         j=0;
192         for (i=0; i<len; ++i) {
193                 if (original_name[i] == ';') {
194                         name[j++] = ',';
195                         name[j++] = ' ';                        
196                 }
197                 else {
198                         name[j++] = original_name[i];
199                 }
200         }
201         name[j] = '\0';
202         free(original_name);
203 }
204
205
206
207
208 /*
209  * preparse a vcard name
210  * display_vcard() calls this after parsing the textual vCard into
211  * our 'struct vCard' data object.
212  * This gets called instead of display_parsed_vcard() if we are only looking
213  * to extract the person's name instead of displaying the card.
214  */
215 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
216         char *name;
217         char *prop;
218         char buf[SIZ];
219         int j, n, len;
220         int is_qp = 0;
221         int is_b64 = 0;
222
223         *storename = NULL;
224
225         name = vcard_get_prop(v, "n", 1, 0, 0);
226         if (name != NULL) {
227                 len = strlen(name);
228                 prop = vcard_get_prop(v, "n", 1, 0, 1);
229                 n = num_tokens(prop, ';');
230
231                 for (j=0; j<n; ++j) {
232                         extract_token(buf, prop, j, ';', sizeof buf);
233                         if (!strcasecmp(buf, "encoding=quoted-printable")) {
234                                 is_qp = 1;
235                         }
236                         if (!strcasecmp(buf, "encoding=base64")) {
237                                 is_b64 = 1;
238                         }
239                 }
240                 if (is_qp) {
241                         // %ff can become 6 bytes in utf8 
242                         *storename = malloc(len * 2 + 3); 
243                         j = CtdlDecodeQuotedPrintable(
244                                 *storename, name,
245                                 len);
246                         (*storename)[j] = 0;
247                 }
248                 else if (is_b64) {
249                         // ff will become one byte..
250                         *storename = malloc(len + 50);
251                         CtdlDecodeBase64(
252                                 *storename, name,
253                                 len);
254                 }
255                 else {
256                         *storename = strdup(name);
257                 }
258                 /* vcard_n_prettyize(storename); */
259         }
260
261 }
262
263
264
265 /*
266  * html print a vcard
267  * display_vcard() calls this after parsing the textual vCard into
268  * our 'struct vCard' data object.
269  *
270  * Set 'full' to nonzero to display the full card, otherwise it will only
271  * show a summary line.
272  *
273  * This code is a bit ugly, so perhaps an explanation is due: we do this
274  * in two passes through the vCard fields.  On the first pass, we process
275  * fields we understand, and then render them in a pretty fashion at the
276  * end.  Then we make a second pass, outputting all the fields we don't
277  * understand in a simple two-column name/value format.
278  * v            the vCard to display
279  * full         display all items of the vcard?
280  * msgnum       Citadel message pointer
281  */
282 void display_parsed_vcard(StrBuf *Target, struct vCard *v, int full, long msgnum) {
283         int i, j;
284         char buf[SIZ];
285         char *name;
286         int is_qp = 0;
287         int is_b64 = 0;
288         char *thisname, *thisvalue;
289         char firsttoken[SIZ];
290         int pass;
291
292         char fullname[SIZ];
293         char title[SIZ];
294         char org[SIZ];
295         char phone[SIZ];
296         char mailto[SIZ];
297
298         strcpy(fullname, "");
299         strcpy(phone, "");
300         strcpy(mailto, "");
301         strcpy(title, "");
302         strcpy(org, "");
303
304         if (!full) {
305                 StrBufAppendPrintf(Target, "<TD>");
306                 name = vcard_get_prop(v, "fn", 1, 0, 0);
307                 if (name != NULL) {
308                         StrEscAppend(Target, NULL, name, 0, 0);
309                 }
310                 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
311                         strcpy(fullname, name);
312                         vcard_n_prettyize(fullname);
313                         StrEscAppend(Target, NULL, fullname, 0, 0);
314                 }
315                 else {
316                         StrBufAppendPrintf(Target, "&nbsp;");
317                 }
318                 StrBufAppendPrintf(Target, "</TD>");
319                 return;
320         }
321
322         StrBufAppendPrintf(Target, "<div align=center>"
323                 "<table bgcolor=#aaaaaa width=50%%>");
324         for (pass=1; pass<=2; ++pass) {
325
326                 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
327                         int len;
328                         thisname = strdup(v->prop[i].name);
329                         extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
330         
331                         for (j=0; j<num_tokens(thisname, ';'); ++j) {
332                                 extract_token(buf, thisname, j, ';', sizeof buf);
333                                 if (!strcasecmp(buf, "encoding=quoted-printable")) {
334                                         is_qp = 1;
335                                         remove_token(thisname, j, ';');
336                                 }
337                                 if (!strcasecmp(buf, "encoding=base64")) {
338                                         is_b64 = 1;
339                                         remove_token(thisname, j, ';');
340                                 }
341                         }
342                         
343                         len = strlen(v->prop[i].value);
344                         /* if we have some untagged QP, detect it here. */
345                         if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL))
346                                 utf8ify_rfc822_string(v->prop[i].value);
347
348                         if (is_qp) {
349                                 // %ff can become 6 bytes in utf8 
350                                 thisvalue = malloc(len * 2 + 3); 
351                                 j = CtdlDecodeQuotedPrintable(
352                                         thisvalue, v->prop[i].value,
353                                         len);
354                                 thisvalue[j] = 0;
355                         }
356                         else if (is_b64) {
357                                 // ff will become one byte..
358                                 thisvalue = malloc(len + 50);
359                                 CtdlDecodeBase64(
360                                         thisvalue, v->prop[i].value,
361                                         strlen(v->prop[i].value) );
362                         }
363                         else {
364                                 thisvalue = strdup(v->prop[i].value);
365                         }
366         
367                         /** Various fields we may encounter ***/
368         
369                         /** N is name, but only if there's no FN already there */
370                         if (!strcasecmp(firsttoken, "n")) {
371                                 if (IsEmptyStr(fullname)) {
372                                         strcpy(fullname, thisvalue);
373                                         vcard_n_prettyize(fullname);
374                                 }
375                         }
376         
377                         /** FN (full name) is a true 'display name' field */
378                         else if (!strcasecmp(firsttoken, "fn")) {
379                                 strcpy(fullname, thisvalue);
380                         }
381
382                         /** title */
383                         else if (!strcasecmp(firsttoken, "title")) {
384                                 strcpy(title, thisvalue);
385                         }
386         
387                         /** organization */
388                         else if (!strcasecmp(firsttoken, "org")) {
389                                 strcpy(org, thisvalue);
390                         }
391         
392                         else if (!strcasecmp(firsttoken, "email")) {
393                                 size_t len;
394                                 if (!IsEmptyStr(mailto)) strcat(mailto, "<br />");
395                                 strcat(mailto,
396                                         "<a href=\"display_enter"
397                                         "?force_room=_MAIL_?recp=");
398
399                                 len = strlen(mailto);
400                                 urlesc(&mailto[len], SIZ - len, "\"");
401                                 len = strlen(mailto);
402                                 urlesc(&mailto[len], SIZ - len,  fullname);
403                                 len = strlen(mailto);
404                                 urlesc(&mailto[len], SIZ - len, "\" <");
405                                 len = strlen(mailto);
406                                 urlesc(&mailto[len], SIZ - len, thisvalue);
407                                 len = strlen(mailto);
408                                 urlesc(&mailto[len], SIZ - len, ">");
409
410                                 strcat(mailto, "\">");
411                                 len = strlen(mailto);
412                                 stresc(mailto+len, SIZ - len, thisvalue, 1, 1);
413                                 strcat(mailto, "</A>");
414                         }
415                         else if (!strcasecmp(firsttoken, "tel")) {
416                                 if (!IsEmptyStr(phone)) strcat(phone, "<br />");
417                                 strcat(phone, thisvalue);
418                                 for (j=0; j<num_tokens(thisname, ';'); ++j) {
419                                         extract_token(buf, thisname, j, ';', sizeof buf);
420                                         if (!strcasecmp(buf, "tel"))
421                                                 strcat(phone, "");
422                                         else if (!strcasecmp(buf, "work"))
423                                                 strcat(phone, _(" (work)"));
424                                         else if (!strcasecmp(buf, "home"))
425                                                 strcat(phone, _(" (home)"));
426                                         else if (!strcasecmp(buf, "cell"))
427                                                 strcat(phone, _(" (cell)"));
428                                         else {
429                                                 strcat(phone, " (");
430                                                 strcat(phone, buf);
431                                                 strcat(phone, ")");
432                                         }
433                                 }
434                         }
435                         else if (!strcasecmp(firsttoken, "adr")) {
436                                 if (pass == 2) {
437                                         StrBufAppendPrintf(Target, "<TR><TD>");
438                                         StrBufAppendPrintf(Target, _("Address:"));
439                                         StrBufAppendPrintf(Target, "</TD><TD>");
440                                         for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
441                                                 extract_token(buf, thisvalue, j, ';', sizeof buf);
442                                                 if (!IsEmptyStr(buf)) {
443                                                         StrEscAppend(Target, NULL, buf, 0, 0);
444                                                         if (j<3) StrBufAppendPrintf(Target, "<br />");
445                                                         else StrBufAppendPrintf(Target, " ");
446                                                 }
447                                         }
448                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
449                                 }
450                         }
451                         /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { 
452                                 // Only output on second pass
453                                 StrBufAppendPrintf(Target, "<tr><td>");
454                                 StrBufAppendPrintf(Target, _("Photo:"));
455                                 StrBufAppendPrintf(Target, "</td><td>");
456                                 StrBufAppendPrintf(Target, "<img src=\"/vcardphoto/%ld/\" alt=\"Contact photo\"/>",msgnum);
457                                 StrBufAppendPrintf(Target, "</td></tr>\n");
458                         } */
459                         else if (!strcasecmp(firsttoken, "version")) {
460                                 /* ignore */
461                         }
462                         else if (!strcasecmp(firsttoken, "rev")) {
463                                 /* ignore */
464                         }
465                         else if (!strcasecmp(firsttoken, "label")) {
466                                 /* ignore */
467                         }
468                         else {
469
470                                 /*** Don't show extra fields.  They're ugly.
471                                 if (pass == 2) {
472                                         StrBufAppendPrintf(Target, "<TR><TD>");
473                                         StrEscAppend(Target, NULL, thisname, 0, 0);
474                                         StrBufAppendPrintf(Target, "</TD><TD>");
475                                         StrEscAppend(Target, NULL, thisvalue, 0, 0);
476                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
477                                 }
478                                 ***/
479                         }
480         
481                         free(thisname);
482                         free(thisvalue);
483                 }
484         
485                 if (pass == 1) {
486                         StrBufAppendPrintf(Target, "<TR BGCOLOR=\"#AAAAAA\">"
487                         "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
488                         "<IMG ALIGN=CENTER src=\"static/viewcontacts_48x.gif\">"
489                         "<FONT SIZE=+1><B>");
490                         StrEscAppend(Target, NULL, fullname, 0, 0);
491                         StrBufAppendPrintf(Target, "</B></FONT>");
492                         if (!IsEmptyStr(title)) {
493                                 StrBufAppendPrintf(Target, "<div align=right>");
494                                 StrEscAppend(Target, NULL, title, 0, 0);
495                                 StrBufAppendPrintf(Target, "</div>");
496                         }
497                         if (!IsEmptyStr(org)) {
498                                 StrBufAppendPrintf(Target, "<div align=right>");
499                                 StrEscAppend(Target, NULL, org, 0, 0);
500                                 StrBufAppendPrintf(Target, "</div>");
501                         }
502                         StrBufAppendPrintf(Target, "</TD></TR>\n");
503                 
504                         if (!IsEmptyStr(phone)) {
505                                 StrBufAppendPrintf(Target, "<tr><td>");
506                                 StrBufAppendPrintf(Target, _("Telephone:"));
507                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", phone);
508                         }
509                         if (!IsEmptyStr(mailto)) {
510                                 StrBufAppendPrintf(Target, "<tr><td>");
511                                 StrBufAppendPrintf(Target, _("E-mail:"));
512                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", mailto);
513                         }
514                 }
515
516         }
517
518         StrBufAppendPrintf(Target, "</table></div>\n");
519 }
520
521
522
523 /**
524  * \brief  Display a textual vCard
525  * (Converts to a vCard object and then calls the actual display function)
526  * Set 'full' to nonzero to display the whole card instead of a one-liner.
527  * Or, if "storename" is non-NULL, just store the person's name in that
528  * buffer instead of displaying the card at all.
529  * \param vcard_source the buffer containing the vcard text
530  * \param alpha what???
531  * \param full should we usse all lines?
532  * \param storename where to store???
533  * \param msgnum Citadel message pointer
534  */
535 void display_vcard(StrBuf *Target, 
536                    StrBuf *vcard_source, 
537                    char alpha, 
538                    int full, 
539                    char **storename, 
540                    long msgnum) 
541 {
542         struct vCard *v;
543         char *name;
544         StrBuf *Buf;
545         StrBuf *Buf2;
546         char this_alpha = 0;
547
548         v = VCardLoad(vcard_source);
549
550         if (v == NULL) return;
551
552         name = vcard_get_prop(v, "n", 1, 0, 0);
553         if (name != NULL) {
554                 Buf = NewStrBufPlain(name, -1);
555                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
556                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
557                 this_alpha = ChrPtr(Buf)[0];
558                 FreeStrBuf(&Buf);
559                 FreeStrBuf(&Buf2);
560         }
561
562         if (storename != NULL) {
563                 fetchname_parsed_vcard(v, storename);
564         }
565         else if (       (alpha == 0)
566                         || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
567                         || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
568                 ) {
569                 display_parsed_vcard(Target, v, full,msgnum);
570         }
571
572         vcard_free(v);
573 }
574
575
576
577 /**
578  * \brief Render the address book using info we gathered during the scan
579  * \param addrbook the addressbook to render
580  * \param num_ab the number of the addressbook
581  */
582 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
583         int i = 0;
584         int displayed = 0;
585         int bg = 0;
586         static int NAMESPERPAGE = 60;
587         int num_pages = 0;
588         int tabfirst = 0;
589         char tabfirst_label[64];
590         int tablast = 0;
591         char tablast_label[64];
592         char this_tablabel[64];
593         int page = 0;
594         char **tablabels;
595
596         if (num_ab == 0) {
597                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
598                 wprintf(_("This address book is empty."));
599                 wprintf("</i></div>\n");
600                 return;
601         }
602
603         if (num_ab > 1) {
604                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
605         }
606
607         num_pages = (num_ab / NAMESPERPAGE) + 1;
608
609         tablabels = malloc(num_pages * sizeof (char *));
610         if (tablabels == NULL) {
611                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
612                 wprintf(_("An internal error has occurred."));
613                 wprintf("</i></div>\n");
614                 return;
615         }
616
617         for (i=0; i<num_pages; ++i) {
618                 tabfirst = i * NAMESPERPAGE;
619                 tablast = tabfirst + NAMESPERPAGE - 1;
620                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
621                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
622                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
623                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
624                 tablabels[i] = strdup(this_tablabel);
625         }
626
627         tabbed_dialog(num_pages, tablabels);
628         page = (-1);
629
630         for (i=0; i<num_ab; ++i) {
631
632                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
633                         page = (i / NAMESPERPAGE);
634                         if (page > 0) {
635                                 wprintf("</tr></table>\n");
636                                 end_tab(page-1, num_pages);
637                         }
638                         begin_tab(page, num_pages);
639                         wprintf("<table border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
640                         displayed = 0;
641                 }
642
643                 if ((displayed % 4) == 0) {
644                         if (displayed > 0) {
645                                 wprintf("</tr>\n");
646                         }
647                         bg = 1 - bg;
648                         wprintf("<tr bgcolor=\"#%s\">",
649                                 (bg ? "DDDDDD" : "FFFFFF")
650                         );
651                 }
652         
653                 wprintf("<td>");
654
655                 wprintf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
656                         addrbook[i].ab_msgnum);
657                 wprintf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
658                 vcard_n_prettyize(addrbook[i].ab_name);
659                 escputs(addrbook[i].ab_name);
660                 wprintf("</a></td>\n");
661                 ++displayed;
662         }
663
664         /* Placeholders for empty columns at end */
665         if ((num_ab % 4) != 0) {
666                 for (i=0; i<(4-(num_ab % 4)); ++i) {
667                         wprintf("<td>&nbsp;</td>");
668                 }
669         }
670
671         wprintf("</tr></table>\n");
672         end_tab((num_pages-1), num_pages);
673
674         begin_tab(num_pages, num_pages);
675         /* FIXME there ought to be something here */
676         end_tab(num_pages, num_pages);
677
678         for (i=0; i<num_pages; ++i) {
679                 free(tablabels[i]);
680         }
681         free(tablabels);
682 }
683
684
685
686
687 /*
688  * Edit the vCard component of a MIME message.  
689  * Supply the message number
690  * and MIME part number to fetch.  Or, specify -1 for the message number
691  * to start with a blank card.
692  */
693 void do_edit_vcard(long msgnum, char *partnum, 
694                    message_summary *VCMsg,
695                    wc_mime_attachment *VCAtt,
696                    char *return_to, 
697                    const char *force_room) {
698         message_summary *Msg = NULL;
699         wc_mime_attachment *VCMime = NULL;
700         struct vCard *v;
701         int i;
702         char *key, *value;
703         char whatuser[256];
704
705         char lastname[256];
706         char firstname[256];
707         char middlename[256];
708         char prefix[256];
709         char suffix[256];
710         char pobox[256];
711         char extadr[256];
712         char street[256];
713         char city[256];
714         char state[256];
715         char zipcode[256];
716         char country[256];
717         char hometel[256];
718         char worktel[256];
719         char faxtel[256];
720         char mobiletel[256];
721         char primary_inetemail[256];
722         char other_inetemail[SIZ];
723         char extrafields[SIZ];
724         char fullname[256];
725         char title[256];
726         char org[256];
727
728         lastname[0] = 0;
729         firstname[0] = 0;
730         middlename[0] = 0;
731         prefix[0] = 0;
732         suffix[0] = 0;
733         pobox[0] = 0;
734         extadr[0] = 0;
735         street[0] = 0;
736         city[0] = 0;
737         state[0] = 0;
738         zipcode[0] = 0;
739         country[0] = 0;
740         hometel[0] = 0;
741         worktel[0] = 0;
742         faxtel[0] = 0;
743         mobiletel[0] = 0;
744         primary_inetemail[0] = 0;
745         other_inetemail[0] = 0;
746         title[0] = 0;
747         org[0] = 0;
748         extrafields[0] = 0;
749         fullname[0] = 0;
750
751         safestrncpy(whatuser, "", sizeof whatuser);
752
753         if ((msgnum >= 0) || 
754             ((VCMsg != NULL) && (VCAtt != NULL)))
755         {
756                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
757
758                         Msg = (message_summary *) malloc(sizeof(message_summary));
759                         memset(Msg, 0, sizeof(message_summary));
760                         Msg->msgnum = msgnum;
761                         VCMime = load_vcard(Msg);
762                         if (VCMime == NULL) {
763                                 convenience_page("770000", _("Error"), "");///TODO: important message
764                                 DestroyMessageSummary(Msg);
765                                 return;
766                         }
767                 
768                         v = VCardLoad(VCMime->Data);
769                 }
770                 else {
771                         v = VCardLoad(VCAtt->Data);
772                 }
773         
774                 /* Populate the variables for our form */
775                 i = 0;
776                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
777                         char prp[256];  /* property name */
778                         char prm[256];  /* parameters */
779
780                         value = vcard_get_prop(v, "", 0, i++, 0);
781
782
783                         extract_token(prp, key, 0, ';', sizeof prp);
784                         safestrncpy(prm, key, sizeof prm);
785                         remove_token(prm, 0, ';');
786
787                         if (!strcasecmp(prp, "n")) {
788                                 extract_token(lastname, value, 0, ';', sizeof lastname);
789                                 extract_token(firstname, value, 1, ';', sizeof firstname);
790                                 extract_token(middlename, value, 2, ';', sizeof middlename);
791                                 extract_token(prefix, value, 3, ';', sizeof prefix);
792                                 extract_token(suffix, value, 4, ';', sizeof suffix);
793                         }
794
795                         else if (!strcasecmp(prp, "fn")) {
796                                 safestrncpy(fullname, value, sizeof fullname);
797                         }
798
799                         else if (!strcasecmp(prp, "title")) {
800                                 safestrncpy(title, value, sizeof title);
801                         }
802         
803                         else if (!strcasecmp(prp, "org")) {
804                                 safestrncpy(org, value, sizeof org);
805                         }
806         
807                         else if (!strcasecmp(prp, "adr")) {
808                                 extract_token(pobox, value, 0, ';', sizeof pobox);
809                                 extract_token(extadr, value, 1, ';', sizeof extadr);
810                                 extract_token(street, value, 2, ';', sizeof street);
811                                 extract_token(city, value, 3, ';', sizeof city);
812                                 extract_token(state, value, 4, ';', sizeof state);
813                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
814                                 extract_token(country, value, 6, ';', sizeof country);
815                         }
816
817                         else if (!strcasecmp(prp, "tel")) {
818
819                                 if (bmstrcasestr(prm, "home")) {
820                                         extract_token(hometel, value, 0, ';', sizeof hometel);
821                                 }
822                                 else if (bmstrcasestr(prm, "work")) {
823                                         extract_token(worktel, value, 0, ';', sizeof worktel);
824                                 }
825                                 else if (bmstrcasestr(prm, "fax")) {
826                                         extract_token(faxtel, value, 0, ';', sizeof faxtel);
827                                 }
828                                 else if (bmstrcasestr(prm, "cell")) {
829                                         extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
830                                 }
831                                 else {  /* Missing or unknown type; put it in the home phone */
832                                         extract_token(hometel, value, 0, ';', sizeof hometel);
833                                 }
834                         }
835         
836                         else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
837                                 if (primary_inetemail[0] == 0) {
838                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
839                                 }
840                                 else {
841                                         if (other_inetemail[0] != 0) {
842                                                 strcat(other_inetemail, "\n");
843                                         }
844                                         strcat(other_inetemail, value);
845                                 }
846                         }
847
848                         /* Unrecognized properties are preserved here so we don't discard them
849                          * just because the vCard was edited with WebCit.
850                          */
851                         else {
852                                 strcat(extrafields, key);
853                                 strcat(extrafields, ":");
854                                 strcat(extrafields, value);
855                                 strcat(extrafields, "\n");
856                         }
857         
858                 }
859         
860                 vcard_free(v);
861         }
862
863         /* Display the form */
864         output_headers(1, 1, 1, 0, 0, 0);
865
866         svput("BOXTITLE", WCS_STRING, _("Edit contact information"));
867         do_template("beginboxx", NULL);
868
869         wprintf("<form method=\"POST\" action=\"submit_vcard\">\n");
870         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
871
872         if (force_room != NULL) {
873                 wprintf("<input type=\"hidden\" name=\"force_room\" value=\"");
874                 escputs(force_room);
875                 wprintf("\">\n");
876         }
877
878         wprintf("<div class=\"fix_scrollbar_bug\">"
879                 "<table class=\"vcard_edit_background\"><tr><td>\n");
880
881         wprintf("<table border=0><tr>"
882                 "<td>%s</td>"
883                 "<td>%s</td>"
884                 "<td>%s</td>"
885                 "<td>%s</td>"
886                 "<td>%s</td></tr>\n",
887                 _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix")
888         );
889         wprintf("<tr><td><input type=\"text\" name=\"prefix\" "
890                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
891                 prefix);
892         wprintf("<td><input type=\"text\" name=\"firstname\" "
893                 "value=\"%s\" maxlength=\"29\"></td>",
894                 firstname);
895         wprintf("<td><input type=\"text\" name=\"middlename\" "
896                 "value=\"%s\" maxlength=\"29\"></td>",
897                 middlename);
898         wprintf("<td><input type=\"text\" name=\"lastname\" "
899                 "value=\"%s\" maxlength=\"29\"></td>",
900                 lastname);
901         wprintf("<td><input type=\"text\" name=\"suffix\" "
902                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
903                 suffix);
904
905         wprintf("<table  class=\"vcard_edit_background_alt\">");
906         wprintf("<tr><td>");
907
908         wprintf(_("Display name:"));
909         wprintf("<br>"
910                 "<input type=\"text\" name=\"fullname\" "
911                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
912                 fullname
913         );
914
915         wprintf(_("Title:"));
916         wprintf("<br>"
917                 "<input type=\"text\" name=\"title\" "
918                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
919                 title
920         );
921
922         wprintf(_("Organization:"));
923         wprintf("<br>"
924                 "<input type=\"text\" name=\"org\" "
925                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
926                 org
927         );
928
929         wprintf("</td><td>");
930
931         wprintf("<table border=0>");
932         wprintf("<tr><td>");
933         wprintf(_("PO box:"));
934         wprintf("</td><td>"
935                 "<input type=\"text\" name=\"pobox\" "
936                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
937                 pobox);
938         wprintf("<tr><td>");
939         wprintf(_("Address:"));
940         wprintf("</td><td>"
941                 "<input type=\"text\" name=\"extadr\" "
942                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
943                 extadr);
944         wprintf("<tr><td> </td><td>"
945                 "<input type=\"text\" name=\"street\" "
946                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
947                 street);
948         wprintf("<tr><td>");
949         wprintf(_("City:"));
950         wprintf("</td><td>"
951                 "<input type=\"text\" name=\"city\" "
952                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
953                 city);
954         wprintf("<tr><td>");
955         wprintf(_("State:"));
956         wprintf("</td><td>"
957                 "<input type=\"text\" name=\"state\" "
958                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
959                 state);
960         wprintf("<tr><td>");
961         wprintf(_("ZIP code:"));
962         wprintf("</td><td>"
963                 "<input type=\"text\" name=\"zipcode\" "
964                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
965                 zipcode);
966         wprintf("<tr><td>");
967         wprintf(_("Country:"));
968         wprintf("</td><td>"
969                 "<input type=\"text\" name=\"country\" "
970                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
971                 country);
972         wprintf("</table>\n");
973
974         wprintf("</table>\n");
975
976         wprintf("<table border=0><tr><td>");
977         wprintf(_("Home telephone:"));
978         wprintf("</td>"
979                 "<td><input type=\"text\" name=\"hometel\" "
980                 "value=\"%s\" maxlength=\"29\"></td>\n",
981                 hometel);
982         wprintf("<td>");
983         wprintf(_("Work telephone:"));
984         wprintf("</td>"
985                 "<td><input type=\"text\" name=\"worktel\" "
986                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
987                 worktel);
988         wprintf("<tr><td>");
989         wprintf(_("Mobile telephone:"));
990         wprintf("</td>"
991                 "<td><input type=\"text\" name=\"mobiletel\" "
992                 "value=\"%s\" maxlength=\"29\"></td>\n",
993                 mobiletel);
994         wprintf("<td>");
995         wprintf(_("Fax number:"));
996         wprintf("</td>"
997                 "<td><input type=\"text\" name=\"faxtel\" "
998                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
999                 faxtel);
1000
1001         wprintf("<table class=\"vcard_edit_background_alt\">");
1002         wprintf("<tr><td>");
1003
1004         wprintf("<table border=0><TR>"
1005                 "<td valign=top>");
1006         wprintf(_("Primary Internet e-mail address"));
1007         wprintf("<br />"
1008                 "<input type=\"text\" name=\"primary_inetemail\" "
1009                 "size=40 maxlength=60 value=\"");
1010         escputs(primary_inetemail);
1011         wprintf("\"><br />"
1012                 "</td><td valign=top>");
1013         wprintf(_("Internet e-mail aliases"));
1014         wprintf("<br />"
1015                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1016         escputs(other_inetemail);
1017         wprintf("</textarea></td></tr></table>\n");
1018
1019         wprintf("</td></tr></table>\n");
1020
1021         wprintf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1022         escputs(extrafields);
1023         wprintf("\">\n");
1024
1025         wprintf("<input type=\"hidden\" name=\"return_to\" value=\"");
1026         urlescputs(return_to);
1027         wprintf("\">\n");
1028
1029         wprintf("<div class=\"buttons\">\n"
1030                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1031                 "&nbsp;"
1032                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1033                 "</div></form>\n",
1034                 _("Save changes"),
1035                 _("Cancel")
1036         );
1037         
1038         wprintf("</td></tr></table>\n");
1039         do_template("endbox", NULL);
1040         wDumpContent(1);
1041         if (Msg != NULL) {
1042                 DestroyMessageSummary(Msg);
1043         }
1044 }
1045
1046
1047 /*
1048  *  commit the edits to the citadel server
1049  */
1050 void edit_vcard(void) {
1051         long msgnum;
1052         char *partnum;
1053
1054         msgnum = lbstr("msgnum");
1055         partnum = bstr("partnum");
1056         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1057 }
1058
1059
1060
1061 /*
1062  *  parse edited vcard from the browser
1063  */
1064 void submit_vcard(void) {
1065         wcsession *WCC = WC;
1066         struct vCard *v;
1067         char *serialized_vcard;
1068         char buf[SIZ];
1069         StrBuf *Buf;
1070         int i;
1071
1072         if (!havebstr("ok_button")) { 
1073                 readloop(readnew);
1074                 return;
1075         }
1076
1077         if (havebstr("force_room")) {
1078                 if (gotoroom(sbstr("force_room")) != 200) {
1079                         StrBufAppendBufPlain(WCC->ImportantMsg,
1080                                              _("Unable to enter the room to save your message"),
1081                                              -1, 0);
1082                         StrBufAppendBufPlain(WCC->ImportantMsg,
1083                                              HKEY(": "), 0);
1084                         StrBufAppendBuf(WCC->ImportantMsg, sbstr("force_room"), 0);
1085                         StrBufAppendBufPlain(WCC->ImportantMsg,
1086                                              HKEY("; "), 0);
1087                                                
1088                         StrBufAppendBufPlain(WCC->ImportantMsg,
1089                                              _("Aborting."),
1090                                              -1, 0);
1091                         /// todo: call the master dispatcher again...
1092                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1093                                 select_user_to_edit(NULL);
1094                         }
1095                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1096                                 do_welcome();
1097                         }
1098                         else {
1099                                 readloop(readnew);
1100                         }
1101                         return;
1102                 }
1103         }
1104
1105         sprintf(buf, "ENT0 1|||4||");
1106         serv_puts(buf);
1107         serv_getln(buf, sizeof buf);
1108         if (buf[0] != '4') {
1109                 edit_vcard();
1110                 return;
1111         }
1112
1113         /* Make a vCard structure out of the data supplied in the form */
1114         Buf = NewStrBuf();
1115         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1116                      bstr("extrafields")
1117         );
1118         v = VCardLoad(Buf);     /** Start with the extra fields */
1119         FreeStrBuf(&Buf);
1120         if (v == NULL) {
1121                 safestrncpy(WCC->ImportantMessage,
1122                         _("An error has occurred."),
1123                         sizeof WCC->ImportantMessage
1124                 );
1125                 edit_vcard();
1126                 return;
1127         }
1128
1129         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1130                 bstr("lastname"),
1131                 bstr("firstname"),
1132                 bstr("middlename"),
1133                 bstr("prefix"),
1134                 bstr("suffix") );
1135         vcard_add_prop(v, "n", buf);
1136         
1137         vcard_add_prop(v, "title", bstr("title"));
1138         vcard_add_prop(v, "fn", bstr("fullname"));
1139         vcard_add_prop(v, "org", bstr("org"));
1140
1141         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1142                 bstr("pobox"),
1143                 bstr("extadr"),
1144                 bstr("street"),
1145                 bstr("city"),
1146                 bstr("state"),
1147                 bstr("zipcode"),
1148                 bstr("country") );
1149         vcard_add_prop(v, "adr", buf);
1150
1151         vcard_add_prop(v, "tel;home", bstr("hometel"));
1152         vcard_add_prop(v, "tel;work", bstr("worktel"));
1153         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1154         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1155         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1156
1157         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1158                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1159                 if (!IsEmptyStr(buf)) {
1160                         vcard_add_prop(v, "email;internet", buf);
1161                 }
1162         }
1163
1164         serialized_vcard = vcard_serialize(v);
1165         vcard_free(v);
1166         if (serialized_vcard == NULL) {
1167                 safestrncpy(WCC->ImportantMessage,
1168                         _("An error has occurred."),
1169                         sizeof WCC->ImportantMessage
1170                 );
1171                 edit_vcard();
1172                 return;
1173         }
1174
1175         serv_puts("Content-type: text/x-vcard; charset=UTF-8");
1176         serv_puts("");
1177         serv_printf("%s\r\n", serialized_vcard);
1178         serv_puts("000");
1179         free(serialized_vcard);
1180
1181         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1182                 select_user_to_edit(NULL);
1183         }
1184         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1185                 do_welcome();
1186         }
1187         else {
1188                 readloop(readnew);
1189         }
1190 }
1191
1192
1193
1194 /*
1195  * Extract an embedded photo from a vCard for display on the client
1196  */
1197 void display_vcard_photo_img(void)
1198 {
1199         long msgnum = 0L;
1200         StrBuf *vcard;
1201         struct vCard *v;
1202         char *photosrc;
1203         const char *contentType;
1204         wcsession *WCC = WC;
1205
1206         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1207         
1208         vcard = load_mimepart(msgnum,"1");
1209         v = VCardLoad(vcard);
1210         
1211         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1212         FlushStrBuf(WCC->WBuf);
1213         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1214         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1215                 FlushStrBuf(WCC->WBuf);
1216                 
1217                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1218                 output_headers(0, 0, 0, 0, 0, 0);
1219                 hprintf("Content-Type: text/plain\r\n");
1220                 wprintf(_("Could Not decode vcard photo\n"));
1221                 end_burst();
1222                 return;
1223         }
1224         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1225         http_transmit_thing(contentType, 0);
1226         free(v);
1227         free(photosrc);
1228 }
1229
1230
1231
1232 void 
1233 InitModule_VCARD
1234 (void)
1235 {
1236         WebcitAddUrlHandler(HKEY("edit_vcard"), edit_vcard, 0);
1237         WebcitAddUrlHandler(HKEY("submit_vcard"), submit_vcard, 0);
1238         WebcitAddUrlHandler(HKEY("vcardphoto"), display_vcard_photo_img, NEED_URL);
1239 }
1240