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 parse
604 * msgnum Citadel message pointer
606 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
611 char buf[20]; //SIZ];
615 StrBuf *thisname = NULL;
616 char firsttoken[20]; ///SIZ];
622 thisname = NewStrBuf();
623 thisVCToken = NewStrBufPlain(NULL, 63);
624 for (i=0; i<(v->numprops); ++i) {
625 FlushStrBuf(thisVCToken);
628 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
629 StrBufPlain(thisname, v->prop[i].name, -1);
630 StrBufLowerCase(thisname);
632 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
633 ntokens = num_tokens(ChrPtr(thisname), ';');
634 for (j=0, k=0; j < ntokens && k < 10; ++j) {
637 len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
638 if (!strcasecmp(buf, "encoding=quoted-printable")) {
640 /* remove_token(thisname, j, ';');*/
642 else if (!strcasecmp(buf, "encoding=base64")) {
644 /* remove_token(thisname, j, ';');*/
647 if (StrLength(thisVCToken) > 0) {
648 StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
650 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
652 if (GetHash(VCToEnum, buf, len, &V))
656 Put(VC, IKEY(evc), Val, HFreeStrBuf);
658 syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
667 if ((StrLength(thisVCToken) > 0) &&
668 GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) &&
670 vcField *thisField = (vcField *)vField;
671 StrBuf *ThisFieldStr = NULL;
672 syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
673 switch (thisField->Type) {
674 case StringCluster: {
676 const char *Pos = NULL;
677 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
678 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
679 while (thisField->Sub[j].STR.len > 0) {
680 StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
681 ThisFieldStr = NewStrBufDup(Buf);
683 PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
686 FreeStrBuf(&thisArray);
697 /* copy over the payload into a StrBuf */
698 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
699 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
702 case Base64BinaryAttachment:
708 /* copy over the payload into a StrBuf */
709 Val = NewStrBufPlain(v->prop[i].value, -1);
711 /* if we have some untagged QP, detect it here. */
712 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
714 StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
721 StrBufDecodeBase64(Val);
725 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
730 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
733 Put(VC, IKEY(evc), Val, HFreeStrBuf);
734 syslog(LOG_DEBUG, "[%ul]\n", evc);
738 syslog(LOG_DEBUG, "[]\n");
740 TODO: check for layer II
743 long max = num_tokens(thisname, ';');
744 firsttoken[len] = '_';
746 for (j = 0; j < max; j++) {
749 extract_token(buf, thisname, j, ';', sizeof (buf));
750 if (!strcasecmp(buf, "tel"))
752 else if (!strcasecmp(buf, "work"))
753 strcat(phone, _(" (work)"));
754 else if (!strcasecmp(buf, "home"))
755 strcat(phone, _(" (home)"));
756 else if (!strcasecmp(buf, "cell"))
757 strcat(phone, _(" (cell)"));
771 FreeStrBuf(&thisname);
773 FreeStrBuf(&thisVCToken);
776 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
778 HashList *VC = CTX(CTX_VCARD);
782 evc = GetTemplateTokenNumber(Target, TP, 0, -1);
785 if (GetHash(VC, IKEY(evc), &vStr))
787 StrBufAppendTemplate(Target, TP,
795 void display_one_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
797 HashList *VC; WCTemplputParams SubTP;
799 memset(&SubTP, 0, sizeof(WCTemplputParams));
802 VC = NewHash(0, lFlathash);
803 parse_vcard(Target, v, VC, Mime);
806 WCTemplputParams *TP = NULL;
807 WCTemplputParams SubTP;
808 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
810 DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
811 UnStackContext(&SubTP);
819 * Display a textual vCard
820 * (Converts to a vCard object and then calls the actual display function)
821 * Set 'full' to nonzero to display the whole card instead of a one-liner.
822 * Or, if "storename" is non-NULL, just store the person's name in that
823 * buffer instead of displaying the card at all.
825 * vcard_source the buffer containing the vcard text
826 * alpha Display only if name begins with this letter of the alphabet
827 * full Display the full vCard (otherwise just the display name)
828 * storename If not NULL, also store the display name here
829 * msgnum Citadel message pointer
831 void display_vcard(StrBuf *Target,
832 wc_mime_attachment *Mime,
844 v = VCardLoad(Mime->Data);
846 if (v == NULL) return;
848 name = vcard_get_prop(v, "n", 1, 0, 0);
850 Buf = NewStrBufPlain(name, -1);
851 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
852 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
853 this_alpha = ChrPtr(Buf)[0];
858 if (storename != NULL) {
859 fetchname_parsed_vcard(v, storename);
861 else if ((alpha == 0) ||
862 ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) ||
863 ((!isalpha(alpha)) && (!isalpha(this_alpha)))
866 display_one_vcard (Target, v, full, Mime);
875 * Render the address book using info we gathered during the scan
877 * addrbook the addressbook to render
878 * num_ab the number of the addressbook
880 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
884 static int NAMESPERPAGE = 60;
887 char tabfirst_label[64];
889 char tablast_label[64];
890 char this_tablabel[64];
895 wc_printf("<br><br><br><div align=\"center\"><i>");
896 wc_printf(_("This address book is empty."));
897 wc_printf("</i></div>\n");
902 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
905 num_pages = (num_ab / NAMESPERPAGE) + 1;
907 tablabels = malloc(num_pages * sizeof (char *));
908 if (tablabels == NULL) {
909 wc_printf("<br><br><br><div align=\"center\"><i>");
910 wc_printf(_("An internal error has occurred."));
911 wc_printf("</i></div>\n");
915 for (i=0; i<num_pages; ++i) {
916 tabfirst = i * NAMESPERPAGE;
917 tablast = tabfirst + NAMESPERPAGE - 1;
918 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
919 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
920 nametab(tablast_label, 64, addrbook[tablast].ab_name);
921 sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label);
922 tablabels[i] = strdup(this_tablabel);
925 tabbed_dialog(num_pages, tablabels);
928 for (i=0; i<num_ab; ++i) {
930 if ((i / NAMESPERPAGE) != page) { /* New tab */
931 page = (i / NAMESPERPAGE);
933 wc_printf("</tr></table>\n");
934 end_tab(page-1, num_pages);
936 begin_tab(page, num_pages);
937 wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
941 if ((displayed % 4) == 0) {
943 wc_printf("</tr>\n");
946 wc_printf("<tr bgcolor=\"#%s\">",
947 (bg ? "dddddd" : "ffffff")
953 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
954 addrbook[i].ab_msgnum);
955 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
956 vcard_n_prettyize(addrbook[i].ab_name);
957 escputs(addrbook[i].ab_name);
958 wc_printf("</a></td>\n");
962 /* Placeholders for empty columns at end */
963 if ((num_ab % 4) != 0) {
964 for (i=0; i<(4-(num_ab % 4)); ++i) {
965 wc_printf("<td> </td>");
969 wc_printf("</tr></table>\n");
970 end_tab((num_pages-1), num_pages);
972 begin_tab(num_pages, num_pages);
973 /* FIXME there ought to be something here */
974 end_tab(num_pages, num_pages);
976 for (i=0; i<num_pages; ++i) {
986 * Edit the vCard component of a MIME message.
987 * Supply the message number
988 * and MIME part number to fetch. Or, specify -1 for the message number
989 * to start with a blank card.
991 void do_edit_vcard(long msgnum, char *partnum,
992 message_summary *VCMsg,
993 wc_mime_attachment *VCAtt,
994 const char *return_to,
995 const char *force_room) {
996 HashList *VC; WCTemplputParams SubTP;
998 message_summary *Msg = NULL;
999 wc_mime_attachment *VCMime = NULL;
1002 VC = NewHash(0, lFlathash);
1004 /* Display the form */
1005 output_headers(1, 1, 1, 0, 0, 0);
1007 safestrncpy(whatuser, "", sizeof whatuser);
1009 if ((msgnum >= 0) ||
1010 ((VCMsg != NULL) && (VCAtt != NULL)))
1012 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1014 Msg = (message_summary *) malloc(sizeof(message_summary));
1015 memset(Msg, 0, sizeof(message_summary));
1016 Msg->msgnum = msgnum;
1017 VCMime = load_vcard(Msg);
1018 if (VCMime == NULL) {
1019 convenience_page("770000", _("Error"), "");///TODO: important message
1020 DestroyMessageSummary(Msg);
1024 v = VCardLoad(VCMime->Data);
1027 v = VCardLoad(VCAtt->Data);
1030 parse_vcard(WCC->WBuf, v, VC, NULL);
1036 memset(&SubTP, 0, sizeof(WCTemplputParams));
1039 WCTemplputParams *TP = NULL;
1040 WCTemplputParams SubTP;
1041 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
1043 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
1044 UnStackContext(&SubTP);
1051 DestroyMessageSummary(Msg);
1057 * commit the edits to the citadel server
1059 void edit_vcard(void) {
1063 msgnum = lbstr("msgnum");
1064 partnum = bstr("partnum");
1065 do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1071 * parse edited vcard from the browser
1073 void submit_vcard(void) {
1075 char *serialized_vcard;
1078 const StrBuf *ForceRoom;
1081 if (!havebstr("ok_button")) {
1082 readloop(readnew, eUseDefault);
1086 if (havebstr("force_room")) {
1087 ForceRoom = sbstr("force_room");
1088 if (gotoroom(ForceRoom) != 200) {
1089 AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1090 AppendImportantMessage(HKEY(": "));
1091 AppendImportantMessage(SKEY(ForceRoom));
1092 AppendImportantMessage(HKEY("; "));
1093 AppendImportantMessage(_("Aborting."), -1);
1095 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1096 select_user_to_edit(NULL);
1098 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1101 else if (!IsEmptyStr(bstr("return_to"))) {
1102 http_redirect(bstr("return_to"));
1105 readloop(readnew, eUseDefault);
1112 serv_write(HKEY("ENT0 1|||4\n"));
1113 if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1119 /* Make a vCard structure out of the data supplied in the form */
1120 StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1123 v = VCardLoad(Buf); /* Start with the extra fields */
1125 AppendImportantMessage(_("An error has occurred."), -1);
1131 snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s",
1137 vcard_add_prop(v, "n", buf);
1139 vcard_add_prop(v, "title", bstr("title"));
1140 vcard_add_prop(v, "fn", bstr("fullname"));
1141 vcard_add_prop(v, "org", bstr("org"));
1143 snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s",
1151 vcard_add_prop(v, "adr", buf);
1153 vcard_add_prop(v, "tel;home", bstr("hometel"));
1154 vcard_add_prop(v, "tel;work", bstr("worktel"));
1155 vcard_add_prop(v, "tel;fax", bstr("faxtel"));
1156 vcard_add_prop(v, "tel;cell", bstr("mobiletel"));
1157 vcard_add_prop(v, "email;internet", bstr("primary_inetemail"));
1159 for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
1160 extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
1161 if (!IsEmptyStr(buf)) {
1162 vcard_add_prop(v, "email;internet", buf);
1166 serialized_vcard = vcard_serialize(v);
1168 if (serialized_vcard == NULL) {
1169 AppendImportantMessage(_("An error has occurred."), -1);
1175 serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1176 serv_write(HKEY("\n"));
1177 serv_printf("%s\r\n", serialized_vcard);
1178 serv_write(HKEY("000\n"));
1179 free(serialized_vcard);
1181 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1182 select_user_to_edit(NULL);
1184 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1187 else if (!IsEmptyStr(bstr("return_to"))) {
1188 http_redirect(bstr("return_to"));
1191 readloop(readnew, eUseDefault);
1199 * Extract an embedded photo from a vCard for display on the client
1201 void display_vcard_photo_img(void)
1207 const char *contentType;
1208 wcsession *WCC = WC;
1210 msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1212 vcard = load_mimepart(msgnum,"1");
1213 v = VCardLoad(vcard);
1215 photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1216 FlushStrBuf(WCC->WBuf);
1217 StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1218 if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1219 FlushStrBuf(WCC->WBuf);
1221 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1222 output_headers(0, 0, 0, 0, 0, 0);
1223 hprintf("Content-Type: text/plain\r\n");
1225 wc_printf(_("Could Not decode vcard photo\n"));
1229 contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1230 http_transmit_thing(contentType, 0);
1235 typedef struct _vcardview_struct {
1237 addrbookent *addrbook;
1242 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat,
1243 void **ViewSpecific,
1250 vcardview_struct *VS;
1252 VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1253 memset(VS, 0, sizeof(vcardview_struct));
1254 *ViewSpecific = (void*)VS;
1256 VS->is_singlecard = ibstr("is_singlecard");
1257 if (VS->is_singlecard != 1) {
1258 if (oper == do_search) {
1259 snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1262 strcpy(cmd, "MSGS ALL");
1264 Stat->maxmsgs = 9999999;
1269 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat,
1270 void **ViewSpecific,
1271 message_summary* Msg,
1275 vcardview_struct *VS;
1278 VS = (vcardview_struct*) *ViewSpecific;
1281 fetch_ab_name(Msg, &ab_name);
1282 if (ab_name == NULL)
1285 VS->addrbook = realloc(VS->addrbook,
1286 (sizeof(addrbookent) * VS->num_ab) );
1287 safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1288 sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1289 VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1295 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1298 vcardview_struct *VS;
1300 VS = (vcardview_struct*) *ViewSpecific;
1301 if (VS->is_singlecard)
1302 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1304 do_addrbook_view(VS->addrbook, VS->num_ab); /* Render the address book */
1308 int vcard_Cleanup(void **ViewSpecific)
1310 vcardview_struct *VS;
1312 VS = (vcardview_struct*) *ViewSpecific;
1315 (VS->addrbook != NULL))
1323 ServerStartModule_VCARD
1326 ///VCToEnum = NewHash(0, NULL);
1331 ServerShutdownModule_VCARD
1334 DeleteHash(&DefineToToken);
1335 DeleteHash(&vcNames);
1336 DeleteHash(&VCTokenToDefine);
1337 /// DeleteHash(&VCToEnum);
1344 RegisterCTX(CTX_VCARD);
1345 RegisterCTX(CTX_VCARD_TYPE);
1346 RegisterReadLoopHandlerset(
1348 vcard_GetParamsGetServerCall,
1352 vcard_LoadMsgFromServer,
1353 vcard_RenderView_or_Tail,
1355 WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1356 WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1357 WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1359 Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1360 Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1361 Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1362 Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1363 Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1364 Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1365 Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1366 Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1367 Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1368 Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1369 Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1370 Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1371 Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1372 Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1375 RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1377 REGISTERTokenParamDefine(VC_n);
1378 REGISTERTokenParamDefine(VC_fn);
1379 REGISTERTokenParamDefine(VC_title);
1380 REGISTERTokenParamDefine(VC_org);
1381 REGISTERTokenParamDefine(VC_email);
1382 REGISTERTokenParamDefine(VC_tel);
1383 REGISTERTokenParamDefine(VC_work);
1384 REGISTERTokenParamDefine(VC_home);
1385 REGISTERTokenParamDefine(VC_cell);
1386 REGISTERTokenParamDefine(VC_adr);
1387 REGISTERTokenParamDefine(VC_photo);
1388 REGISTERTokenParamDefine(VC_version);
1389 REGISTERTokenParamDefine(VC_rev);
1390 REGISTERTokenParamDefine(VC_label);
1394 StrBuf *Prefix = NewStrBufPlain(HKEY("VC:"));
1395 DefineToToken = NewHash(1, lFlathash);
1396 vcNames = NewHash(1, lFlathash);
1397 VCTokenToDefine = NewHash(1, NULL);
1398 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0);
1399 FreeStrBuf(&Prefix);
1401 RegisterCTX(CTX_VCARD);
1402 RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1403 RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1404 RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1405 RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1406 REGISTERTokenParamDefine(FlatString);
1407 REGISTERTokenParamDefine(StringCluster);
1408 REGISTERTokenParamDefine(PhoneNumber);
1409 REGISTERTokenParamDefine(EmailAddr);
1410 REGISTERTokenParamDefine(Street);
1411 REGISTERTokenParamDefine(Number);
1412 REGISTERTokenParamDefine(AliasFor);
1413 REGISTERTokenParamDefine(Base64BinaryAttachment);
1414 REGISTERTokenParamDefine(TerminateList);
1415 REGISTERTokenParamDefine(Address);
1417 RegisterConditional("VC:HAVE:TYPE", 1, conditional_VC_Havetype, CTX_VCARD);
1418 RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1419 RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);