2 * Copyright (c) 1996-2012 by the citadel.org team
4 * This program is open source software. You can redistribute it and/or
5 * modify it under the terms of the GNU General Public License, version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
14 #include "webserver.h"
17 CtxType CTX_VCARD = CTX_NONE;
18 CtxType CTX_VCARD_TYPE = CTX_NONE;
19 long VCEnumCounter = 0;
21 typedef enum _VCStrEnum {
30 Base64BinaryAttachment,
33 typedef struct vcField vcField;
42 vcField VCStr_Ns [] = {
43 {{HKEY("last")}, FlatString, NULL, 0, {HKEY("Last Name")}},
44 {{HKEY("first")}, FlatString, NULL, 0, {HKEY("First Name")}},
45 {{HKEY("middle")}, FlatString, NULL, 0, {HKEY("Middle Name")}},
46 {{HKEY("prefix")}, FlatString, NULL, 0, {HKEY("Prefix")}},
47 {{HKEY("suffix")}, FlatString, NULL, 0, {HKEY("Suffix")}},
48 {{HKEY("")}, TerminateList, NULL, 0, {HKEY("")}}
51 vcField VCStr_Addrs [] = {
52 {{HKEY("POBox")}, Address, NULL, 0, {HKEY("PO box")}},
53 {{HKEY("address")}, Address, NULL, 0, {HKEY("Address")}},
54 {{HKEY("address2")}, Address, NULL, 0, {HKEY("")}},
55 {{HKEY("city")}, Address, NULL, 0, {HKEY("City")}},
56 {{HKEY("state")}, Address, NULL, 0, {HKEY("State")}},
57 {{HKEY("zip")}, Address, NULL, 0, {HKEY("ZIP code")}},
58 {{HKEY("country")}, Address, NULL, 0, {HKEY("Country")}},
59 {{HKEY("")}, TerminateList, NULL, 0, {HKEY("")}}
63 {{HKEY("version")}, Number, NULL, 0, {HKEY("")}},
64 {{HKEY("rev")}, Number, NULL, 0, {HKEY("")}},
65 {{HKEY("label")}, FlatString, NULL, 0, {HKEY("")}},
66 {{HKEY("uid")}, FlatString, NULL, 0, {HKEY("")}},
67 {{HKEY("n")}, StringCluster, VCStr_Ns, 0, {HKEY("")}}, /* N is name, but only if there's no FN already there */
68 {{HKEY("fn")}, FlatString, NULL, 0, {HKEY("")}}, /* FN (full name) is a true 'display name' field */
69 {{HKEY("title")}, FlatString, NULL, 0, {HKEY("Title:")}},
70 {{HKEY("org")}, FlatString, NULL, 0, {HKEY("Organization:")}},/* organization */
71 {{HKEY("email")}, EmailAddr, NULL, 0, {HKEY("E-mail:")}},
72 {{HKEY("tel")}, PhoneNumber, NULL, 0, {HKEY("Telephone:")}},
73 {{HKEY("adr")}, StringCluster, VCStr_Addrs, 0, {HKEY("Address:")}},
74 {{HKEY("photo")}, Base64BinaryAttachment, NULL, 0, {HKEY("Photo:")}},
75 {{HKEY("tel;home")}, PhoneNumber, NULL, 0, {HKEY(" (home)")}},
76 {{HKEY("tel;work")}, PhoneNumber, NULL, 0, {HKEY(" (work)")}},
77 {{HKEY("tel;fax")}, PhoneNumber, NULL, 0, {HKEY(" (fax)")}},
78 {{HKEY("tel;cell")}, PhoneNumber, NULL, 0, {HKEY(" (cell)")}},
79 {{HKEY("email;internet")}, EmailAddr, NULL, 0, {HKEY("E-mail:")}},
80 {{HKEY("")}, TerminateList, NULL, 0, {HKEY("")}}
85 {HKEY("n")}, /* N is name, but only if there's no FN already there */
86 {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
87 {HKEY("title")}, /* title */
88 {HKEY("org")}, /* organization */
103 HashList *DefineToToken = NULL;
104 HashList *VCTokenToDefine = NULL;
105 HashList *vcNames = NULL; /* todo: fill with the name strings */
108 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
110 RegisterTokenParamDefine(SKEY(name), vf->cval);
111 Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
112 Put(vcNames, LKEY(vf->cval), NewStrBufPlain(CKEY(vf->Name)), HFreeStrBuf);
114 syslog(LOG_DEBUG, "Token: %s -> %ld, %d",
121 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer)
124 StrBuf *subStr = NewStrBuf();
125 while (vf[i].STR.len > 0) {
127 vf[i].cval = (*enumCounter) ++;
128 StrBufAppendBuf(subStr, BaseStr, 0);
129 if (StrLength(subStr) > 0) {
130 StrBufAppendBufPlain(subStr, HKEY("."), 0);
132 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
134 Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
136 switch (vf[i].Type) {
141 autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1);
156 case Base64BinaryAttachment:
163 RegisterVCardToken(&vf[i], subStr, i);
169 int preeval_vcard_item(WCTemplateToken *Token)
171 WCTemplputParams TPP;
172 WCTemplputParams *TP;
174 StrBuf *Target = NULL;
176 memset(&TPP, 0, sizeof(WCTemplputParams));
179 searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
180 if (searchFieldNo >= VCEnumCounter) {
181 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
188 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
191 long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
192 HashList *vc = (HashList*) CTX(CTX_VCARD);
193 if (GetHash(vc, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
194 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
198 void tmpl_vcard_context_item(StrBuf *Target, WCTemplputParams *TP)
201 vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
202 HashList *vc = (HashList*) CTX(CTX_VCARD);
205 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
210 if (GetHash(vc, LKEY(t->cval), &vItem) && (vItem != NULL)) {
211 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 0);
214 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
215 "Doesn't have that key - did you miss to filter in advance?");
218 int preeval_vcard_name_str(WCTemplateToken *Token)
220 WCTemplputParams TPP;
221 WCTemplputParams *TP;
223 StrBuf *Target = NULL;
225 memset(&TPP, 0, sizeof(WCTemplputParams));
228 searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
229 if (searchFieldNo >= VCEnumCounter) {
230 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
237 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
240 long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
241 /* todo: get descriptive string for this vcard type */
242 if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
243 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
246 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
247 "No i18n string for this.");
252 void tmpl_vcard_context_name_str(StrBuf *Target, WCTemplputParams *TP)
255 vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
258 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
263 if (GetHash(vcNames, LKEY(t->cval), &vItem) && (vItem != NULL)) {
264 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
267 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
268 "No i18n string for this.");
273 int filter_VC_ByType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
279 vcField *vf = (vcField*) Context;
281 memcpy(&type, key, sizeof(long));
282 searchType = GetTemplateTokenNumber(Target, TP, IT_ADDT_PARAM(0), 0);
284 if (vf->Type == searchType) {
285 HashList *vc = (HashList*) CTX(CTX_VCARD);
286 if (GetHash(vc, LKEY(vf->cval), &v) && v != NULL)
295 HashList *getContextVcard(StrBuf *Target, WCTemplputParams *TP)
297 vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
298 HashList *vc = (HashList*) CTX(CTX_VCARD);
300 if ((vf == NULL) || (vc == NULL)) {
301 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
302 "Need VCard and Vcard type in context");
309 int filter_VC_ByContextType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
312 vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
314 memcpy(&searchType, key, sizeof(long));
316 if (vf->cval == searchType) {
325 int conditional_VC_Havetype(StrBuf *Target, WCTemplputParams *TP)
327 HashList *vc = (HashList*) CTX(CTX_VCARD);
328 long HaveFieldType = GetTemplateTokenNumber(Target, TP, 2, 0);
333 HashPos *it = GetNewHashPos(vc, 0);
334 while (GetNextHashPos(vc, it, &len, &Key, &vVCitem) &&
339 memcpy(&type, Key, sizeof(long));
340 if (GetHash(DefineToToken, LKEY(type), &vvcField) &&
343 vcField *t = (vcField*) vvcField;
344 if (t && t->Type == HaveFieldType) {
355 * Record compare function for sorting address book indices
357 int abcmp(const void *ab1, const void *ab2) {
359 (((const addrbookent *)ab1)->ab_name),
360 (((const addrbookent *)ab2)->ab_name)
366 * Helper function for do_addrbook_view()
367 * Converts a name into a three-letter tab label
369 void nametab(char *tabbuf, long len, char *name) {
370 stresc(tabbuf, len, name, 0, 0);
371 tabbuf[0] = toupper(tabbuf[0]);
372 tabbuf[1] = tolower(tabbuf[1]);
373 tabbuf[2] = tolower(tabbuf[2]);
379 * If it's an old "Firstname Lastname" style record, try to convert it.
381 void lastfirst_firstlast(char *namebuf) {
386 if (namebuf == NULL) return;
387 if (strchr(namebuf, ';') != NULL) return;
389 i = num_tokens(namebuf, ' ');
392 extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
393 remove_token(namebuf, i-1, ' ');
394 strcpy(firstname, namebuf);
395 sprintf(namebuf, "%s; %s", lastname, firstname);
400 wc_mime_attachment *load_vcard(message_summary *Msg)
403 StrBuf *FoundCharset = NewStrBuf();
408 wc_mime_attachment *Mime;
409 wc_mime_attachment *VCMime = NULL;
411 Msg->MsgBody = (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
412 memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
413 Msg->MsgBody->msgnum = Msg->msgnum;
415 load_message(Msg, FoundCharset, &Error);
417 FreeStrBuf(&FoundCharset);
418 /* look up the vcard... */
419 it = GetNewHashPos(Msg->AllAttach, 0);
420 while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) &&
423 Mime = (wc_mime_attachment*) vMime;
424 if ((strcmp(ChrPtr(Mime->ContentType),
425 "text/x-vcard") == 0) ||
426 (strcmp(ChrPtr(Mime->ContentType),
437 if (VCMime->Data == NULL)
438 MimeLoadData(VCMime);
443 * fetch the display name off a vCard
445 void fetch_ab_name(message_summary *Msg, char **namebuf) {
448 wc_mime_attachment *VCMime = NULL;
450 if (namebuf == NULL) return;
452 VCMime = load_vcard(Msg);
456 /* Grab the name off the card */
457 display_vcard(WC->WBuf, VCMime, 0, 0, namebuf, Msg->msgnum);
459 if (*namebuf != NULL) {
460 lastfirst_firstlast(*namebuf);
462 len = strlen(*namebuf);
463 for (i=0; i<len; ++i) {
464 if ((*namebuf)[i] != ';') return;
467 (*namebuf) = strdup(_("(no name)"));
470 (*namebuf) = strdup(_("(no name)"));
477 * Turn a vCard "n" (name) field into something displayable.
479 void vcard_n_prettyize(char *name)
484 original_name = strdup(name);
485 len = strlen(original_name);
486 for (i=0; i<5; ++i) {
488 if (original_name[len-1] == ' ') {
489 original_name[--len] = 0;
491 if (original_name[len-1] == ';') {
492 original_name[--len] = 0;
498 for (i=0; i<len; ++i) {
499 if (original_name[i] == ';') {
504 name[j++] = original_name[i];
515 * preparse a vcard name
516 * display_vcard() calls this after parsing the textual vCard into
517 * our 'struct vCard' data object.
518 * This gets called instead of display_parsed_vcard() if we are only looking
519 * to extract the person's name instead of displaying the card.
521 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
531 name = vcard_get_prop(v, "n", 1, 0, 0);
534 prop = vcard_get_prop(v, "n", 1, 0, 1);
535 n = num_tokens(prop, ';');
537 for (j=0; j<n; ++j) {
538 extract_token(buf, prop, j, ';', sizeof buf);
539 if (!strcasecmp(buf, "encoding=quoted-printable")) {
542 if (!strcasecmp(buf, "encoding=base64")) {
547 /* %ff can become 6 bytes in utf8 */
548 *storename = malloc(len * 2 + 3);
549 j = CtdlDecodeQuotedPrintable(
555 /* ff will become one byte.. */
556 *storename = malloc(len + 50);
566 *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
567 memcpy(*storename, name, len + 1);
569 /* vcard_n_prettyize(storename); */
577 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
579 /* if we have some untagged QP, detect it here. */
580 if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
582 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
588 Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
592 * display_vcard() calls this after parsing the textual vCard into
593 * our 'struct vCard' data object.
595 * Set 'full' to nonzero to display the full card, otherwise it will only
596 * show a summary line.
598 * This code is a bit ugly, so perhaps an explanation is due: we do this
599 * in two passes through the vCard fields. On the first pass, we process
600 * fields we understand, and then render them in a pretty fashion at the
601 * end. Then we make a second pass, outputting all the fields we don't
602 * understand in a simple two-column name/value format.
603 * v the vCard to display
604 * full display all items of the vcard?
605 * msgnum Citadel message pointer
607 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, int full, wc_mime_attachment *Mime)
612 char buf[20]; //SIZ];
616 StrBuf *thisname = NULL;
617 char firsttoken[20]; ///SIZ];
623 thisname = NewStrBuf();
624 thisVCToken = NewStrBufPlain(NULL, 63);
625 for (i=0; i<(v->numprops); ++i) {
626 FlushStrBuf(thisVCToken);
629 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
630 StrBufPlain(thisname, v->prop[i].name, -1);
631 StrBufLowerCase(thisname);
633 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
634 ntokens = num_tokens(ChrPtr(thisname), ';');
635 for (j=0, k=0; j < ntokens && k < 10; ++j) {
638 len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
639 if (!strcasecmp(buf, "encoding=quoted-printable")) {
641 /* remove_token(thisname, j, ';');*/
643 else if (!strcasecmp(buf, "encoding=base64")) {
645 /* remove_token(thisname, j, ';');*/
648 if (StrLength(thisVCToken) > 0) {
649 StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
651 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
653 if (GetHash(VCToEnum, buf, len, &V))
657 Put(VC, IKEY(evc), Val, HFreeStrBuf);
659 syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
668 if ((StrLength(thisVCToken) > 0) &&
669 GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) &&
671 vcField *thisField = (vcField *)vField;
672 StrBuf *ThisFieldStr = NULL;
673 syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
674 switch (thisField->Type) {
675 case StringCluster: {
677 const char *Pos = NULL;
678 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
679 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
680 while (thisField->Sub[j].STR.len > 0) {
681 StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
682 ThisFieldStr = NewStrBufDup(Buf);
684 PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
687 FreeStrBuf(&thisArray);
698 /* copy over the payload into a StrBuf */
699 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
700 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
703 case Base64BinaryAttachment:
709 /* copy over the payload into a StrBuf */
710 Val = NewStrBufPlain(v->prop[i].value, -1);
712 /* if we have some untagged QP, detect it here. */
713 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
715 StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
722 StrBufDecodeBase64(Val);
726 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
731 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
734 Put(VC, IKEY(evc), Val, HFreeStrBuf);
735 syslog(LOG_DEBUG, "[%ul]\n", evc);
739 syslog(LOG_DEBUG, "[]\n");
741 TODO: check for layer II
744 long max = num_tokens(thisname, ';');
745 firsttoken[len] = '_';
747 for (j = 0; j < max; j++) {
750 extract_token(buf, thisname, j, ';', sizeof (buf));
751 if (!strcasecmp(buf, "tel"))
753 else if (!strcasecmp(buf, "work"))
754 strcat(phone, _(" (work)"));
755 else if (!strcasecmp(buf, "home"))
756 strcat(phone, _(" (home)"));
757 else if (!strcasecmp(buf, "cell"))
758 strcat(phone, _(" (cell)"));
774 FreeStrBuf(&thisname);
776 FreeStrBuf(&thisVCToken);
779 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
781 HashList *VC = CTX(CTX_VCARD);
785 evc = GetTemplateTokenNumber(Target, TP, 0, -1);
788 if (GetHash(VC, IKEY(evc), &vStr))
790 StrBufAppendTemplate(Target, TP,
798 void display_one_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
800 HashList *VC; WCTemplputParams SubTP;
802 memset(&SubTP, 0, sizeof(WCTemplputParams));
805 VC = NewHash(0, lFlathash);
806 parse_vcard(Target, v, VC, full, Mime);
809 WCTemplputParams *TP = NULL;
810 WCTemplputParams SubTP;
811 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
813 DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
814 UnStackContext(&SubTP);
822 * Display a textual vCard
823 * (Converts to a vCard object and then calls the actual display function)
824 * Set 'full' to nonzero to display the whole card instead of a one-liner.
825 * Or, if "storename" is non-NULL, just store the person's name in that
826 * buffer instead of displaying the card at all.
828 * vcard_source the buffer containing the vcard text
829 * alpha Display only if name begins with this letter of the alphabet
830 * full Display the full vCard (otherwise just the display name)
831 * storename If not NULL, also store the display name here
832 * msgnum Citadel message pointer
834 void display_vcard(StrBuf *Target,
835 wc_mime_attachment *Mime,
847 v = VCardLoad(Mime->Data);
849 if (v == NULL) return;
851 name = vcard_get_prop(v, "n", 1, 0, 0);
853 Buf = NewStrBufPlain(name, -1);
854 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
855 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
856 this_alpha = ChrPtr(Buf)[0];
861 if (storename != NULL) {
862 fetchname_parsed_vcard(v, storename);
864 else if ((alpha == 0) ||
865 ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) ||
866 ((!isalpha(alpha)) && (!isalpha(this_alpha)))
869 display_one_vcard (Target, v, full, Mime);
878 * Render the address book using info we gathered during the scan
880 * addrbook the addressbook to render
881 * num_ab the number of the addressbook
883 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
887 static int NAMESPERPAGE = 60;
890 char tabfirst_label[64];
892 char tablast_label[64];
893 char this_tablabel[64];
898 wc_printf("<br><br><br><div align=\"center\"><i>");
899 wc_printf(_("This address book is empty."));
900 wc_printf("</i></div>\n");
905 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
908 num_pages = (num_ab / NAMESPERPAGE) + 1;
910 tablabels = malloc(num_pages * sizeof (char *));
911 if (tablabels == NULL) {
912 wc_printf("<br><br><br><div align=\"center\"><i>");
913 wc_printf(_("An internal error has occurred."));
914 wc_printf("</i></div>\n");
918 for (i=0; i<num_pages; ++i) {
919 tabfirst = i * NAMESPERPAGE;
920 tablast = tabfirst + NAMESPERPAGE - 1;
921 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
922 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
923 nametab(tablast_label, 64, addrbook[tablast].ab_name);
924 sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label);
925 tablabels[i] = strdup(this_tablabel);
928 tabbed_dialog(num_pages, tablabels);
931 for (i=0; i<num_ab; ++i) {
933 if ((i / NAMESPERPAGE) != page) { /* New tab */
934 page = (i / NAMESPERPAGE);
936 wc_printf("</tr></table>\n");
937 end_tab(page-1, num_pages);
939 begin_tab(page, num_pages);
940 wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
944 if ((displayed % 4) == 0) {
946 wc_printf("</tr>\n");
949 wc_printf("<tr bgcolor=\"#%s\">",
950 (bg ? "dddddd" : "ffffff")
956 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
957 addrbook[i].ab_msgnum);
958 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
959 vcard_n_prettyize(addrbook[i].ab_name);
960 escputs(addrbook[i].ab_name);
961 wc_printf("</a></td>\n");
965 /* Placeholders for empty columns at end */
966 if ((num_ab % 4) != 0) {
967 for (i=0; i<(4-(num_ab % 4)); ++i) {
968 wc_printf("<td> </td>");
972 wc_printf("</tr></table>\n");
973 end_tab((num_pages-1), num_pages);
975 begin_tab(num_pages, num_pages);
976 /* FIXME there ought to be something here */
977 end_tab(num_pages, num_pages);
979 for (i=0; i<num_pages; ++i) {
989 * Edit the vCard component of a MIME message.
990 * Supply the message number
991 * and MIME part number to fetch. Or, specify -1 for the message number
992 * to start with a blank card.
994 void do_edit_vcard(long msgnum, char *partnum,
995 message_summary *VCMsg,
996 wc_mime_attachment *VCAtt,
997 const char *return_to,
998 const char *force_room) {
1000 message_summary *Msg = NULL;
1001 wc_mime_attachment *VCMime = NULL;
1008 char firstname[256];
1009 char middlename[256];
1022 char mobiletel[256];
1023 char primary_inetemail[256];
1024 char other_inetemail[SIZ];
1025 char extrafields[SIZ];
1046 primary_inetemail[0] = 0;
1047 other_inetemail[0] = 0;
1053 safestrncpy(whatuser, "", sizeof whatuser);
1055 if ((msgnum >= 0) ||
1056 ((VCMsg != NULL) && (VCAtt != NULL)))
1058 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1060 Msg = (message_summary *) malloc(sizeof(message_summary));
1061 memset(Msg, 0, sizeof(message_summary));
1062 Msg->msgnum = msgnum;
1063 VCMime = load_vcard(Msg);
1064 if (VCMime == NULL) {
1065 convenience_page("770000", _("Error"), "");///TODO: important message
1066 DestroyMessageSummary(Msg);
1070 v = VCardLoad(VCMime->Data);
1073 v = VCardLoad(VCAtt->Data);
1076 /* Populate the variables for our form */
1078 while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
1079 char prp[256]; /* property name */
1080 char prm[256]; /* parameters */
1082 value = vcard_get_prop(v, "", 0, i++, 0);
1085 extract_token(prp, key, 0, ';', sizeof prp);
1086 safestrncpy(prm, key, sizeof prm);
1087 remove_token(prm, 0, ';');
1089 if (!strcasecmp(prp, "n")) {
1090 extract_token(lastname, value, 0, ';', sizeof lastname);
1091 extract_token(firstname, value, 1, ';', sizeof firstname);
1092 extract_token(middlename, value, 2, ';', sizeof middlename);
1093 extract_token(prefix, value, 3, ';', sizeof prefix);
1094 extract_token(suffix, value, 4, ';', sizeof suffix);
1097 else if (!strcasecmp(prp, "fn")) {
1098 safestrncpy(fullname, value, sizeof fullname);
1101 else if (!strcasecmp(prp, "title")) {
1102 safestrncpy(title, value, sizeof title);
1105 else if (!strcasecmp(prp, "org")) {
1106 safestrncpy(org, value, sizeof org);
1109 else if (!strcasecmp(prp, "adr")) {
1110 extract_token(pobox, value, 0, ';', sizeof pobox);
1111 extract_token(extadr, value, 1, ';', sizeof extadr);
1112 extract_token(street, value, 2, ';', sizeof street);
1113 extract_token(city, value, 3, ';', sizeof city);
1114 extract_token(state, value, 4, ';', sizeof state);
1115 extract_token(zipcode, value, 5, ';', sizeof zipcode);
1116 extract_token(country, value, 6, ';', sizeof country);
1119 else if (!strcasecmp(prp, "tel")) {
1121 if (bmstrcasestr(prm, "home")) {
1122 extract_token(hometel, value, 0, ';', sizeof hometel);
1124 else if (bmstrcasestr(prm, "work")) {
1125 extract_token(worktel, value, 0, ';', sizeof worktel);
1127 else if (bmstrcasestr(prm, "fax")) {
1128 extract_token(faxtel, value, 0, ';', sizeof faxtel);
1130 else if (bmstrcasestr(prm, "cell")) {
1131 extract_token(mobiletel, value, 0, ';', sizeof mobiletel);
1133 else { /* Missing or unknown type; put it in the home phone */
1134 extract_token(hometel, value, 0, ';', sizeof hometel);
1138 else if ( (!strcasecmp(prp, "email")) && (bmstrcasestr(prm, "internet")) ) {
1139 if (primary_inetemail[0] == 0) {
1140 safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
1143 if (other_inetemail[0] != 0) {
1144 strcat(other_inetemail, "\n");
1146 strcat(other_inetemail, value);
1150 /* Unrecognized properties are preserved here so we don't discard them
1151 * just because the vCard was edited with WebCit.
1154 strcat(extrafields, key);
1155 strcat(extrafields, ":");
1156 strcat(extrafields, value);
1157 strcat(extrafields, "\n");
1165 /* Display the form */
1166 output_headers(1, 1, 1, 0, 0, 0);
1168 do_template("box_begin_1");
1169 StrBufAppendBufPlain(WC->WBuf, _("Edit contact information"), -1, 0);
1170 do_template("box_begin_2");
1172 wc_printf("<form method=\"POST\" action=\"submit_vcard\">\n");
1173 wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
1175 if (force_room != NULL) {
1176 wc_printf("<input type=\"hidden\" name=\"force_room\" value=\"");
1177 escputs(force_room);
1182 wc_printf("<input type=\"hidden\" name=\"go\" value=\"");
1183 StrEscAppend(WCC->WBuf, WCC->CurRoom.name, NULL, 0, 0);
1187 wc_printf("<table class=\"vcard_edit_background\"><tr><td>\n");
1189 wc_printf("<table border=\"0\"><tr>"
1194 "<td>%s</td></tr>\n",
1195 _("Prefix"), _("First Name"), _("Middle Name"), _("Last Name"), _("Suffix")
1197 wc_printf("<tr><td><input type=\"text\" name=\"prefix\" "
1198 "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
1200 wc_printf("<td><input type=\"text\" name=\"firstname\" "
1201 "value=\"%s\" maxlength=\"29\"></td>",
1203 wc_printf("<td><input type=\"text\" name=\"middlename\" "
1204 "value=\"%s\" maxlength=\"29\"></td>",
1206 wc_printf("<td><input type=\"text\" name=\"lastname\" "
1207 "value=\"%s\" maxlength=\"29\"></td>",
1209 wc_printf("<td><input type=\"text\" name=\"suffix\" "
1210 "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
1213 wc_printf("<table class=\"vcard_edit_background_alt\">");
1214 wc_printf("<tr><td>");
1216 wc_printf(_("Display name:"));
1218 "<input type=\"text\" name=\"fullname\" "
1219 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1223 wc_printf(_("Title:"));
1225 "<input type=\"text\" name=\"title\" "
1226 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1230 wc_printf(_("Organization:"));
1232 "<input type=\"text\" name=\"org\" "
1233 "value=\"%s\" maxlength=\"40\"><br><br>\n",
1237 wc_printf("</td><td>");
1239 wc_printf("<table border=\"0\">");
1240 wc_printf("<tr><td>");
1241 wc_printf(_("PO box:"));
1242 wc_printf("</td><td>"
1243 "<input type=\"text\" name=\"pobox\" "
1244 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1246 wc_printf("<tr><td>");
1247 wc_printf(_("Address:"));
1248 wc_printf("</td><td>"
1249 "<input type=\"text\" name=\"extadr\" "
1250 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1252 wc_printf("<tr><td> </td><td>"
1253 "<input type=\"text\" name=\"street\" "
1254 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1256 wc_printf("<tr><td>");
1257 wc_printf(_("City:"));
1258 wc_printf("</td><td>"
1259 "<input type=\"text\" name=\"city\" "
1260 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1262 wc_printf("<tr><td>");
1263 wc_printf(_("State:"));
1264 wc_printf("</td><td>"
1265 "<input type=\"text\" name=\"state\" "
1266 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1268 wc_printf("<tr><td>");
1269 wc_printf(_("ZIP code:"));
1270 wc_printf("</td><td>"
1271 "<input type=\"text\" name=\"zipcode\" "
1272 "value=\"%s\" maxlength=\"10\"></td></tr>\n",
1274 wc_printf("<tr><td>");
1275 wc_printf(_("Country:"));
1276 wc_printf("</td><td>"
1277 "<input type=\"text\" name=\"country\" "
1278 "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
1280 wc_printf("</table>\n");
1282 wc_printf("</table>\n");
1284 wc_printf("<table border=0><tr><td>");
1285 wc_printf(_("Home telephone:"));
1287 "<td><input type=\"text\" name=\"hometel\" "
1288 "value=\"%s\" maxlength=\"29\"></td>\n",
1291 wc_printf(_("Work telephone:"));
1293 "<td><input type=\"text\" name=\"worktel\" "
1294 "value=\"%s\" maxlength=\"29\"></td></tr>\n",
1296 wc_printf("<tr><td>");
1297 wc_printf(_("Mobile telephone:"));
1299 "<td><input type=\"text\" name=\"mobiletel\" "
1300 "value=\"%s\" maxlength=\"29\"></td>\n",
1303 wc_printf(_("Fax number:"));
1305 "<td><input type=\"text\" name=\"faxtel\" "
1306 "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
1309 wc_printf("<table class=\"vcard_edit_background_alt\">");
1310 wc_printf("<tr><td>");
1312 wc_printf("<table border=0><TR>"
1314 wc_printf(_("Primary Internet e-mail address"));
1316 "<input type=\"text\" name=\"primary_inetemail\" "
1317 "size=40 maxlength=60 value=\"");
1318 escputs(primary_inetemail);
1320 "</td><td valign=top>");
1321 wc_printf(_("Internet e-mail aliases"));
1323 "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
1324 escputs(other_inetemail);
1325 wc_printf("</textarea></td></tr></table>\n");
1327 wc_printf("</td></tr></table>\n");
1329 wc_printf("<input type=\"hidden\" name=\"extrafields\" value=\"");
1330 escputs(extrafields);
1333 wc_printf("<input type=\"hidden\" name=\"return_to\" value=\"");
1337 wc_printf("<div class=\"buttons\">\n"
1338 "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
1340 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
1346 wc_printf("</td></tr></table>\n");
1347 do_template("box_end");
1350 DestroyMessageSummary(Msg);
1356 * commit the edits to the citadel server
1358 void edit_vcard(void) {
1362 msgnum = lbstr("msgnum");
1363 partnum = bstr("partnum");
1364 do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1370 * parse edited vcard from the browser
1372 void submit_vcard(void) {
1374 char *serialized_vcard;
1377 const StrBuf *ForceRoom;
1380 if (!havebstr("ok_button")) {
1381 readloop(readnew, eUseDefault);
1385 if (havebstr("force_room")) {
1386 ForceRoom = sbstr("force_room");
1387 if (gotoroom(ForceRoom) != 200) {
1388 AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1389 AppendImportantMessage(HKEY(": "));
1390 AppendImportantMessage(SKEY(ForceRoom));
1391 AppendImportantMessage(HKEY("; "));
1392 AppendImportantMessage(_("Aborting."), -1);
1394 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1395 select_user_to_edit(NULL);
1397 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1400 else if (!IsEmptyStr(bstr("return_to"))) {
1401 http_redirect(bstr("return_to"));
1404 readloop(readnew, eUseDefault);
1411 serv_write(HKEY("ENT0 1|||4\n"));
1412 if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1418 /* Make a vCard structure out of the data supplied in the form */
1419 StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1422 v = VCardLoad(Buf); /* Start with the extra fields */
1424 AppendImportantMessage(_("An error has occurred."), -1);
1430 snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1436 vcard_add_prop(v, "n", buf);
1438 vcard_add_prop(v, "title", bstr("title"));
1439 vcard_add_prop(v, "fn", bstr("fullname"));
1440 vcard_add_prop(v, "org", bstr("org"));
1442 snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1450 vcard_add_prop(v, "adr", buf);
1452 vcard_add_prop(v, "tel;home", bstr("hometel"));
1453 vcard_add_prop(v, "tel;work", bstr("worktel"));
1454 vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1455 vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1456 vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1458 for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1459 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1460 if (!IsEmptyStr(buf)) {
1461 vcard_add_prop(v, "email;internet", buf);
1465 serialized_vcard = vcard_serialize(v);
1467 if (serialized_vcard == NULL) {
1468 AppendImportantMessage(_("An error has occurred."), -1);
1474 serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1475 serv_write(HKEY("\n"));
1476 serv_printf("%s\r\n", serialized_vcard);
1477 serv_write(HKEY("000\n"));
1478 free(serialized_vcard);
1480 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1481 select_user_to_edit(NULL);
1483 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1486 else if (!IsEmptyStr(bstr("return_to"))) {
1487 http_redirect(bstr("return_to"));
1490 readloop(readnew, eUseDefault);
1498 * Extract an embedded photo from a vCard for display on the client
1500 void display_vcard_photo_img(void)
1506 const char *contentType;
1507 wcsession *WCC = WC;
1509 msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1511 vcard = load_mimepart(msgnum,"1");
1512 v = VCardLoad(vcard);
1514 photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1515 FlushStrBuf(WCC->WBuf);
1516 StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1517 if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1518 FlushStrBuf(WCC->WBuf);
1520 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1521 output_headers(0, 0, 0, 0, 0, 0);
1522 hprintf("Content-Type: text/plain\r\n");
1524 wc_printf(_("Could Not decode vcard photo\n"));
1528 contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1529 http_transmit_thing(contentType, 0);
1534 typedef struct _vcardview_struct {
1536 addrbookent *addrbook;
1541 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat,
1542 void **ViewSpecific,
1549 vcardview_struct *VS;
1551 VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1552 memset(VS, 0, sizeof(vcardview_struct));
1553 *ViewSpecific = (void*)VS;
1555 VS->is_singlecard = ibstr("is_singlecard");
1556 if (VS->is_singlecard != 1) {
1557 if (oper == do_search) {
1558 snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1561 strcpy(cmd, "MSGS ALL");
1563 Stat->maxmsgs = 9999999;
1568 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat,
1569 void **ViewSpecific,
1570 message_summary* Msg,
1574 vcardview_struct *VS;
1577 VS = (vcardview_struct*) *ViewSpecific;
1580 fetch_ab_name(Msg, &ab_name);
1581 if (ab_name == NULL)
1584 VS->addrbook = realloc(VS->addrbook,
1585 (sizeof(addrbookent) * VS->num_ab) );
1586 safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1587 sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1588 VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1594 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1597 vcardview_struct *VS;
1599 VS = (vcardview_struct*) *ViewSpecific;
1600 if (VS->is_singlecard)
1601 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1603 do_addrbook_view(VS->addrbook, VS->num_ab); /* Render the address book */
1607 int vcard_Cleanup(void **ViewSpecific)
1609 vcardview_struct *VS;
1611 VS = (vcardview_struct*) *ViewSpecific;
1614 (VS->addrbook != NULL))
1622 ServerStartModule_VCARD
1625 ///VCToEnum = NewHash(0, NULL);
1630 ServerShutdownModule_VCARD
1633 DeleteHash(&DefineToToken);
1634 DeleteHash(&vcNames);
1635 DeleteHash(&VCTokenToDefine);
1636 /// DeleteHash(&VCToEnum);
1643 RegisterCTX(CTX_VCARD);
1644 RegisterCTX(CTX_VCARD_TYPE);
1645 RegisterReadLoopHandlerset(
1647 vcard_GetParamsGetServerCall,
1651 vcard_LoadMsgFromServer,
1652 vcard_RenderView_or_Tail,
1654 WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1655 WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1656 WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1658 Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1659 Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1660 Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1661 Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1662 Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1663 Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1664 Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1665 Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1666 Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1667 Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1668 Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1669 Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1670 Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1671 Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1674 RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1676 REGISTERTokenParamDefine(VC_n);
1677 REGISTERTokenParamDefine(VC_fn);
1678 REGISTERTokenParamDefine(VC_title);
1679 REGISTERTokenParamDefine(VC_org);
1680 REGISTERTokenParamDefine(VC_email);
1681 REGISTERTokenParamDefine(VC_tel);
1682 REGISTERTokenParamDefine(VC_work);
1683 REGISTERTokenParamDefine(VC_home);
1684 REGISTERTokenParamDefine(VC_cell);
1685 REGISTERTokenParamDefine(VC_adr);
1686 REGISTERTokenParamDefine(VC_photo);
1687 REGISTERTokenParamDefine(VC_version);
1688 REGISTERTokenParamDefine(VC_rev);
1689 REGISTERTokenParamDefine(VC_label);
1693 StrBuf *Prefix = NewStrBufPlain(HKEY("VC:"));
1694 DefineToToken = NewHash(1, lFlathash);
1695 vcNames = NewHash(1, lFlathash);
1696 VCTokenToDefine = NewHash(1, NULL);
1697 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0);
1698 FreeStrBuf(&Prefix);
1700 RegisterCTX(CTX_VCARD);
1701 RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1702 RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1703 RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1704 RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1705 REGISTERTokenParamDefine(FlatString);
1706 REGISTERTokenParamDefine(StringCluster);
1707 REGISTERTokenParamDefine(PhoneNumber);
1708 REGISTERTokenParamDefine(EmailAddr);
1709 REGISTERTokenParamDefine(Street);
1710 REGISTERTokenParamDefine(Number);
1711 REGISTERTokenParamDefine(AliasFor);
1712 REGISTERTokenParamDefine(Base64BinaryAttachment);
1713 REGISTERTokenParamDefine(TerminateList);
1714 REGISTERTokenParamDefine(Address);
1716 RegisterConditional("VC:HAVE:TYPE", 1, conditional_VC_Havetype, CTX_VCARD);
1717 RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1718 RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);