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,
34 typedef struct vcField vcField;
44 vcField VCStr_Ns [] = {
45 {{HKEY("last")}, FlatString, NULL, 0, 0, {HKEY("Last Name")}},
46 {{HKEY("first")}, FlatString, NULL, 0, 0, {HKEY("First Name")}},
47 {{HKEY("middle")}, FlatString, NULL, 0, 0, {HKEY("Middle Name")}},
48 {{HKEY("prefix")}, FlatString, NULL, 0, 0, {HKEY("Prefix")}},
49 {{HKEY("suffix")}, FlatString, NULL, 0, 0, {HKEY("Suffix")}},
50 {{HKEY("")}, TerminateList, NULL, 0, 0, {HKEY("")}}
53 vcField VCStr_Addrs [] = {
54 {{HKEY("POBox")}, Address, NULL, 0, 0, {HKEY("PO box")}},
55 {{HKEY("extadr")}, Address, NULL, 0, 0, {HKEY("Address")}},
56 {{HKEY("street")}, Address, NULL, 0, 0, {HKEY("")}},
57 {{HKEY("city")}, Address, NULL, 0, 0, {HKEY("City")}},
58 {{HKEY("state")}, Address, NULL, 0, 0, {HKEY("State")}},
59 {{HKEY("zip")}, Address, NULL, 0, 0, {HKEY("ZIP code")}},
60 {{HKEY("country")}, Address, NULL, 0, 0, {HKEY("Country")}},
61 {{HKEY("")}, TerminateList, NULL, 0, 0, {HKEY("")}}
65 {{HKEY("version")}, Number, NULL, 0, 0, {HKEY("")}},
66 {{HKEY("rev")}, Number, NULL, 0, 0, {HKEY("")}},
67 {{HKEY("label")}, FlatString, NULL, 0, 0, {HKEY("")}},
68 {{HKEY("uid")}, FlatString, NULL, 0, 0, {HKEY("")}},
69 {{HKEY("n")}, StringCluster, VCStr_Ns, 0, 0, {HKEY("")}}, /* N is name, but only if there's no FN already there */
70 {{HKEY("fn")}, FlatString, NULL, 0, 0, {HKEY("")}}, /* FN (full name) is a true 'display name' field */
71 {{HKEY("title")}, FlatString, NULL, 0, 0, {HKEY("Title:")}},
72 {{HKEY("org")}, FlatString, NULL, 0, 0, {HKEY("Organization:")}},/* organization */
73 {{HKEY("email")}, EmailAddr, NULL, 0, 0, {HKEY("E-mail:")}},
74 {{HKEY("tel")}, PhoneNumber, NULL, 0, 0, {HKEY("Telephone:")}},
75 {{HKEY("adr")}, StringCluster, VCStr_Addrs, 0, 0, {HKEY("Address:")}},
76 {{HKEY("photo")}, Base64BinaryAttachment, NULL, 0, 0, {HKEY("Photo:")}},
77 {{HKEY("tel;home")}, PhoneNumber, NULL, 0, 0, {HKEY(" (home)")}},
78 {{HKEY("tel;work")}, PhoneNumber, NULL, 0, 0, {HKEY(" (work)")}},
79 {{HKEY("tel;fax")}, PhoneNumber, NULL, 0, 0, {HKEY(" (fax)")}},
80 {{HKEY("tel;cell")}, PhoneNumber, NULL, 0, 0, {HKEY(" (cell)")}},
81 {{HKEY("email;internet")}, EmailAddr, NULL, 0, 0, {HKEY("E-mail:")}},
82 {{HKEY("UNKNOWN")}, UnKnown, NULL, 0, 0, {HKEY("")}},
83 {{HKEY("")}, TerminateList, NULL, 0, 0, {HKEY("")}}
88 {HKEY("n")}, /* N is name, but only if there's no FN already there */
89 {HKEY("fn")}, /* FN (full name) is a true 'display name' field */
90 {HKEY("title")}, /* title */
91 {HKEY("org")}, /* organization */
106 HashList *DefineToToken = NULL;
107 HashList *VCTokenToDefine = NULL;
108 HashList *vcNames = NULL; /* todo: fill with the name strings */
109 vcField* vcfUnknown = NULL;
111 void RegisterVCardToken(vcField* vf, StrBuf *name, int inTokenCount)
113 if (vf->Type == UnKnown) {
116 RegisterTokenParamDefine(SKEY(name), vf->cval);
117 Put(DefineToToken, LKEY(vf->cval), vf, reference_free_handler);
118 Put(vcNames, LKEY(vf->cval), NewStrBufPlain(CKEY(vf->Name)), HFreeStrBuf);
120 syslog(LOG_DEBUG, "Token: %s -> %ld, %d",
127 void autoRegisterTokens(long *enumCounter, vcField* vf, StrBuf *BaseStr, int layer, long parentCVal)
130 StrBuf *subStr = NewStrBuf();
131 while (vf[i].STR.len > 0) {
133 vf[i].cval = (*enumCounter) ++;
134 vf[i].parentCVal = parentCVal;
135 StrBufAppendBuf(subStr, BaseStr, 0);
136 if (StrLength(subStr) > 0) {
137 StrBufAppendBufPlain(subStr, HKEY("."), 0);
139 StrBufAppendBufPlain(subStr, CKEY(vf[i].STR), 0);
141 Put(VCTokenToDefine, CKEY(vf[i].STR), &vf[i], reference_free_handler);
143 switch (vf[i].Type) {
148 autoRegisterTokens(enumCounter, vf[i].Sub, subStr, 1, vf[i].cval);
161 case Base64BinaryAttachment:
170 RegisterVCardToken(&vf[i], subStr, i);
176 int preeval_vcard_item(WCTemplateToken *Token)
178 WCTemplputParams TPP;
179 WCTemplputParams *TP;
181 StrBuf *Target = NULL;
183 memset(&TPP, 0, sizeof(WCTemplputParams));
186 searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
187 if (searchFieldNo >= VCEnumCounter) {
188 LogTemplateError(NULL, "VCardItem", ERR_PARM1, TP,
195 void tmpl_vcard_item(StrBuf *Target, WCTemplputParams *TP)
198 long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
199 HashList *vc = (HashList*) CTX(CTX_VCARD);
200 if (GetHash(vc, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
201 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
205 void tmpl_vcard_context_item(StrBuf *Target, WCTemplputParams *TP)
208 vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
209 HashList *vc = (HashList*) CTX(CTX_VCARD);
212 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
217 if (GetHash(vc, LKEY(t->cval), &vItem) && (vItem != NULL)) {
218 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 0);
221 LogTemplateError(NULL, "VCard item", ERR_NAME, TP,
222 "Doesn't have that key - did you miss to filter in advance?");
225 int preeval_vcard_name_str(WCTemplateToken *Token)
227 WCTemplputParams TPP;
228 WCTemplputParams *TP;
230 StrBuf *Target = NULL;
232 memset(&TPP, 0, sizeof(WCTemplputParams));
235 searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
236 if (searchFieldNo >= VCEnumCounter) {
237 LogTemplateError(NULL, "VCardName", ERR_PARM1, TP,
244 void tmpl_vcard_name_str(StrBuf *Target, WCTemplputParams *TP)
247 long searchFieldNo = GetTemplateTokenNumber(Target, TP, 0, 0);
248 /* todo: get descriptive string for this vcard type */
249 if (GetHash(vcNames, LKEY(searchFieldNo), &vItem) && (vItem != NULL)) {
250 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
253 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
254 "No i18n string for this.");
259 void tmpl_vcard_context_name_str(StrBuf *Target, WCTemplputParams *TP)
262 vcField *t = (vcField*) CTX(CTX_VCARD_TYPE);
265 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
270 if (GetHash(vcNames, LKEY(t->cval), &vItem) && (vItem != NULL)) {
271 StrBufAppendTemplate(Target, TP, (StrBuf*) vItem, 1);
274 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
275 "No i18n string for this.");
280 int filter_VC_ByType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
286 vcField *vf = (vcField*) Context;
288 memcpy(&type, key, sizeof(long));
289 searchType = GetTemplateTokenNumber(Target, TP, IT_ADDT_PARAM(0), 0);
291 if (vf->Type == searchType) {
292 HashList *vc = (HashList*) CTX(CTX_VCARD);
293 if (GetHash(vc, LKEY(vf->cval), &v) && v != NULL)
302 HashList *getContextVcard(StrBuf *Target, WCTemplputParams *TP)
304 vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
305 HashList *vc = (HashList*) CTX(CTX_VCARD);
307 if ((vf == NULL) || (vc == NULL)) {
308 LogTemplateError(NULL, "VCard item type", ERR_NAME, TP,
309 "Need VCard and Vcard type in context");
316 int filter_VC_ByContextType(const char* key, long len, void *Context, StrBuf *Target, WCTemplputParams *TP)
319 vcField *vf = (vcField*) CTX(CTX_VCARD_TYPE);
321 memcpy(&searchType, key, sizeof(long));
323 if (vf->cval == searchType) {
332 int conditional_VC_Havetype(StrBuf *Target, WCTemplputParams *TP)
334 HashList *vc = (HashList*) CTX(CTX_VCARD);
335 long HaveFieldType = GetTemplateTokenNumber(Target, TP, 2, 0);
340 HashPos *it = GetNewHashPos(vc, 0);
341 while (GetNextHashPos(vc, it, &len, &Key, &vVCitem) &&
346 memcpy(&type, Key, sizeof(long));
347 if (GetHash(DefineToToken, LKEY(type), &vvcField) &&
350 vcField *t = (vcField*) vvcField;
351 if (t && t->Type == HaveFieldType) {
363 * Address book entry (keep it short and sweet, it's just a quickie lookup
364 * which we can use to get to the real meat and bones later)
366 typedef struct _addrbookent {
367 char ab_name[64]; /* name string */
368 long ab_msgnum; /* message number of address book entry */
373 * Record compare function for sorting address book indices
375 int abcmp(const void *ab1, const void *ab2) {
377 (((const addrbookent *)ab1)->ab_name),
378 (((const addrbookent *)ab2)->ab_name)
384 * Helper function for do_addrbook_view()
385 * Converts a name into a three-letter tab label
387 void nametab(char *tabbuf, long len, char *name) {
388 stresc(tabbuf, len, name, 0, 0);
389 tabbuf[0] = toupper(tabbuf[0]);
390 tabbuf[1] = tolower(tabbuf[1]);
391 tabbuf[2] = tolower(tabbuf[2]);
397 * If it's an old "Firstname Lastname" style record, try to convert it.
399 void lastfirst_firstlast(char *namebuf) {
404 if (namebuf == NULL) return;
405 if (strchr(namebuf, ';') != NULL) return;
407 i = num_tokens(namebuf, ' ');
410 extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
411 remove_token(namebuf, i-1, ' ');
412 strcpy(firstname, namebuf);
413 sprintf(namebuf, "%s; %s", lastname, firstname);
418 wc_mime_attachment *load_vcard(message_summary *Msg)
421 StrBuf *FoundCharset = NewStrBuf();
426 wc_mime_attachment *Mime;
427 wc_mime_attachment *VCMime = NULL;
429 Msg->MsgBody = (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
430 memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
431 Msg->MsgBody->msgnum = Msg->msgnum;
433 load_message(Msg, FoundCharset, &Error);
435 FreeStrBuf(&FoundCharset);
436 /* look up the vcard... */
437 it = GetNewHashPos(Msg->AllAttach, 0);
438 while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) &&
441 Mime = (wc_mime_attachment*) vMime;
442 if ((strcmp(ChrPtr(Mime->ContentType),
443 "text/x-vcard") == 0) ||
444 (strcmp(ChrPtr(Mime->ContentType),
455 if (VCMime->Data == NULL)
456 MimeLoadData(VCMime);
463 * Turn a vCard "n" (name) field into something displayable.
465 void vcard_n_prettyize(char *name)
470 original_name = strdup(name);
471 len = strlen(original_name);
472 for (i=0; i<5; ++i) {
474 if (original_name[len-1] == ' ') {
475 original_name[--len] = 0;
477 if (original_name[len-1] == ';') {
478 original_name[--len] = 0;
484 for (i=0; i<len; ++i) {
485 if (original_name[i] == ';') {
490 name[j++] = original_name[i];
503 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
505 /* if we have some untagged QP, detect it here. */
506 if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
508 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
514 Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
518 * display_vcard() calls this after parsing the textual vCard into
519 * our 'struct vCard' data object.
521 * Set 'full' to nonzero to display the full card, otherwise it will only
522 * show a summary line.
524 * This code is a bit ugly, so perhaps an explanation is due: we do this
525 * in two passes through the vCard fields. On the first pass, we process
526 * fields we understand, and then render them in a pretty fashion at the
527 * end. Then we make a second pass, outputting all the fields we don't
528 * understand in a simple two-column name/value format.
529 * v the vCard to parse
530 * msgnum Citadel message pointer
532 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
541 StrBuf *thisname = NULL;
542 char firsttoken[SIZ];
547 thisname = NewStrBuf();
548 thisVCToken = NewStrBufPlain(NULL, 63);
549 for (i=0; i<(v->numprops); ++i) {
550 FlushStrBuf(thisVCToken);
553 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
554 StrBufPlain(thisname, v->prop[i].name, -1);
555 StrBufLowerCase(thisname);
557 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
558 ntokens = num_tokens(ChrPtr(thisname), ';');
559 for (j=0, k=0; j < ntokens && k < 10; ++j) {
562 len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
563 if (!strcasecmp(buf, "encoding=quoted-printable")) {
565 /* remove_token(thisname, j, ';');*/
567 else if (!strcasecmp(buf, "encoding=base64")) {
569 /* remove_token(thisname, j, ';');*/
572 if (StrLength(thisVCToken) > 0) {
573 StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
575 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
577 if (GetHash(VCToEnum, buf, len, &V))
581 Put(VC, IKEY(evc), Val, HFreeStrBuf);
583 syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
592 if ((StrLength(thisVCToken) > 0) &&
593 GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) &&
595 vcField *thisField = (vcField *)vField;
596 StrBuf *ThisFieldStr = NULL;
597 syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
598 switch (thisField->Type) {
599 case StringCluster: {
601 const char *Pos = NULL;
602 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
603 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
604 while (thisField->Sub[j].STR.len > 0) {
605 StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
606 ThisFieldStr = NewStrBufDup(Buf);
608 PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
611 FreeStrBuf(&thisArray);
622 /* copy over the payload into a StrBuf */
623 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
624 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
627 case Base64BinaryAttachment:
634 else if (StrLength(thisVCToken) > 0) {
635 /* Add it to the UNKNOWN field... */
638 GetHash(VC, IKEY(vcfUnknown->cval), &pv);
639 oldVal = (StrBuf*) pv;
640 if (oldVal == NULL) {
641 oldVal = NewStrBuf();
642 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
645 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
648 StrBufAppendBuf(oldVal, thisVCToken, 0);
649 StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
650 StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
654 /* copy over the payload into a StrBuf */
655 Val = NewStrBufPlain(v->prop[i].value, -1);
657 /* if we have some untagged QP, detect it here. */
658 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
660 StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
667 StrBufDecodeBase64(Val);
670 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
678 FreeStrBuf(&thisname);
680 FreeStrBuf(&thisVCToken);
683 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
685 HashList *VC = CTX(CTX_VCARD);
689 evc = GetTemplateTokenNumber(Target, TP, 0, -1);
692 if (GetHash(VC, IKEY(evc), &vStr))
694 StrBufAppendTemplate(Target, TP,
702 void display_one_vcard (StrBuf *Target, HashList *VC, const char *tp_name, size_t tp_name_len)
704 WCTemplputParams *TP = NULL;
705 WCTemplputParams SubTP;
707 memset(&SubTP, 0, sizeof(WCTemplputParams));
708 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
710 DoTemplate(tp_name, tp_name_len, Target, &SubTP);
711 UnStackContext(&SubTP);
716 * Render the address book using info we gathered during the scan
718 * addrbook the addressbook to render
719 * num_ab the number of the addressbook
721 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
725 static int NAMESPERPAGE = 60;
728 char tabfirst_label[64];
730 char tablast_label[64];
731 char this_tablabel[64];
736 wc_printf("<br><br><br><div align=\"center\"><i>");
737 wc_printf(_("This address book is empty."));
738 wc_printf("</i></div>\n");
743 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
746 num_pages = (num_ab / NAMESPERPAGE) + 1;
748 tablabels = malloc(num_pages * sizeof (char *));
749 if (tablabels == NULL) {
750 wc_printf("<br><br><br><div align=\"center\"><i>");
751 wc_printf(_("An internal error has occurred."));
752 wc_printf("</i></div>\n");
756 for (i=0; i<num_pages; ++i) {
757 tabfirst = i * NAMESPERPAGE;
758 tablast = tabfirst + NAMESPERPAGE - 1;
759 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
760 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
761 nametab(tablast_label, 64, addrbook[tablast].ab_name);
762 sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label);
763 tablabels[i] = strdup(this_tablabel);
766 tabbed_dialog(num_pages, tablabels);
769 for (i=0; i<num_ab; ++i) {
771 if ((i / NAMESPERPAGE) != page) { /* New tab */
772 page = (i / NAMESPERPAGE);
774 wc_printf("</tr></table>\n");
775 end_tab(page-1, num_pages);
777 begin_tab(page, num_pages);
778 wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
782 if ((displayed % 4) == 0) {
784 wc_printf("</tr>\n");
787 wc_printf("<tr bgcolor=\"#%s\">",
788 (bg ? "dddddd" : "ffffff")
794 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
795 addrbook[i].ab_msgnum);
796 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
797 vcard_n_prettyize(addrbook[i].ab_name);
798 escputs(addrbook[i].ab_name);
799 wc_printf("</a></td>\n");
803 /* Placeholders for empty columns at end */
804 if ((num_ab % 4) != 0) {
805 for (i=0; i<(4-(num_ab % 4)); ++i) {
806 wc_printf("<td> </td>");
810 wc_printf("</tr></table>\n");
811 end_tab((num_pages-1), num_pages);
813 begin_tab(num_pages, num_pages);
814 /* FIXME there ought to be something here */
815 end_tab(num_pages, num_pages);
817 for (i=0; i<num_pages; ++i) {
827 * Edit the vCard component of a MIME message.
828 * Supply the message number
829 * and MIME part number to fetch. Or, specify -1 for the message number
830 * to start with a blank card.
832 void do_edit_vcard(long msgnum, char *partnum,
833 message_summary *VCMsg,
834 wc_mime_attachment *VCAtt,
835 const char *return_to,
836 const char *force_room) {
837 HashList *VC; WCTemplputParams SubTP;
839 message_summary *Msg = NULL;
840 wc_mime_attachment *VCMime = NULL;
843 VC = NewHash(0, lFlathash);
845 /* Display the form */
846 output_headers(1, 1, 1, 0, 0, 0);
848 safestrncpy(whatuser, "", sizeof whatuser);
851 ((VCMsg != NULL) && (VCAtt != NULL)))
853 if ((VCMsg == NULL) && (VCAtt == NULL)) {
855 Msg = (message_summary *) malloc(sizeof(message_summary));
856 memset(Msg, 0, sizeof(message_summary));
857 Msg->msgnum = msgnum;
858 VCMime = load_vcard(Msg);
859 if (VCMime == NULL) {
860 convenience_page("770000", _("Error"), "");///TODO: important message
861 DestroyMessageSummary(Msg);
865 v = VCardLoad(VCMime->Data);
868 v = VCardLoad(VCAtt->Data);
871 parse_vcard(WCC->WBuf, v, VC, NULL);
877 memset(&SubTP, 0, sizeof(WCTemplputParams));
880 WCTemplputParams *TP = NULL;
881 WCTemplputParams SubTP;
882 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
884 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
885 UnStackContext(&SubTP);
892 DestroyMessageSummary(Msg);
898 * commit the edits to the citadel server
900 void edit_vcard(void) {
904 msgnum = lbstr("msgnum");
905 partnum = bstr("partnum");
906 do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
912 * parse edited vcard from the browser
914 void submit_vcard(void) {
916 char *serialized_vcard;
918 const StrBuf *ForceRoom;
926 const char *Pos = NULL;
928 if (!havebstr("ok_button")) {
929 readloop(readnew, eUseDefault);
933 if (havebstr("force_room")) {
934 ForceRoom = sbstr("force_room");
935 if (gotoroom(ForceRoom) != 200) {
936 AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
937 AppendImportantMessage(HKEY(": "));
938 AppendImportantMessage(SKEY(ForceRoom));
939 AppendImportantMessage(HKEY("; "));
940 AppendImportantMessage(_("Aborting."), -1);
942 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
943 select_user_to_edit(NULL);
945 else if (!strcmp(bstr("return_to"), "do_welcome")) {
948 else if (!IsEmptyStr(bstr("return_to"))) {
949 http_redirect(bstr("return_to"));
952 readloop(readnew, eUseDefault);
958 postVcard = getSubStruct(HKEY("VC"));
959 if (postVcard == NULL) {
960 AppendImportantMessage(_("An error has occurred."), -1);
962 return;//// more details
966 serv_write(HKEY("ENT0 1|||4\n"));
967 if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
973 /* Make a vCard structure out of the data supplied in the form */
974 StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
977 v = VCardLoad(Buf); /* Start with the extra fields */
979 AppendImportantMessage(_("An error has occurred."), -1);
985 SubStr = NewStrBuf();
986 it = GetNewHashPos(DefineToToken, 0);
987 while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) &&
992 vcField *t = (vcField*) pv;
997 itSub = GetNewHashPos(DefineToToken, 0);
998 while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) &&
1001 Sub = (vcField*) pv;
1002 if (Sub->parentCVal == t->cval) {
1003 if (StrLength(SubStr) > 0)
1004 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
1008 blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
1009 s = SSubBstr(postVcard, buf, blen);
1011 if ((s != NULL) && (StrLength(s) > 0)) {
1013 StrBufAppendBuf(SubStr, s, 0);
1017 if (StrLength(SubStr) > 0) {
1018 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
1020 DeleteHashPos(&itSub);
1022 else if (t->parentCVal == 0) {
1023 blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
1024 s = SSubBstr(postVcard, buf, blen);
1026 if ((s != NULL) && (StrLength(s) > 0)) {
1027 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
1032 vcField *t = (vcField*) vvcField;
1033 if (t->layer == 0) switch (t->Type) {
1053 case Base64BinaryAttachment:
1066 s = sbstr("other_inetemail");
1067 if (StrLength(s) > 0) {
1068 FlushStrBuf(SubStr);
1069 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
1070 if (StrLength(SubStr) > 0) {
1071 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
1076 FreeStrBuf(&SubStr);
1079 serialized_vcard = vcard_serialize(v);
1081 if (serialized_vcard == NULL) {
1082 AppendImportantMessage(_("An error has occurred."), -1);
1088 printf("%s", serialized_vcard);
1089 serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1090 serv_write(HKEY("\n"));
1091 serv_printf("%s\r\n", serialized_vcard);
1092 serv_write(HKEY("000\n"));
1093 free(serialized_vcard);
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);
1113 * Extract an embedded photo from a vCard for display on the client
1115 void display_vcard_photo_img(void)
1121 const char *contentType;
1122 wcsession *WCC = WC;
1124 msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1126 vcard = load_mimepart(msgnum,"1");
1127 v = VCardLoad(vcard);
1129 photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1130 FlushStrBuf(WCC->WBuf);
1131 StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1132 if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1133 FlushStrBuf(WCC->WBuf);
1135 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1136 output_headers(0, 0, 0, 0, 0, 0);
1137 hprintf("Content-Type: text/plain\r\n");
1139 wc_printf(_("Could Not decode vcard photo\n"));
1143 contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1144 http_transmit_thing(contentType, 0);
1149 typedef struct _vcardview_struct {
1151 addrbookent *addrbook;
1156 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat,
1157 void **ViewSpecific,
1164 vcardview_struct *VS;
1166 VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1167 memset(VS, 0, sizeof(vcardview_struct));
1168 *ViewSpecific = (void*)VS;
1170 VS->is_singlecard = ibstr("is_singlecard");
1171 if (VS->is_singlecard != 1) {
1172 if (oper == do_search) {
1173 snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1176 strcpy(cmd, "MSGS ALL");
1178 Stat->maxmsgs = 9999999;
1188 * preparse a vcard name
1189 * display_vcard() calls this after parsing the textual vCard into
1190 * our 'struct vCard' data object.
1191 * This gets called instead of display_parsed_vcard() if we are only looking
1192 * to extract the person's name instead of displaying the card.
1194 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
1204 name = vcard_get_prop(v, "n", 1, 0, 0);
1207 prop = vcard_get_prop(v, "n", 1, 0, 1);
1208 n = num_tokens(prop, ';');
1210 for (j=0; j<n; ++j) {
1211 extract_token(buf, prop, j, ';', sizeof buf);
1212 if (!strcasecmp(buf, "encoding=quoted-printable")) {
1215 if (!strcasecmp(buf, "encoding=base64")) {
1220 /* %ff can become 6 bytes in utf8 */
1221 *storename = malloc(len * 2 + 3);
1222 j = CtdlDecodeQuotedPrintable(
1225 (*storename)[j] = 0;
1228 /* ff will become one byte.. */
1229 *storename = malloc(len + 50);
1237 len = strlen (name);
1239 *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
1240 memcpy(*storename, name, len + 1);
1242 /* vcard_n_prettyize(storename); */
1247 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat,
1248 void **ViewSpecific,
1249 message_summary* Msg,
1253 vcardview_struct *VS;
1260 wc_mime_attachment *VCMime = NULL;
1265 char this_alpha = 0;
1268 VS = (vcardview_struct*) *ViewSpecific;
1270 VCMime = load_vcard(Msg);
1274 v = VCardLoad(VCMime->Data);
1276 if (v == NULL) return 0;
1277 VC = NewHash(0, lFlathash);
1278 parse_vcard(WC->WBuf, v, VC, VCMime);
1280 name = vcard_get_prop(v, "n", 1, 0, 0);
1282 Buf = NewStrBufPlain(name, -1);
1283 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
1284 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
1285 this_alpha = ChrPtr(Buf)[0];
1290 fetchname_parsed_vcard(v, &namebuf);
1296 if (namebuf != NULL) {
1297 lastfirst_firstlast(namebuf);
1299 len = strlen(namebuf);
1300 for (j=0; i<len; ++j) {
1301 if (namebuf[j] != ';') return 0;
1304 namebuf = strdup(_("(no name)"));
1307 namebuf = strdup(_("(no name)"));
1311 if (namebuf == NULL)
1314 VS->addrbook = realloc(VS->addrbook,
1315 (sizeof(addrbookent) * VS->num_ab) );
1316 safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, namebuf,
1317 sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1318 VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1324 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1327 vcardview_struct *VS;
1329 VS = (vcardview_struct*) *ViewSpecific;
1330 if (VS->is_singlecard)
1331 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1333 do_addrbook_view(VS->addrbook, VS->num_ab); /* Render the address book */
1337 int vcard_Cleanup(void **ViewSpecific)
1339 vcardview_struct *VS;
1341 VS = (vcardview_struct*) *ViewSpecific;
1344 (VS->addrbook != NULL))
1351 void render_MIME_VCard(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundCharset)
1353 wc_mime_attachment *Mime = (wc_mime_attachment *) CTX(CTX_MIME_ATACH);
1354 wcsession *WCC = WC;
1355 if (StrLength(Mime->Data) == 0)
1357 if (StrLength(Mime->Data) > 0) {
1362 /** If it's my vCard I can edit it */
1363 if ( (!strcasecmp(ChrPtr(WCC->CurRoom.name), USERCONFIGROOM))
1364 || ((StrLength(WCC->CurRoom.name) > 11) &&
1365 (!strcasecmp(&(ChrPtr(WCC->CurRoom.name)[11]), USERCONFIGROOM)))
1366 || (WCC->CurRoom.view == VIEW_ADDRESSBOOK)
1368 StrBufAppendPrintf(Buf, "<a href=\"edit_vcard?msgnum=%ld?partnum=%s\">",
1369 Mime->msgnum, ChrPtr(Mime->PartNum));
1370 StrBufAppendPrintf(Buf, "[%s]</a>", _("edit"));
1373 /* In all cases, display the full card */
1375 v = VCardLoad(Mime->Data);
1380 VC = NewHash(0, lFlathash);
1381 parse_vcard(Target, v, VC, Mime);
1382 display_one_vcard (Target, VC, HKEY("vcard_msg_display"));
1387 StrBufPlain(Buf, _("failed to load vcard"), -1);
1389 FreeStrBuf(&Mime->Data);
1396 ServerStartModule_VCARD
1399 ///VCToEnum = NewHash(0, NULL);
1404 ServerShutdownModule_VCARD
1407 DeleteHash(&DefineToToken);
1408 DeleteHash(&vcNames);
1409 DeleteHash(&VCTokenToDefine);
1410 /// DeleteHash(&VCToEnum);
1417 StrBuf *Prefix = NewStrBufPlain(HKEY("VC:"));
1418 DefineToToken = NewHash(1, lFlathash);
1419 vcNames = NewHash(1, lFlathash);
1420 VCTokenToDefine = NewHash(1, NULL);
1421 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1422 FreeStrBuf(&Prefix);
1423 RegisterCTX(CTX_VCARD);
1424 RegisterCTX(CTX_VCARD_TYPE);
1425 RegisterReadLoopHandlerset(
1427 vcard_GetParamsGetServerCall,
1431 vcard_LoadMsgFromServer,
1432 vcard_RenderView_or_Tail,
1434 WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1435 WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1436 WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1438 RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1439 RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1440 RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1441 RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1442 REGISTERTokenParamDefine(FlatString);
1443 REGISTERTokenParamDefine(StringCluster);
1444 REGISTERTokenParamDefine(PhoneNumber);
1445 REGISTERTokenParamDefine(EmailAddr);
1446 REGISTERTokenParamDefine(Street);
1447 REGISTERTokenParamDefine(Number);
1448 REGISTERTokenParamDefine(AliasFor);
1449 REGISTERTokenParamDefine(Base64BinaryAttachment);
1450 REGISTERTokenParamDefine(TerminateList);
1451 REGISTERTokenParamDefine(Address);
1453 RegisterConditional("VC:HAVE:TYPE", 1, conditional_VC_Havetype, CTX_VCARD);
1454 RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1455 RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);
1457 RegisterMimeRenderer(HKEY("text/x-vcard"), render_MIME_VCard, 1, 201);
1458 RegisterMimeRenderer(HKEY("text/vcard"), render_MIME_VCard, 1, 200);