* add transitional beginboxx template and move some places to the new syntax
[citadel.git] / webcit / vcard_edit.c
1 /*
2  * $Id$
3  */
4
5 #include "webcit.h"
6
7
8 /**
9  * \brief Record compare function for sorting address book indices
10  * \param ab1 adressbook one
11  * \param ab2 adressbook two
12  */
13 int abcmp(const void *ab1, const void *ab2) {
14         return(strcasecmp(
15                 (((const addrbookent *)ab1)->ab_name),
16                 (((const addrbookent *)ab2)->ab_name)
17         ));
18 }
19
20
21 /**
22  * \brief Helper function for do_addrbook_view()
23  * Converts a name into a three-letter tab label
24  * \param tabbuf the tabbuffer to add name to
25  * \param name the name to add to the tabbuffer
26  */
27 void nametab(char *tabbuf, long len, char *name) {
28         stresc(tabbuf, len, name, 0, 0);
29         tabbuf[0] = toupper(tabbuf[0]);
30         tabbuf[1] = tolower(tabbuf[1]);
31         tabbuf[2] = tolower(tabbuf[2]);
32         tabbuf[3] = 0;
33 }
34
35
36
37 /**
38  * \brief Turn a vCard "n" (name) field into something displayable.
39  * \param name the name field to convert
40  */
41 void vcard_n_prettyize(char *name)
42 {
43         char *original_name;
44         int i, j, len;
45
46         original_name = strdup(name);
47         len = strlen(original_name);
48         for (i=0; i<5; ++i) {
49                 if (len > 0) {
50                         if (original_name[len-1] == ' ') {
51                                 original_name[--len] = 0;
52                         }
53                         if (original_name[len-1] == ';') {
54                                 original_name[--len] = 0;
55                         }
56                 }
57         }
58         strcpy(name, "");
59         j=0;
60         for (i=0; i<len; ++i) {
61                 if (original_name[i] == ';') {
62                         name[j++] = ',';
63                         name[j++] = ' ';                        
64                 }
65                 else {
66                         name[j++] = original_name[i];
67                 }
68         }
69         name[j] = '\0';
70         free(original_name);
71 }
72
73
74
75
76 /**
77  * \brief preparse a vcard name
78  * display_vcard() calls this after parsing the textual vCard into
79  * our 'struct vCard' data object.
80  * This gets called instead of display_parsed_vcard() if we are only looking
81  * to extract the person's name instead of displaying the card.
82  * \param v the vcard to retrieve the name from
83  * \param storename where to put the name at
84  */
85 void fetchname_parsed_vcard(struct vCard *v, char *storename) {
86         char *name;
87
88         strcpy(storename, "");
89
90         name = vcard_get_prop(v, "n", 1, 0, 0);
91         if (name != NULL) {
92                 strcpy(storename, name);
93                 /* vcard_n_prettyize(storename); */
94         }
95
96 }
97
98
99
100 /**
101  * \brief html print a vcard
102  * display_vcard() calls this after parsing the textual vCard into
103  * our 'struct vCard' data object.
104  *
105  * Set 'full' to nonzero to display the full card, otherwise it will only
106  * show a summary line.
107  *
108  * This code is a bit ugly, so perhaps an explanation is due: we do this
109  * in two passes through the vCard fields.  On the first pass, we process
110  * fields we understand, and then render them in a pretty fashion at the
111  * end.  Then we make a second pass, outputting all the fields we don't
112  * understand in a simple two-column name/value format.
113  * \param v the vCard to display
114  * \param full display all items of the vcard?
115  * \param msgnum Citadel message pointer
116  */
117 void display_parsed_vcard(StrBuf *Target, struct vCard *v, int full, long msgnum) {
118         int i, j;
119         char buf[SIZ];
120         char *name;
121         int is_qp = 0;
122         int is_b64 = 0;
123         char *thisname, *thisvalue;
124         char firsttoken[SIZ];
125         int pass;
126
127         char fullname[SIZ];
128         char title[SIZ];
129         char org[SIZ];
130         char phone[SIZ];
131         char mailto[SIZ];
132
133         strcpy(fullname, "");
134         strcpy(phone, "");
135         strcpy(mailto, "");
136         strcpy(title, "");
137         strcpy(org, "");
138
139         if (!full) {
140                 StrBufAppendPrintf(Target, "<TD>");
141                 name = vcard_get_prop(v, "fn", 1, 0, 0);
142                 if (name != NULL) {
143                         StrEscAppend(Target, NULL, name, 0, 0);
144                 }
145                 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
146                         strcpy(fullname, name);
147                         vcard_n_prettyize(fullname);
148                         StrEscAppend(Target, NULL, fullname, 0, 0);
149                 }
150                 else {
151                         StrBufAppendPrintf(Target, "&nbsp;");
152                 }
153                 StrBufAppendPrintf(Target, "</TD>");
154                 return;
155         }
156
157         StrBufAppendPrintf(Target, "<div align=center>"
158                 "<table bgcolor=#aaaaaa width=50%%>");
159         for (pass=1; pass<=2; ++pass) {
160
161                 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
162                         int len;
163                         thisname = strdup(v->prop[i].name);
164                         extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
165         
166                         for (j=0; j<num_tokens(thisname, ';'); ++j) {
167                                 extract_token(buf, thisname, j, ';', sizeof buf);
168                                 if (!strcasecmp(buf, "encoding=quoted-printable")) {
169                                         is_qp = 1;
170                                         remove_token(thisname, j, ';');
171                                 }
172                                 if (!strcasecmp(buf, "encoding=base64")) {
173                                         is_b64 = 1;
174                                         remove_token(thisname, j, ';');
175                                 }
176                         }
177                         
178                         len = strlen(v->prop[i].value);
179                         /* if we have some untagged QP, detect it here. */
180                         if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL))
181                                 utf8ify_rfc822_string(v->prop[i].value);
182
183                         if (is_qp) {
184                                 // %ff can become 6 bytes in utf8 
185                                 thisvalue = malloc(len * 2 + 3); 
186                                 j = CtdlDecodeQuotedPrintable(
187                                         thisvalue, v->prop[i].value,
188                                         len);
189                                 thisvalue[j] = 0;
190                         }
191                         else if (is_b64) {
192                                 // ff will become one byte..
193                                 thisvalue = malloc(len + 50);
194                                 CtdlDecodeBase64(
195                                         thisvalue, v->prop[i].value,
196                                         strlen(v->prop[i].value) );
197                         }
198                         else {
199                                 thisvalue = strdup(v->prop[i].value);
200                         }
201         
202                         /** Various fields we may encounter ***/
203         
204                         /** N is name, but only if there's no FN already there */
205                         if (!strcasecmp(firsttoken, "n")) {
206                                 if (IsEmptyStr(fullname)) {
207                                         strcpy(fullname, thisvalue);
208                                         vcard_n_prettyize(fullname);
209                                 }
210                         }
211         
212                         /** FN (full name) is a true 'display name' field */
213                         else if (!strcasecmp(firsttoken, "fn")) {
214                                 strcpy(fullname, thisvalue);
215                         }
216
217                         /** title */
218                         else if (!strcasecmp(firsttoken, "title")) {
219                                 strcpy(title, thisvalue);
220                         }
221         
222                         /** organization */
223                         else if (!strcasecmp(firsttoken, "org")) {
224                                 strcpy(org, thisvalue);
225                         }
226         
227                         else if (!strcasecmp(firsttoken, "email")) {
228                                 size_t len;
229                                 if (!IsEmptyStr(mailto)) strcat(mailto, "<br />");
230                                 strcat(mailto,
231                                         "<a href=\"display_enter"
232                                         "?force_room=_MAIL_?recp=");
233
234                                 len = strlen(mailto);
235                                 urlesc(&mailto[len], SIZ - len, "\"");
236                                 len = strlen(mailto);
237                                 urlesc(&mailto[len], SIZ - len,  fullname);
238                                 len = strlen(mailto);
239                                 urlesc(&mailto[len], SIZ - len, "\" <");
240                                 len = strlen(mailto);
241                                 urlesc(&mailto[len], SIZ - len, thisvalue);
242                                 len = strlen(mailto);
243                                 urlesc(&mailto[len], SIZ - len, ">");
244
245                                 strcat(mailto, "\">");
246                                 len = strlen(mailto);
247                                 stresc(mailto+len, SIZ - len, thisvalue, 1, 1);
248                                 strcat(mailto, "</A>");
249                         }
250                         else if (!strcasecmp(firsttoken, "tel")) {
251                                 if (!IsEmptyStr(phone)) strcat(phone, "<br />");
252                                 strcat(phone, thisvalue);
253                                 for (j=0; j<num_tokens(thisname, ';'); ++j) {
254                                         extract_token(buf, thisname, j, ';', sizeof buf);
255                                         if (!strcasecmp(buf, "tel"))
256                                                 strcat(phone, "");
257                                         else if (!strcasecmp(buf, "work"))
258                                                 strcat(phone, _(" (work)"));
259                                         else if (!strcasecmp(buf, "home"))
260                                                 strcat(phone, _(" (home)"));
261                                         else if (!strcasecmp(buf, "cell"))
262                                                 strcat(phone, _(" (cell)"));
263                                         else {
264                                                 strcat(phone, " (");
265                                                 strcat(phone, buf);
266                                                 strcat(phone, ")");
267                                         }
268                                 }
269                         }
270                         else if (!strcasecmp(firsttoken, "adr")) {
271                                 if (pass == 2) {
272                                         StrBufAppendPrintf(Target, "<TR><TD>");
273                                         StrBufAppendPrintf(Target, _("Address:"));
274                                         StrBufAppendPrintf(Target, "</TD><TD>");
275                                         for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
276                                                 extract_token(buf, thisvalue, j, ';', sizeof buf);
277                                                 if (!IsEmptyStr(buf)) {
278                                                         StrEscAppend(Target, NULL, buf, 0, 0);
279                                                         if (j<3) StrBufAppendPrintf(Target, "<br />");
280                                                         else StrBufAppendPrintf(Target, " ");
281                                                 }
282                                         }
283                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
284                                 }
285                         }
286                         /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { 
287                                 // Only output on second pass
288                                 StrBufAppendPrintf(Target, "<tr><td>");
289                                 StrBufAppendPrintf(Target, _("Photo:"));
290                                 StrBufAppendPrintf(Target, "</td><td>");
291                                 StrBufAppendPrintf(Target, "<img src=\"/vcardphoto/%ld/\" alt=\"Contact photo\"/>",msgnum);
292                                 StrBufAppendPrintf(Target, "</td></tr>\n");
293                         } */
294                         else if (!strcasecmp(firsttoken, "version")) {
295                                 /* ignore */
296                         }
297                         else if (!strcasecmp(firsttoken, "rev")) {
298                                 /* ignore */
299                         }
300                         else if (!strcasecmp(firsttoken, "label")) {
301                                 /* ignore */
302                         }
303                         else {
304
305                                 /*** Don't show extra fields.  They're ugly.
306                                 if (pass == 2) {
307                                         StrBufAppendPrintf(Target, "<TR><TD>");
308                                         StrEscAppend(Target, NULL, thisname, 0, 0);
309                                         StrBufAppendPrintf(Target, "</TD><TD>");
310                                         StrEscAppend(Target, NULL, thisvalue, 0, 0);
311                                         StrBufAppendPrintf(Target, "</TD></TR>\n");
312                                 }
313                                 ***/
314                         }
315         
316                         free(thisname);
317                         free(thisvalue);
318                 }
319         
320                 if (pass == 1) {
321                         StrBufAppendPrintf(Target, "<TR BGCOLOR=\"#AAAAAA\">"
322                         "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
323                         "<IMG ALIGN=CENTER src=\"static/viewcontacts_48x.gif\">"
324                         "<FONT SIZE=+1><B>");
325                         StrEscAppend(Target, NULL, fullname, 0, 0);
326                         StrBufAppendPrintf(Target, "</B></FONT>");
327                         if (!IsEmptyStr(title)) {
328                                 StrBufAppendPrintf(Target, "<div align=right>");
329                                 StrEscAppend(Target, NULL, title, 0, 0);
330                                 StrBufAppendPrintf(Target, "</div>");
331                         }
332                         if (!IsEmptyStr(org)) {
333                                 StrBufAppendPrintf(Target, "<div align=right>");
334                                 StrEscAppend(Target, NULL, org, 0, 0);
335                                 StrBufAppendPrintf(Target, "</div>");
336                         }
337                         StrBufAppendPrintf(Target, "</TD></TR>\n");
338                 
339                         if (!IsEmptyStr(phone)) {
340                                 StrBufAppendPrintf(Target, "<tr><td>");
341                                 StrBufAppendPrintf(Target, _("Telephone:"));
342                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", phone);
343                         }
344                         if (!IsEmptyStr(mailto)) {
345                                 StrBufAppendPrintf(Target, "<tr><td>");
346                                 StrBufAppendPrintf(Target, _("E-mail:"));
347                                 StrBufAppendPrintf(Target, "</td><td>%s</td></tr>\n", mailto);
348                         }
349                 }
350
351         }
352
353         StrBufAppendPrintf(Target, "</table></div>\n");
354 }
355
356
357
358 /**
359  * \brief  Display a textual vCard
360  * (Converts to a vCard object and then calls the actual display function)
361  * Set 'full' to nonzero to display the whole card instead of a one-liner.
362  * Or, if "storename" is non-NULL, just store the person's name in that
363  * buffer instead of displaying the card at all.
364  * \param vcard_source the buffer containing the vcard text
365  * \param alpha what???
366  * \param full should we usse all lines?
367  * \param storename where to store???
368  * \param msgnum Citadel message pointer
369  */
370 void display_vcard(StrBuf *Target, const char *vcard_source, char alpha, int full, char *storename, 
371         long msgnum) {
372         struct vCard *v;
373         char *name;
374         char buf[SIZ];
375         char this_alpha = 0;
376
377         v = vcard_load((char*)vcard_source); ///TODO
378
379         if (v == NULL) return;
380
381         name = vcard_get_prop(v, "n", 1, 0, 0);
382         if (name != NULL) {
383                 utf8ify_rfc822_string(name);
384                 strcpy(buf, name);
385                 this_alpha = buf[0];
386         }
387
388         if (storename != NULL) {
389                 fetchname_parsed_vcard(v, storename);
390         }
391         else if (       (alpha == 0)
392                         || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
393                         || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
394                 ) {
395                 display_parsed_vcard(Target, v, full,msgnum);
396         }
397
398         vcard_free(v);
399 }
400
401
402
403 /**
404  * \brief Render the address book using info we gathered during the scan
405  * \param addrbook the addressbook to render
406  * \param num_ab the number of the addressbook
407  */
408 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
409         int i = 0;
410         int displayed = 0;
411         int bg = 0;
412         static int NAMESPERPAGE = 60;
413         int num_pages = 0;
414         int tabfirst = 0;
415         char tabfirst_label[64];
416         int tablast = 0;
417         char tablast_label[64];
418         char this_tablabel[64];
419         int page = 0;
420         char **tablabels;
421
422         if (num_ab == 0) {
423                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
424                 wprintf(_("This address book is empty."));
425                 wprintf("</i></div>\n");
426                 return;
427         }
428
429         if (num_ab > 1) {
430                 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
431         }
432
433         num_pages = (num_ab / NAMESPERPAGE) + 1;
434
435         tablabels = malloc(num_pages * sizeof (char *));
436         if (tablabels == NULL) {
437                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
438                 wprintf(_("An internal error has occurred."));
439                 wprintf("</i></div>\n");
440                 return;
441         }
442
443         for (i=0; i<num_pages; ++i) {
444                 tabfirst = i * NAMESPERPAGE;
445                 tablast = tabfirst + NAMESPERPAGE - 1;
446                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
447                 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
448                 nametab(tablast_label, 64, addrbook[tablast].ab_name);
449                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
450                 tablabels[i] = strdup(this_tablabel);
451         }
452
453         tabbed_dialog(num_pages, tablabels);
454         page = (-1);
455
456         for (i=0; i<num_ab; ++i) {
457
458                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
459                         page = (i / NAMESPERPAGE);
460                         if (page > 0) {
461                                 wprintf("</tr></table>\n");
462                                 end_tab(page-1, num_pages);
463                         }
464                         begin_tab(page, num_pages);
465                         wprintf("<table border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
466                         displayed = 0;
467                 }
468
469                 if ((displayed % 4) == 0) {
470                         if (displayed > 0) {
471                                 wprintf("</tr>\n");
472                         }
473                         bg = 1 - bg;
474                         wprintf("<tr bgcolor=\"#%s\">",
475                                 (bg ? "DDDDDD" : "FFFFFF")
476                         );
477                 }
478         
479                 wprintf("<td>");
480
481                 wprintf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
482                         addrbook[i].ab_msgnum);
483                 wprintf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
484                 vcard_n_prettyize(addrbook[i].ab_name);
485                 escputs(addrbook[i].ab_name);
486                 wprintf("</a></td>\n");
487                 ++displayed;
488         }
489
490         /* Placeholders for empty columns at end */
491         if ((num_ab % 4) != 0) {
492                 for (i=0; i<(4-(num_ab % 4)); ++i) {
493                         wprintf("<td>&nbsp;</td>");
494                 }
495         }
496
497         wprintf("</tr></table>\n");
498         end_tab((num_pages-1), num_pages);
499
500         begin_tab(num_pages, num_pages);
501         /* FIXME there ought to be something here */
502         end_tab(num_pages, num_pages);
503
504         for (i=0; i<num_pages; ++i) {
505                 free(tablabels[i]);
506         }
507         free(tablabels);
508 }
509
510
511
512
513 /*
514  * Edit the vCard component of a MIME message.  
515  * Supply the message number
516  * and MIME part number to fetch.  Or, specify -1 for the message number
517  * to start with a blank card.
518  */
519 void do_edit_vcard(long msgnum, char *partnum, char *return_to, char *force_room) {
520         char buf[SIZ];
521         char *serialized_vcard = NULL;
522         size_t total_len = 0;
523         struct vCard *v;
524         int i;
525         char *key, *value;
526         char whatuser[256];
527
528         char lastname[256];
529         char firstname[256];
530         char middlename[256];
531         char prefix[256];
532         char suffix[256];
533         char pobox[256];
534         char extadr[256];
535         char street[256];
536         char city[256];
537         char state[256];
538         char zipcode[256];
539         char country[256];
540         char hometel[256];
541         char worktel[256];
542         char faxtel[256];
543         char mobiletel[256];
544         char primary_inetemail[256];
545         char other_inetemail[SIZ];
546         char extrafields[SIZ];
547         char fullname[256];
548         char title[256];
549         char org[256];
550
551         lastname[0] = 0;
552         firstname[0] = 0;
553         middlename[0] = 0;
554         prefix[0] = 0;
555         suffix[0] = 0;
556         pobox[0] = 0;
557         extadr[0] = 0;
558         street[0] = 0;
559         city[0] = 0;
560         state[0] = 0;
561         zipcode[0] = 0;
562         country[0] = 0;
563         hometel[0] = 0;
564         worktel[0] = 0;
565         faxtel[0] = 0;
566         mobiletel[0] = 0;
567         primary_inetemail[0] = 0;
568         other_inetemail[0] = 0;
569         title[0] = 0;
570         org[0] = 0;
571         extrafields[0] = 0;
572         fullname[0] = 0;
573
574         safestrncpy(whatuser, "", sizeof whatuser);
575
576         if (msgnum >= 0) {
577                 sprintf(buf, "MSG0 %ld|1", msgnum);
578                 serv_puts(buf);
579                 serv_getln(buf, sizeof buf);
580                 if (buf[0] != '1') {
581                         convenience_page("770000", _("Error"), &buf[4]);
582                         return;
583                 }
584                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
585                         if (!strncasecmp(buf, "from=", 5)) {
586                                 safestrncpy(whatuser, &buf[5], sizeof whatuser);
587                         }
588                         else if (!strncasecmp(buf, "node=", 5)) {
589                                 strcat(whatuser, " @ ");
590                                 strcat(whatuser, &buf[5]);
591                         }
592                 }
593         
594                 sprintf(buf, "DLAT %ld|%s", msgnum, partnum);
595                 serv_puts(buf);
596                 serv_getln(buf, sizeof buf);
597                 if (buf[0] != '6') {
598                         convenience_page("770000", "Error", &buf[4]);
599                         return;
600                 }
601         
602                 total_len = atoi(&buf[4]);
603                 serialized_vcard = malloc(total_len + 2);
604
605                 serv_read(serialized_vcard, total_len);
606                 serialized_vcard[total_len] = 0;
607         
608                 v = vcard_load(serialized_vcard);
609                 free(serialized_vcard);
610         
611                 /* Populate the variables for our form */
612                 i = 0;
613                 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
614                         value = vcard_get_prop(v, "", 0, i++, 0);
615         
616                         if (!strcasecmp(key, "n")) {
617                                 extract_token(lastname, value, 0, ';', sizeof lastname);
618                                 extract_token(firstname, value, 1, ';', sizeof firstname);
619                                 extract_token(middlename, value, 2, ';', sizeof middlename);
620                                 extract_token(prefix, value, 3, ';', sizeof prefix);
621                                 extract_token(suffix, value, 4, ';', sizeof suffix);
622                         }
623
624                         else if (!strcasecmp(key, "fn")) {
625                                 safestrncpy(fullname, value, sizeof fullname);
626                         }
627
628                         else if (!strcasecmp(key, "title")) {
629                                 safestrncpy(title, value, sizeof title);
630                         }
631         
632                         else if (!strcasecmp(key, "org")) {
633                                 safestrncpy(org, value, sizeof org);
634                         }
635         
636                         else if ( (!strcasecmp(key, "adr")) || (!strncasecmp(key, "adr;", 4)) ) {
637                                 extract_token(pobox, value, 0, ';', sizeof pobox);
638                                 extract_token(extadr, value, 1, ';', sizeof extadr);
639                                 extract_token(street, value, 2, ';', sizeof street);
640                                 extract_token(city, value, 3, ';', sizeof city);
641                                 extract_token(state, value, 4, ';', sizeof state);
642                                 extract_token(zipcode, value, 5, ';', sizeof zipcode);
643                                 extract_token(country, value, 6, ';', sizeof country);
644                         }
645         
646                         else if ( (!strcasecmp(key, "tel;home")) || (!strcasecmp(key, "tel;type=home")) ) {
647                                 extract_token(hometel, value, 0, ';', sizeof hometel);
648                         }
649         
650                         else if ( (!strcasecmp(key, "tel;work")) || (!strcasecmp(key, "tel;type=work")) ) {
651                                 extract_token(worktel, value, 0, ';', sizeof worktel);
652                         }
653         
654                         else if ( (!strcasecmp(key, "tel;fax")) || (!strcasecmp(key, "tel;type=fax")) ) {
655                                 extract_token(faxtel, value, 0, ';', sizeof faxtel);
656                         }
657         
658                         else if ( (!strcasecmp(key, "tel;cell")) || (!strcasecmp(key, "tel;type=cell")) ) {
659                                 extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
660                         }
661         
662                         else if ( (!strcasecmp(key, "email;internet"))
663                              || (!strcasecmp(key, "email;type=internet")) ) {
664                                 if (primary_inetemail[0] == 0) {
665                                         safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
666                                 }
667                                 else {
668                                         if (other_inetemail[0] != 0) {
669                                                 strcat(other_inetemail, "\n");
670                                         }
671                                         strcat(other_inetemail, value);
672                                 }
673                         }
674         
675                         else {
676                                 strcat(extrafields, key);
677                                 strcat(extrafields, ":");
678                                 strcat(extrafields, value);
679                                 strcat(extrafields, "\n");
680                         }
681         
682                 }
683         
684                 vcard_free(v);
685         }
686
687         /** Display the form */
688         output_headers(1, 1, 1, 0, 0, 0);
689
690         svput("BOXTITLE", WCS_STRING, _("Edit contact information"));
691         do_template("beginboxx", NULL);
692
693         wprintf("<form method=\"POST\" action=\"submit_vcard\">\n");
694         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
695
696         if (force_room != NULL) {
697                 wprintf("<input type=\"hidden\" name=\"force_room\" value=\"");
698                 escputs(force_room);
699                 wprintf("\">\n");
700         }
701
702         wprintf("<div class=\"fix_scrollbar_bug\">"
703                 "<table class=\"vcard_edit_background\"><tr><td>\n");
704
705         wprintf("<table border=0><tr>"
706                 "<td>%s</td>"
707                 "<td>%s</td>"
708                 "<td>%s</td>"
709                 "<td>%s</td>"
710                 "<td>%s</td></tr>\n",
711                 _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix")
712         );
713         wprintf("<tr><td><input type=\"text\" name=\"prefix\" "
714                 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
715                 prefix);
716         wprintf("<td><input type=\"text\" name=\"firstname\" "
717                 "value=\"%s\" maxlength=\"29\"></td>",
718                 firstname);
719         wprintf("<td><input type=\"text\" name=\"middlename\" "
720                 "value=\"%s\" maxlength=\"29\"></td>",
721                 middlename);
722         wprintf("<td><input type=\"text\" name=\"lastname\" "
723                 "value=\"%s\" maxlength=\"29\"></td>",
724                 lastname);
725         wprintf("<td><input type=\"text\" name=\"suffix\" "
726                 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
727                 suffix);
728
729         wprintf("<table  class=\"vcard_edit_background_alt\">");
730         wprintf("<tr><td>");
731
732         wprintf(_("Display name:"));
733         wprintf("<br>"
734                 "<input type=\"text\" name=\"fullname\" "
735                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
736                 fullname
737         );
738
739         wprintf(_("Title:"));
740         wprintf("<br>"
741                 "<input type=\"text\" name=\"title\" "
742                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
743                 title
744         );
745
746         wprintf(_("Organization:"));
747         wprintf("<br>"
748                 "<input type=\"text\" name=\"org\" "
749                 "value=\"%s\" maxlength=\"40\"><br><br>\n",
750                 org
751         );
752
753         wprintf("</td><td>");
754
755         wprintf("<table border=0>");
756         wprintf("<tr><td>");
757         wprintf(_("PO box:"));
758         wprintf("</td><td>"
759                 "<input type=\"text\" name=\"pobox\" "
760                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
761                 pobox);
762         wprintf("<tr><td>");
763         wprintf(_("Address:"));
764         wprintf("</td><td>"
765                 "<input type=\"text\" name=\"extadr\" "
766                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
767                 extadr);
768         wprintf("<tr><td> </td><td>"
769                 "<input type=\"text\" name=\"street\" "
770                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
771                 street);
772         wprintf("<tr><td>");
773         wprintf(_("City:"));
774         wprintf("</td><td>"
775                 "<input type=\"text\" name=\"city\" "
776                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
777                 city);
778         wprintf("<tr><td>");
779         wprintf(_("State:"));
780         wprintf("</td><td>"
781                 "<input type=\"text\" name=\"state\" "
782                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
783                 state);
784         wprintf("<tr><td>");
785         wprintf(_("ZIP code:"));
786         wprintf("</td><td>"
787                 "<input type=\"text\" name=\"zipcode\" "
788                 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
789                 zipcode);
790         wprintf("<tr><td>");
791         wprintf(_("Country:"));
792         wprintf("</td><td>"
793                 "<input type=\"text\" name=\"country\" "
794                 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
795                 country);
796         wprintf("</table>\n");
797
798         wprintf("</table>\n");
799
800         wprintf("<table border=0><tr><td>");
801         wprintf(_("Home telephone:"));
802         wprintf("</td>"
803                 "<td><input type=\"text\" name=\"hometel\" "
804                 "value=\"%s\" maxlength=\"29\"></td>\n",
805                 hometel);
806         wprintf("<td>");
807         wprintf(_("Work telephone:"));
808         wprintf("</td>"
809                 "<td><input type=\"text\" name=\"worktel\" "
810                 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
811                 worktel);
812         wprintf("<tr><td>");
813         wprintf(_("Mobile telephone:"));
814         wprintf("</td>"
815                 "<td><input type=\"text\" name=\"mobiletel\" "
816                 "value=\"%s\" maxlength=\"29\"></td>\n",
817                 mobiletel);
818         wprintf("<td>");
819         wprintf(_("Fax number:"));
820         wprintf("</td>"
821                 "<td><input type=\"text\" name=\"faxtel\" "
822                 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
823                 faxtel);
824
825         wprintf("<table class=\"vcard_edit_background_alt\">");
826         wprintf("<tr><td>");
827
828         wprintf("<table border=0><TR>"
829                 "<td valign=top>");
830         wprintf(_("Primary Internet e-mail address"));
831         wprintf("<br />"
832                 "<input type=\"text\" name=\"primary_inetemail\" "
833                 "size=40 maxlength=60 value=\"");
834         escputs(primary_inetemail);
835         wprintf("\"><br />"
836                 "</td><td valign=top>");
837         wprintf(_("Internet e-mail aliases"));
838         wprintf("<br />"
839                 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
840         escputs(other_inetemail);
841         wprintf("</textarea></td></tr></table>\n");
842
843         wprintf("</td></tr></table>\n");
844
845         wprintf("<input type=\"hidden\" name=\"extrafields\" value=\"");
846         escputs(extrafields);
847         wprintf("\">\n");
848
849         wprintf("<input type=\"hidden\" name=\"return_to\" value=\"");
850         urlescputs(return_to);
851         wprintf("\">\n");
852
853         wprintf("<div class=\"buttons\">\n"
854                 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
855                 "&nbsp;"
856                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
857                 "</div></form>\n",
858                 _("Save changes"),
859                 _("Cancel")
860         );
861         
862         wprintf("</td></tr></table>\n");
863         do_template("endbox", NULL);
864         wDumpContent(1);
865 }
866
867
868 /**
869  *  commit the edits to the citadel server
870  */
871 void edit_vcard(void) {
872         long msgnum;
873         char *partnum;
874
875         msgnum = lbstr("msgnum");
876         partnum = bstr("partnum");
877         do_edit_vcard(msgnum, partnum, "", NULL);
878 }
879
880
881
882 /**
883  *  parse edited vcard from the browser
884  */
885 void submit_vcard(void) {
886         struct vCard *v;
887         char *serialized_vcard;
888         char buf[SIZ];
889         int i;
890
891         if (!havebstr("ok_button")) { 
892                 readloop("readnew");
893                 return;
894         }
895
896         if (havebstr("force_room")) {
897                 gotoroom(bstr("force_room"));
898         }
899
900         sprintf(buf, "ENT0 1|||4||");
901         serv_puts(buf);
902         serv_getln(buf, sizeof buf);
903         if (buf[0] != '4') {
904                 edit_vcard();
905                 return;
906         }
907
908         /** Make a vCard structure out of the data supplied in the form */
909
910         snprintf(buf, sizeof buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
911                 bstr("extrafields")
912         );
913         v = vcard_load(buf);    /** Start with the extra fields */
914         if (v == NULL) {
915                 safestrncpy(WC->ImportantMessage,
916                         _("An error has occurred."),
917                         sizeof WC->ImportantMessage
918                 );
919                 edit_vcard();
920                 return;
921         }
922
923         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
924                 bstr("lastname"),
925                 bstr("firstname"),
926                 bstr("middlename"),
927                 bstr("prefix"),
928                 bstr("suffix") );
929         vcard_add_prop(v, "n", buf);
930         
931         vcard_add_prop(v, "title", bstr("title"));
932         vcard_add_prop(v, "fn", bstr("fullname"));
933         vcard_add_prop(v, "org", bstr("org"));
934
935         snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
936                 bstr("pobox"),
937                 bstr("extadr"),
938                 bstr("street"),
939                 bstr("city"),
940                 bstr("state"),
941                 bstr("zipcode"),
942                 bstr("country") );
943         vcard_add_prop(v, "adr", buf);
944
945         vcard_add_prop(v, "tel;home", bstr("hometel"));
946         vcard_add_prop(v, "tel;work", bstr("worktel"));
947         vcard_add_prop(v, "tel;fax", bstr("faxtel"));
948         vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
949         vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
950
951         for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
952                 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
953                 if (!IsEmptyStr(buf)) {
954                         vcard_add_prop(v, "email;internet", buf);
955                 }
956         }
957
958         serialized_vcard = vcard_serialize(v);
959         vcard_free(v);
960         if (serialized_vcard == NULL) {
961                 safestrncpy(WC->ImportantMessage,
962                         _("An error has occurred."),
963                         sizeof WC->ImportantMessage
964                 );
965                 edit_vcard();
966                 return;
967         }
968
969         serv_puts("Content-type: text/x-vcard; charset=UTF-8");
970         serv_puts("");
971         serv_printf("%s\r\n", serialized_vcard);
972         serv_puts("000");
973         free(serialized_vcard);
974
975         if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
976                 select_user_to_edit(NULL, NULL);
977         }
978         else if (!strcmp(bstr("return_to"), "do_welcome")) {
979                 do_welcome();
980         }
981         else {
982                 readloop("readnew");
983         }
984 }
985
986
987
988 void 
989 InitModule_VCARD
990 (void)
991 {
992         WebcitAddUrlHandler(HKEY("edit_vcard"), edit_vcard, 0);
993         WebcitAddUrlHandler(HKEY("submit_vcard"), submit_vcard, 0);
994 }
995