* More style cleanup
[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  * 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  *
530  * vcard_source the buffer containing the vcard text
531  * alpha        Display only if name begins with this letter of the alphabet
532  * full         Display the full vCard (otherwise just the display name)
533  * storename    If not NULL, also store the display name here
534  * msgnum       Citadel message pointer
535  */
536 void display_vcard(StrBuf *Target, 
537                    StrBuf *vcard_source, 
538                    char alpha, 
539                    int full, 
540                    char **storename, 
541                    long msgnum) 
542 {
543         struct vCard *v;
544         char *name;
545         StrBuf *Buf;
546         StrBuf *Buf2;
547         char this_alpha = 0;
548
549         v = VCardLoad(vcard_source);
550
551         if (v == NULL) return;
552
553         name = vcard_get_prop(v, "n", 1, 0, 0);
554         if (name != NULL) {
555                 Buf = NewStrBufPlain(name, -1);
556                 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
557                 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
558                 this_alpha = ChrPtr(Buf)[0];
559                 FreeStrBuf(&Buf);
560                 FreeStrBuf(&Buf2);
561         }
562
563         if (storename != NULL) {
564                 fetchname_parsed_vcard(v, storename);
565         }
566         else if (
567                 (alpha == 0)
568                 || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)))
569                 || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
570         ) {
571                 display_parsed_vcard(Target, v, full,msgnum);
572         }
573
574         vcard_free(v);
575 }
576
577
578
579 /*
580  * Render the address book using info we gathered during the scan
581  *
582  * addrbook     the addressbook to render
583  * num_ab       the number of the addressbook
584  */
585 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
586         int i = 0;
587         int displayed = 0;
588         int bg = 0;
589         static int NAMESPERPAGE = 60;
590         int num_pages = 0;
591         int tabfirst = 0;
592         char tabfirst_label[64];
593         int tablast = 0;
594         char tablast_label[64];
595         char this_tablabel[64];
596         int page = 0;
597         char **tablabels;
598
599         if (num_ab == 0) {
600                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
601                 wprintf(_("This address book is empty."));
602                 wprintf("</i></div>\n");
603                 return;
604         }
605
606         if (num_ab > 1) {
607                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
608         }
609
610         num_pages = (num_ab / NAMESPERPAGE) + 1;
611
612         tablabels = malloc(num_pages * sizeof (char *));
613         if (tablabels == NULL) {
614                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
615                 wprintf(_("An internal error has occurred."));
616                 wprintf("</i></div>\n");
617                 return;
618         }
619
620         for (i=0; i<num_pages; ++i) {
621                 tabfirst = i * NAMESPERPAGE;
622                 tablast = tabfirst + NAMESPERPAGE - 1;
623                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
624                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
625                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
626                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
627                 tablabels[i] = strdup(this_tablabel);
628         }
629
630         tabbed_dialog(num_pages, tablabels);
631         page = (-1);
632
633         for (i=0; i<num_ab; ++i) {
634
635                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
636                         page = (i / NAMESPERPAGE);
637                         if (page > 0) {
638                                 wprintf("</tr></table>\n");
639                                 end_tab(page-1, num_pages);
640                         }
641                         begin_tab(page, num_pages);
642                         wprintf("<table border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
643                         displayed = 0;
644                 }
645
646                 if ((displayed % 4) == 0) {
647                         if (displayed > 0) {
648                                 wprintf("</tr>\n");
649                         }
650                         bg = 1 - bg;
651                         wprintf("<tr bgcolor=\"#%s\">",
652                                 (bg ? "DDDDDD" : "FFFFFF")
653                         );
654                 }
655         
656                 wprintf("<td>");
657
658                 wprintf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
659                         addrbook[i].ab_msgnum);
660                 wprintf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
661                 vcard_n_prettyize(addrbook[i].ab_name);
662                 escputs(addrbook[i].ab_name);
663                 wprintf("</a></td>\n");
664                 ++displayed;
665         }
666
667         /* Placeholders for empty columns at end */
668         if ((num_ab % 4) != 0) {
669                 for (i=0; i<(4-(num_ab % 4)); ++i) {
670                         wprintf("<td>&nbsp;</td>");
671                 }
672         }
673
674         wprintf("</tr></table>\n");
675         end_tab((num_pages-1), num_pages);
676
677         begin_tab(num_pages, num_pages);
678         /* FIXME there ought to be something here */
679         end_tab(num_pages, num_pages);
680
681         for (i=0; i<num_pages; ++i) {
682                 free(tablabels[i]);
683         }
684         free(tablabels);
685 }
686
687
688
689
690 /*
691  * Edit the vCard component of a MIME message.  
692  * Supply the message number
693  * and MIME part number to fetch.  Or, specify -1 for the message number
694  * to start with a blank card.
695  */
696 void do_edit_vcard(long msgnum, char *partnum, 
697                    message_summary *VCMsg,
698                    wc_mime_attachment *VCAtt,
699                    char *return_to, 
700                    const char *force_room) {
701         message_summary *Msg = NULL;
702         wc_mime_attachment *VCMime = NULL;
703         struct vCard *v;
704         int i;
705         char *key, *value;
706         char whatuser[256];
707
708         char lastname[256];
709         char firstname[256];
710         char middlename[256];
711         char prefix[256];
712         char suffix[256];
713         char pobox[256];
714         char extadr[256];
715         char street[256];
716         char city[256];
717         char state[256];
718         char zipcode[256];
719         char country[256];
720         char hometel[256];
721         char worktel[256];
722         char faxtel[256];
723         char mobiletel[256];
724         char primary_inetemail[256];
725         char other_inetemail[SIZ];
726         char extrafields[SIZ];
727         char fullname[256];
728         char title[256];
729         char org[256];
730
731         lastname[0] = 0;
732         firstname[0] = 0;
733         middlename[0] = 0;
734         prefix[0] = 0;
735         suffix[0] = 0;
736         pobox[0] = 0;
737         extadr[0] = 0;
738         street[0] = 0;
739         city[0] = 0;
740         state[0] = 0;
741         zipcode[0] = 0;
742         country[0] = 0;
743         hometel[0] = 0;
744         worktel[0] = 0;
745         faxtel[0] = 0;
746         mobiletel[0] = 0;
747         primary_inetemail[0] = 0;
748         other_inetemail[0] = 0;
749         title[0] = 0;
750         org[0] = 0;
751         extrafields[0] = 0;
752         fullname[0] = 0;
753
754         safestrncpy(whatuser, "", sizeof whatuser);
755
756         if ((msgnum >= 0) || 
757             ((VCMsg != NULL) && (VCAtt != NULL)))
758         {
759                 if ((VCMsg == NULL) && (VCAtt == NULL)) {
760
761                         Msg = (message_summary *) malloc(sizeof(message_summary));
762                         memset(Msg, 0, sizeof(message_summary));
763                         Msg->msgnum = msgnum;
764                         VCMime = load_vcard(Msg);
765                         if (VCMime == NULL) {
766                                 convenience_page("770000", _("Error"), "");///TODO: important message
767                                 DestroyMessageSummary(Msg);
768                                 return;
769                         }
770                 
771                         v = VCardLoad(VCMime->Data);
772                 }
773                 else {
774                         v = VCardLoad(VCAtt->Data);
775                 }
776         
777                 /* Populate the variables for our form */
778                 i = 0;
779                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
780                         char prp[256];  /* property name */
781                         char prm[256];  /* parameters */
782
783                         value = vcard_get_prop(v, "", 0, i++, 0);
784
785
786                         extract_token(prp, key, 0, ';', sizeof prp);
787                         safestrncpy(prm, key, sizeof prm);
788                         remove_token(prm, 0, ';');
789
790                         if (!strcasecmp(prp, "n")) {
791                                 extract_token(lastname, value, 0, ';', sizeof lastname);
792                                 extract_token(firstname, value, 1, ';', sizeof firstname);
793                                 extract_token(middlename, value, 2, ';', sizeof middlename);
794                                 extract_token(prefix, value, 3, ';', sizeof prefix);
795                                 extract_token(suffix, value, 4, ';', sizeof suffix);
796                         }
797
798                         else if (!strcasecmp(prp, "fn")) {
799                                 safestrncpy(fullname, value, sizeof fullname);
800                         }
801
802                         else if (!strcasecmp(prp, "title")) {
803                                 safestrncpy(title, value, sizeof title);
804                         }
805         
806                         else if (!strcasecmp(prp, "org")) {
807                                 safestrncpy(org, value, sizeof org);
808                         }
809         
810                         else if (!strcasecmp(prp, "adr")) {
811                                 extract_token(pobox, value, 0, ';', sizeof pobox);
812                                 extract_token(extadr, value, 1, ';', sizeof extadr);
813                                 extract_token(street, value, 2, ';', sizeof street);
814                                 extract_token(city, value, 3, ';', sizeof city);
815                                 extract_token(state, value, 4, ';', sizeof state);
816                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
817                                 extract_token(country, value, 6, ';', sizeof country);
818                         }
819
820                         else if (!strcasecmp(prp, "tel")) {
821
822                                 if (bmstrcasestr(prm, "home")) {
823                                         extract_token(hometel, value, 0, ';', sizeof hometel);
824                                 }
825                                 else if (bmstrcasestr(prm, "work")) {
826                                         extract_token(worktel, value, 0, ';', sizeof worktel);
827                                 }
828                                 else if (bmstrcasestr(prm, "fax")) {
829                                         extract_token(faxtel, value, 0, ';', sizeof faxtel);
830                                 }
831                                 else if (bmstrcasestr(prm, "cell")) {
832                                         extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
833                                 }
834                                 else {  /* Missing or unknown type; put it in the home phone */
835                                         extract_token(hometel, value, 0, ';', sizeof hometel);
836                                 }
837                         }
838         
839                         else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
840                                 if (primary_inetemail[0] == 0) {
841                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
842                                 }
843                                 else {
844                                         if (other_inetemail[0] != 0) {
845                                                 strcat(other_inetemail, "\n");
846                                         }
847                                         strcat(other_inetemail, value);
848                                 }
849                         }
850
851                         /* Unrecognized properties are preserved here so we don't discard them
852                          * just because the vCard was edited with WebCit.
853                          */
854                         else {
855                                 strcat(extrafields, key);
856                                 strcat(extrafields, ":");
857                                 strcat(extrafields, value);
858                                 strcat(extrafields, "\n");
859                         }
860         
861                 }
862         
863                 vcard_free(v);
864         }
865
866         /* Display the form */
867         output_headers(1, 1, 1, 0, 0, 0);
868
869         svput("BOXTITLE", WCS_STRING, _("Edit contact information"));
870         do_template("beginboxx", NULL);
871
872         wprintf("<form method=\"POST\" action=\"submit_vcard\">\n");
873         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
874
875         if (force_room != NULL) {
876                 wprintf("<input type=\"hidden\" name=\"force_room\" value=\"");
877                 escputs(force_room);
878                 wprintf("\">\n");
879         }
880
881         wprintf("<div class=\"fix_scrollbar_bug\">"
882                 "<table class=\"vcard_edit_background\"><tr><td>\n");
883
884         wprintf("<table border=0><tr>"
885                 "<td>%s</td>"
886                 "<td>%s</td>"
887                 "<td>%s</td>"
888                 "<td>%s</td>"
889                 "<td>%s</td></tr>\n",
890                 _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix")
891         );
892         wprintf("<tr><td><input type=\"text\" name=\"prefix\" "
893                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
894                 prefix);
895         wprintf("<td><input type=\"text\" name=\"firstname\" "
896                 "value=\"%s\" maxlength=\"29\"></td>",
897                 firstname);
898         wprintf("<td><input type=\"text\" name=\"middlename\" "
899                 "value=\"%s\" maxlength=\"29\"></td>",
900                 middlename);
901         wprintf("<td><input type=\"text\" name=\"lastname\" "
902                 "value=\"%s\" maxlength=\"29\"></td>",
903                 lastname);
904         wprintf("<td><input type=\"text\" name=\"suffix\" "
905                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
906                 suffix);
907
908         wprintf("<table  class=\"vcard_edit_background_alt\">");
909         wprintf("<tr><td>");
910
911         wprintf(_("Display name:"));
912         wprintf("<br>"
913                 "<input type=\"text\" name=\"fullname\" "
914                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
915                 fullname
916         );
917
918         wprintf(_("Title:"));
919         wprintf("<br>"
920                 "<input type=\"text\" name=\"title\" "
921                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
922                 title
923         );
924
925         wprintf(_("Organization:"));
926         wprintf("<br>"
927                 "<input type=\"text\" name=\"org\" "
928                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
929                 org
930         );
931
932         wprintf("</td><td>");
933
934         wprintf("<table border=0>");
935         wprintf("<tr><td>");
936         wprintf(_("PO box:"));
937         wprintf("</td><td>"
938                 "<input type=\"text\" name=\"pobox\" "
939                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
940                 pobox);
941         wprintf("<tr><td>");
942         wprintf(_("Address:"));
943         wprintf("</td><td>"
944                 "<input type=\"text\" name=\"extadr\" "
945                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
946                 extadr);
947         wprintf("<tr><td> </td><td>"
948                 "<input type=\"text\" name=\"street\" "
949                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
950                 street);
951         wprintf("<tr><td>");
952         wprintf(_("City:"));
953         wprintf("</td><td>"
954                 "<input type=\"text\" name=\"city\" "
955                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
956                 city);
957         wprintf("<tr><td>");
958         wprintf(_("State:"));
959         wprintf("</td><td>"
960                 "<input type=\"text\" name=\"state\" "
961                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
962                 state);
963         wprintf("<tr><td>");
964         wprintf(_("ZIP code:"));
965         wprintf("</td><td>"
966                 "<input type=\"text\" name=\"zipcode\" "
967                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
968                 zipcode);
969         wprintf("<tr><td>");
970         wprintf(_("Country:"));
971         wprintf("</td><td>"
972                 "<input type=\"text\" name=\"country\" "
973                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
974                 country);
975         wprintf("</table>\n");
976
977         wprintf("</table>\n");
978
979         wprintf("<table border=0><tr><td>");
980         wprintf(_("Home telephone:"));
981         wprintf("</td>"
982                 "<td><input type=\"text\" name=\"hometel\" "
983                 "value=\"%s\" maxlength=\"29\"></td>\n",
984                 hometel);
985         wprintf("<td>");
986         wprintf(_("Work telephone:"));
987         wprintf("</td>"
988                 "<td><input type=\"text\" name=\"worktel\" "
989                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
990                 worktel);
991         wprintf("<tr><td>");
992         wprintf(_("Mobile telephone:"));
993         wprintf("</td>"
994                 "<td><input type=\"text\" name=\"mobiletel\" "
995                 "value=\"%s\" maxlength=\"29\"></td>\n",
996                 mobiletel);
997         wprintf("<td>");
998         wprintf(_("Fax number:"));
999         wprintf("</td>"
1000                 "<td><input type=\"text\" name=\"faxtel\" "
1001                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
1002                 faxtel);
1003
1004         wprintf("<table class=\"vcard_edit_background_alt\">");
1005         wprintf("<tr><td>");
1006
1007         wprintf("<table border=0><TR>"
1008                 "<td valign=top>");
1009         wprintf(_("Primary Internet e-mail address"));
1010         wprintf("<br />"
1011                 "<input type=\"text\" name=\"primary_inetemail\" "
1012                 "size=40 maxlength=60 value=\"");
1013         escputs(primary_inetemail);
1014         wprintf("\"><br />"
1015                 "</td><td valign=top>");
1016         wprintf(_("Internet e-mail aliases"));
1017         wprintf("<br />"
1018                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1019         escputs(other_inetemail);
1020         wprintf("</textarea></td></tr></table>\n");
1021
1022         wprintf("</td></tr></table>\n");
1023
1024         wprintf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1025         escputs(extrafields);
1026         wprintf("\">\n");
1027
1028         wprintf("<input type=\"hidden\" name=\"return_to\" value=\"");
1029         urlescputs(return_to);
1030         wprintf("\">\n");
1031
1032         wprintf("<div class=\"buttons\">\n"
1033                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1034                 "&nbsp;"
1035                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1036                 "</div></form>\n",
1037                 _("Save changes"),
1038                 _("Cancel")
1039         );
1040         
1041         wprintf("</td></tr></table>\n");
1042         do_template("endbox", NULL);
1043         wDumpContent(1);
1044         if (Msg != NULL) {
1045                 DestroyMessageSummary(Msg);
1046         }
1047 }
1048
1049
1050 /*
1051  *  commit the edits to the citadel server
1052  */
1053 void edit_vcard(void) {
1054         long msgnum;
1055         char *partnum;
1056
1057         msgnum = lbstr("msgnum");
1058         partnum = bstr("partnum");
1059         do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1060 }
1061
1062
1063
1064 /*
1065  *  parse edited vcard from the browser
1066  */
1067 void submit_vcard(void) {
1068         wcsession *WCC = WC;
1069         struct vCard *v;
1070         char *serialized_vcard;
1071         char buf[SIZ];
1072         StrBuf *Buf;
1073         int i;
1074
1075         if (!havebstr("ok_button")) { 
1076                 readloop(readnew);
1077                 return;
1078         }
1079
1080         if (havebstr("force_room")) {
1081                 if (gotoroom(sbstr("force_room")) != 200) {
1082                         StrBufAppendBufPlain(WCC->ImportantMsg,
1083                                              _("Unable to enter the room to save your message"),
1084                                              -1, 0);
1085                         StrBufAppendBufPlain(WCC->ImportantMsg,
1086                                              HKEY(": "), 0);
1087                         StrBufAppendBuf(WCC->ImportantMsg, sbstr("force_room"), 0);
1088                         StrBufAppendBufPlain(WCC->ImportantMsg,
1089                                              HKEY("; "), 0);
1090                                                
1091                         StrBufAppendBufPlain(WCC->ImportantMsg,
1092                                              _("Aborting."),
1093                                              -1, 0);
1094                         /// todo: call the master dispatcher again...
1095                         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1096                                 select_user_to_edit(NULL);
1097                         }
1098                         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1099                                 do_welcome();
1100                         }
1101                         else {
1102                                 readloop(readnew);
1103                         }
1104                         return;
1105                 }
1106         }
1107
1108         sprintf(buf, "ENT0 1|||4||");
1109         serv_puts(buf);
1110         serv_getln(buf, sizeof buf);
1111         if (buf[0] != '4') {
1112                 edit_vcard();
1113                 return;
1114         }
1115
1116         /* Make a vCard structure out of the data supplied in the form */
1117         Buf = NewStrBuf();
1118         StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1119                      bstr("extrafields")
1120         );
1121         v = VCardLoad(Buf);     /* Start with the extra fields */
1122         FreeStrBuf(&Buf);
1123         if (v == NULL) {
1124                 safestrncpy(WCC->ImportantMessage,
1125                         _("An error has occurred."),
1126                         sizeof WCC->ImportantMessage
1127                 );
1128                 edit_vcard();
1129                 return;
1130         }
1131
1132         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1133                 bstr("lastname"),
1134                 bstr("firstname"),
1135                 bstr("middlename"),
1136                 bstr("prefix"),
1137                 bstr("suffix") );
1138         vcard_add_prop(v, "n", buf);
1139         
1140         vcard_add_prop(v, "title", bstr("title"));
1141         vcard_add_prop(v, "fn", bstr("fullname"));
1142         vcard_add_prop(v, "org", bstr("org"));
1143
1144         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1145                 bstr("pobox"),
1146                 bstr("extadr"),
1147                 bstr("street"),
1148                 bstr("city"),
1149                 bstr("state"),
1150                 bstr("zipcode"),
1151                 bstr("country") );
1152         vcard_add_prop(v, "adr", buf);
1153
1154         vcard_add_prop(v, "tel;home", bstr("hometel"));
1155         vcard_add_prop(v, "tel;work", bstr("worktel"));
1156         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1157         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1158         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1159
1160         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1161                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1162                 if (!IsEmptyStr(buf)) {
1163                         vcard_add_prop(v, "email;internet", buf);
1164                 }
1165         }
1166
1167         serialized_vcard = vcard_serialize(v);
1168         vcard_free(v);
1169         if (serialized_vcard == NULL) {
1170                 safestrncpy(WCC->ImportantMessage,
1171                         _("An error has occurred."),
1172                         sizeof WCC->ImportantMessage
1173                 );
1174                 edit_vcard();
1175                 return;
1176         }
1177
1178         serv_puts("Content-type: text/x-vcard; charset=UTF-8");
1179         serv_puts("");
1180         serv_printf("%s\r\n", serialized_vcard);
1181         serv_puts("000");
1182         free(serialized_vcard);
1183
1184         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1185                 select_user_to_edit(NULL);
1186         }
1187         else if (!strcmp(bstr("return_to"), "do_welcome")) {
1188                 do_welcome();
1189         }
1190         else {
1191                 readloop(readnew);
1192         }
1193 }
1194
1195
1196
1197 /*
1198  * Extract an embedded photo from a vCard for display on the client
1199  */
1200 void display_vcard_photo_img(void)
1201 {
1202         long msgnum = 0L;
1203         StrBuf *vcard;
1204         struct vCard *v;
1205         char *photosrc;
1206         const char *contentType;
1207         wcsession *WCC = WC;
1208
1209         msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1210         
1211         vcard = load_mimepart(msgnum,"1");
1212         v = VCardLoad(vcard);
1213         
1214         photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1215         FlushStrBuf(WCC->WBuf);
1216         StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1217         if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1218                 FlushStrBuf(WCC->WBuf);
1219                 
1220                 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1221                 output_headers(0, 0, 0, 0, 0, 0);
1222                 hprintf("Content-Type: text/plain\r\n");
1223                 wprintf(_("Could Not decode vcard photo\n"));
1224                 end_burst();
1225                 return;
1226         }
1227         contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1228         http_transmit_thing(contentType, 0);
1229         free(v);
1230         free(photosrc);
1231 }
1232
1233
1234
1235 void 
1236 InitModule_VCARD
1237 (void)
1238 {
1239         WebcitAddUrlHandler(HKEY("edit_vcard"), edit_vcard, 0);
1240         WebcitAddUrlHandler(HKEY("submit_vcard"), submit_vcard, 0);
1241         WebcitAddUrlHandler(HKEY("vcardphoto"), display_vcard_photo_img, NEED_URL);
1242 }
1243