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) {
362 * Record compare function for sorting address book indices
364 int abcmp(const void *ab1, const void *ab2) {
366 (((const addrbookent *)ab1)->ab_name),
367 (((const addrbookent *)ab2)->ab_name)
373 * Helper function for do_addrbook_view()
374 * Converts a name into a three-letter tab label
376 void nametab(char *tabbuf, long len, char *name) {
377 stresc(tabbuf, len, name, 0, 0);
378 tabbuf[0] = toupper(tabbuf[0]);
379 tabbuf[1] = tolower(tabbuf[1]);
380 tabbuf[2] = tolower(tabbuf[2]);
386 * If it's an old "Firstname Lastname" style record, try to convert it.
388 void lastfirst_firstlast(char *namebuf) {
393 if (namebuf == NULL) return;
394 if (strchr(namebuf, ';') != NULL) return;
396 i = num_tokens(namebuf, ' ');
399 extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
400 remove_token(namebuf, i-1, ' ');
401 strcpy(firstname, namebuf);
402 sprintf(namebuf, "%s; %s", lastname, firstname);
407 wc_mime_attachment *load_vcard(message_summary *Msg)
410 StrBuf *FoundCharset = NewStrBuf();
415 wc_mime_attachment *Mime;
416 wc_mime_attachment *VCMime = NULL;
418 Msg->MsgBody = (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
419 memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
420 Msg->MsgBody->msgnum = Msg->msgnum;
422 load_message(Msg, FoundCharset, &Error);
424 FreeStrBuf(&FoundCharset);
425 /* look up the vcard... */
426 it = GetNewHashPos(Msg->AllAttach, 0);
427 while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) &&
430 Mime = (wc_mime_attachment*) vMime;
431 if ((strcmp(ChrPtr(Mime->ContentType),
432 "text/x-vcard") == 0) ||
433 (strcmp(ChrPtr(Mime->ContentType),
444 if (VCMime->Data == NULL)
445 MimeLoadData(VCMime);
450 * fetch the display name off a vCard
452 void fetch_ab_name(message_summary *Msg, char **namebuf) {
455 wc_mime_attachment *VCMime = NULL;
457 if (namebuf == NULL) return;
459 VCMime = load_vcard(Msg);
463 /* Grab the name off the card */
464 display_vcard(WC->WBuf, VCMime, 0, 0, namebuf, Msg->msgnum);
466 if (*namebuf != NULL) {
467 lastfirst_firstlast(*namebuf);
469 len = strlen(*namebuf);
470 for (i=0; i<len; ++i) {
471 if ((*namebuf)[i] != ';') return;
474 (*namebuf) = strdup(_("(no name)"));
477 (*namebuf) = strdup(_("(no name)"));
484 * Turn a vCard "n" (name) field into something displayable.
486 void vcard_n_prettyize(char *name)
491 original_name = strdup(name);
492 len = strlen(original_name);
493 for (i=0; i<5; ++i) {
495 if (original_name[len-1] == ' ') {
496 original_name[--len] = 0;
498 if (original_name[len-1] == ';') {
499 original_name[--len] = 0;
505 for (i=0; i<len; ++i) {
506 if (original_name[i] == ';') {
511 name[j++] = original_name[i];
522 * preparse a vcard name
523 * display_vcard() calls this after parsing the textual vCard into
524 * our 'struct vCard' data object.
525 * This gets called instead of display_parsed_vcard() if we are only looking
526 * to extract the person's name instead of displaying the card.
528 void fetchname_parsed_vcard(struct vCard *v, char **storename) {
538 name = vcard_get_prop(v, "n", 1, 0, 0);
541 prop = vcard_get_prop(v, "n", 1, 0, 1);
542 n = num_tokens(prop, ';');
544 for (j=0; j<n; ++j) {
545 extract_token(buf, prop, j, ';', sizeof buf);
546 if (!strcasecmp(buf, "encoding=quoted-printable")) {
549 if (!strcasecmp(buf, "encoding=base64")) {
554 /* %ff can become 6 bytes in utf8 */
555 *storename = malloc(len * 2 + 3);
556 j = CtdlDecodeQuotedPrintable(
562 /* ff will become one byte.. */
563 *storename = malloc(len + 50);
573 *storename = malloc(len + 3); /* \0 + eventualy missing ', '*/
574 memcpy(*storename, name, len + 1);
576 /* vcard_n_prettyize(storename); */
584 void PutVcardItem(HashList *thisVC, vcField *thisField, StrBuf *ThisFieldStr, int is_qp, StrBuf *Swap)
586 /* if we have some untagged QP, detect it here. */
587 if (is_qp || (strstr(ChrPtr(ThisFieldStr), "=?")!=NULL)){
589 StrBuf_RFC822_to_Utf8(Swap, ThisFieldStr, NULL, NULL); /* default charset, current charset */
595 Put(thisVC, LKEY(thisField->cval), ThisFieldStr, HFreeStrBuf);
599 * display_vcard() calls this after parsing the textual vCard into
600 * our 'struct vCard' data object.
602 * Set 'full' to nonzero to display the full card, otherwise it will only
603 * show a summary line.
605 * This code is a bit ugly, so perhaps an explanation is due: we do this
606 * in two passes through the vCard fields. On the first pass, we process
607 * fields we understand, and then render them in a pretty fashion at the
608 * end. Then we make a second pass, outputting all the fields we don't
609 * understand in a simple two-column name/value format.
610 * v the vCard to parse
611 * msgnum Citadel message pointer
613 void parse_vcard(StrBuf *Target, struct vCard *v, HashList *VC, wc_mime_attachment *Mime)
622 StrBuf *thisname = NULL;
623 char firsttoken[SIZ];
628 thisname = NewStrBuf();
629 thisVCToken = NewStrBufPlain(NULL, 63);
630 for (i=0; i<(v->numprops); ++i) {
631 FlushStrBuf(thisVCToken);
634 syslog(LOG_DEBUG, "i: %d oneprop: %s - value: %s", i, v->prop[i].name, v->prop[i].value);
635 StrBufPlain(thisname, v->prop[i].name, -1);
636 StrBufLowerCase(thisname);
638 /*len = */extract_token(firsttoken, ChrPtr(thisname), 0, ';', sizeof firsttoken);
639 ntokens = num_tokens(ChrPtr(thisname), ';');
640 for (j=0, k=0; j < ntokens && k < 10; ++j) {
643 len = extract_token(buf, ChrPtr(thisname), j, ';', sizeof buf);
644 if (!strcasecmp(buf, "encoding=quoted-printable")) {
646 /* remove_token(thisname, j, ';');*/
648 else if (!strcasecmp(buf, "encoding=base64")) {
650 /* remove_token(thisname, j, ';');*/
653 if (StrLength(thisVCToken) > 0) {
654 StrBufAppendBufPlain(thisVCToken, HKEY(";"), 0);
656 StrBufAppendBufPlain(thisVCToken, buf, len, 0);
658 if (GetHash(VCToEnum, buf, len, &V))
662 Put(VC, IKEY(evc), Val, HFreeStrBuf);
664 syslog(LOG_DEBUG, "[%ul] -> k: %d %s - %s", evc, k, buf, VCStr[evc[k]].Key);
673 if ((StrLength(thisVCToken) > 0) &&
674 GetHash(VCTokenToDefine, SKEY(thisVCToken), &vField) &&
676 vcField *thisField = (vcField *)vField;
677 StrBuf *ThisFieldStr = NULL;
678 syslog(LOG_DEBUG, "got this token: %s, found: %s", ChrPtr(thisVCToken), thisField->STR.Key);
679 switch (thisField->Type) {
680 case StringCluster: {
682 const char *Pos = NULL;
683 StrBuf *thisArray = NewStrBufPlain(v->prop[i].value, -1);
684 StrBuf *Buf = NewStrBufPlain(NULL, StrLength(thisArray));
685 while (thisField->Sub[j].STR.len > 0) {
686 StrBufExtract_NextToken(Buf, thisArray, &Pos, ';');
687 ThisFieldStr = NewStrBufDup(Buf);
689 PutVcardItem(VC, &thisField->Sub[j], ThisFieldStr, is_qp, Swap);
692 FreeStrBuf(&thisArray);
703 /* copy over the payload into a StrBuf */
704 ThisFieldStr = NewStrBufPlain(v->prop[i].value, -1);
705 PutVcardItem(VC, thisField, ThisFieldStr, is_qp, Swap);
708 case Base64BinaryAttachment:
715 else if (StrLength(thisVCToken) > 0) {
716 /* Add it to the UNKNOWN field... */
719 GetHash(VC, IKEY(vcfUnknown->cval), &pv);
720 oldVal = (StrBuf*) pv;
721 if (oldVal == NULL) {
722 oldVal = NewStrBuf();
723 Put(VC, IKEY(vcfUnknown->cval), oldVal, HFreeStrBuf);
726 StrBufAppendBufPlain(oldVal, HKEY("\n"), 0);
729 StrBufAppendBuf(oldVal, thisVCToken, 0);
730 StrBufAppendBufPlain(oldVal, HKEY(":"), 0);
731 StrBufAppendBufPlain(oldVal, v->prop[i].value, -1, 0);
735 /* copy over the payload into a StrBuf */
736 Val = NewStrBufPlain(v->prop[i].value, -1);
738 /* if we have some untagged QP, detect it here. */
739 if (is_qp || (strstr(v->prop[i].value, "=?")!=NULL)){
741 StrBuf_RFC822_to_Utf8(Swap, Val, NULL, NULL); /* default charset, current charset */
748 StrBufDecodeBase64(Val);
751 syslog(LOG_DEBUG, "-> firsttoken: %s thisname: %s Value: [%s][%s]",
756 if (GetHash(VCToEnum, firsttoken, strlen(firsttoken), &V))
759 Put(VC, IKEY(evc), Val, HFreeStrBuf);
760 syslog(LOG_DEBUG, "[%ul]\n", evc);
764 syslog(LOG_DEBUG, "[]\n");
766 TODO: check for layer II
769 long max = num_tokens(thisname, ';');
770 firsttoken[len] = '_';
772 for (j = 0; j < max; j++) {
775 extract_token(buf, thisname, j, ';', sizeof (buf));
776 if (!strcasecmp(buf, "tel"))
778 else if (!strcasecmp(buf, "work"))
779 strcat(phone, _(" (work)"));
780 else if (!strcasecmp(buf, "home"))
781 strcat(phone, _(" (home)"));
782 else if (!strcasecmp(buf, "cell"))
783 strcat(phone, _(" (cell)"));
797 FreeStrBuf(&thisname);
799 FreeStrBuf(&thisVCToken);
802 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
804 HashList *VC = CTX(CTX_VCARD);
808 evc = GetTemplateTokenNumber(Target, TP, 0, -1);
811 if (GetHash(VC, IKEY(evc), &vStr))
813 StrBufAppendTemplate(Target, TP,
821 void display_one_vcard (StrBuf *Target, struct vCard *v, int full, wc_mime_attachment *Mime)
823 HashList *VC; WCTemplputParams SubTP;
825 memset(&SubTP, 0, sizeof(WCTemplputParams));
828 VC = NewHash(0, lFlathash);
829 parse_vcard(Target, v, VC, Mime);
832 WCTemplputParams *TP = NULL;
833 WCTemplputParams SubTP;
834 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
836 DoTemplate(HKEY("vcard_msg_display"), Target, &SubTP);
837 UnStackContext(&SubTP);
845 * Display a textual vCard
846 * (Converts to a vCard object and then calls the actual display function)
847 * Set 'full' to nonzero to display the whole card instead of a one-liner.
848 * Or, if "storename" is non-NULL, just store the person's name in that
849 * buffer instead of displaying the card at all.
851 * vcard_source the buffer containing the vcard text
852 * alpha Display only if name begins with this letter of the alphabet
853 * full Display the full vCard (otherwise just the display name)
854 * storename If not NULL, also store the display name here
855 * msgnum Citadel message pointer
857 void display_vcard(StrBuf *Target,
858 wc_mime_attachment *Mime,
870 v = VCardLoad(Mime->Data);
872 if (v == NULL) return;
874 name = vcard_get_prop(v, "n", 1, 0, 0);
876 Buf = NewStrBufPlain(name, -1);
877 Buf2 = NewStrBufPlain(NULL, StrLength(Buf));
878 StrBuf_RFC822_to_Utf8(Buf2, Buf, WC->DefaultCharset, NULL);
879 this_alpha = ChrPtr(Buf)[0];
884 if (storename != NULL) {
885 fetchname_parsed_vcard(v, storename);
887 else if ((alpha == 0) ||
888 ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha))) ||
889 ((!isalpha(alpha)) && (!isalpha(this_alpha)))
892 display_one_vcard (Target, v, full, Mime);
901 * Render the address book using info we gathered during the scan
903 * addrbook the addressbook to render
904 * num_ab the number of the addressbook
906 void do_addrbook_view(addrbookent *addrbook, int num_ab) {
910 static int NAMESPERPAGE = 60;
913 char tabfirst_label[64];
915 char tablast_label[64];
916 char this_tablabel[64];
921 wc_printf("<br><br><br><div align=\"center\"><i>");
922 wc_printf(_("This address book is empty."));
923 wc_printf("</i></div>\n");
928 qsort(addrbook, num_ab, sizeof(addrbookent), abcmp);
931 num_pages = (num_ab / NAMESPERPAGE) + 1;
933 tablabels = malloc(num_pages * sizeof (char *));
934 if (tablabels == NULL) {
935 wc_printf("<br><br><br><div align=\"center\"><i>");
936 wc_printf(_("An internal error has occurred."));
937 wc_printf("</i></div>\n");
941 for (i=0; i<num_pages; ++i) {
942 tabfirst = i * NAMESPERPAGE;
943 tablast = tabfirst + NAMESPERPAGE - 1;
944 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
945 nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name);
946 nametab(tablast_label, 64, addrbook[tablast].ab_name);
947 sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label);
948 tablabels[i] = strdup(this_tablabel);
951 tabbed_dialog(num_pages, tablabels);
954 for (i=0; i<num_ab; ++i) {
956 if ((i / NAMESPERPAGE) != page) { /* New tab */
957 page = (i / NAMESPERPAGE);
959 wc_printf("</tr></table>\n");
960 end_tab(page-1, num_pages);
962 begin_tab(page, num_pages);
963 wc_printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\" width=\"100%%\">\n");
967 if ((displayed % 4) == 0) {
969 wc_printf("</tr>\n");
972 wc_printf("<tr bgcolor=\"#%s\">",
973 (bg ? "dddddd" : "ffffff")
979 wc_printf("<a href=\"readfwd?startmsg=%ld?is_singlecard=1",
980 addrbook[i].ab_msgnum);
981 wc_printf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha"));
982 vcard_n_prettyize(addrbook[i].ab_name);
983 escputs(addrbook[i].ab_name);
984 wc_printf("</a></td>\n");
988 /* Placeholders for empty columns at end */
989 if ((num_ab % 4) != 0) {
990 for (i=0; i<(4-(num_ab % 4)); ++i) {
991 wc_printf("<td> </td>");
995 wc_printf("</tr></table>\n");
996 end_tab((num_pages-1), num_pages);
998 begin_tab(num_pages, num_pages);
999 /* FIXME there ought to be something here */
1000 end_tab(num_pages, num_pages);
1002 for (i=0; i<num_pages; ++i) {
1012 * Edit the vCard component of a MIME message.
1013 * Supply the message number
1014 * and MIME part number to fetch. Or, specify -1 for the message number
1015 * to start with a blank card.
1017 void do_edit_vcard(long msgnum, char *partnum,
1018 message_summary *VCMsg,
1019 wc_mime_attachment *VCAtt,
1020 const char *return_to,
1021 const char *force_room) {
1022 HashList *VC; WCTemplputParams SubTP;
1023 wcsession *WCC = WC;
1024 message_summary *Msg = NULL;
1025 wc_mime_attachment *VCMime = NULL;
1028 VC = NewHash(0, lFlathash);
1030 /* Display the form */
1031 output_headers(1, 1, 1, 0, 0, 0);
1033 safestrncpy(whatuser, "", sizeof whatuser);
1035 if ((msgnum >= 0) ||
1036 ((VCMsg != NULL) && (VCAtt != NULL)))
1038 if ((VCMsg == NULL) && (VCAtt == NULL)) {
1040 Msg = (message_summary *) malloc(sizeof(message_summary));
1041 memset(Msg, 0, sizeof(message_summary));
1042 Msg->msgnum = msgnum;
1043 VCMime = load_vcard(Msg);
1044 if (VCMime == NULL) {
1045 convenience_page("770000", _("Error"), "");///TODO: important message
1046 DestroyMessageSummary(Msg);
1050 v = VCardLoad(VCMime->Data);
1053 v = VCardLoad(VCAtt->Data);
1056 parse_vcard(WCC->WBuf, v, VC, NULL);
1062 memset(&SubTP, 0, sizeof(WCTemplputParams));
1065 WCTemplputParams *TP = NULL;
1066 WCTemplputParams SubTP;
1067 StackContext(TP, &SubTP, VC, CTX_VCARD, 0, NULL);
1069 DoTemplate(HKEY("vcard_edit"), WCC->WBuf, &SubTP);
1070 UnStackContext(&SubTP);
1077 DestroyMessageSummary(Msg);
1083 * commit the edits to the citadel server
1085 void edit_vcard(void) {
1089 msgnum = lbstr("msgnum");
1090 partnum = bstr("partnum");
1091 do_edit_vcard(msgnum, partnum, NULL, NULL, "", NULL);
1097 * parse edited vcard from the browser
1099 void submit_vcard(void) {
1101 char *serialized_vcard;
1103 const StrBuf *ForceRoom;
1104 HashList* postVcard;
1105 HashPos *it, *itSub;
1111 const char *Pos = NULL;
1113 if (!havebstr("ok_button")) {
1114 readloop(readnew, eUseDefault);
1118 if (havebstr("force_room")) {
1119 ForceRoom = sbstr("force_room");
1120 if (gotoroom(ForceRoom) != 200) {
1121 AppendImportantMessage(_("Unable to enter the room to save your message"), -1);
1122 AppendImportantMessage(HKEY(": "));
1123 AppendImportantMessage(SKEY(ForceRoom));
1124 AppendImportantMessage(HKEY("; "));
1125 AppendImportantMessage(_("Aborting."), -1);
1127 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1128 select_user_to_edit(NULL);
1130 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1133 else if (!IsEmptyStr(bstr("return_to"))) {
1134 http_redirect(bstr("return_to"));
1137 readloop(readnew, eUseDefault);
1143 postVcard = getSubStruct(HKEY("VC"));
1144 if (postVcard == NULL) {
1145 AppendImportantMessage(_("An error has occurred."), -1);
1147 return;//// more details
1151 serv_write(HKEY("ENT0 1|||4\n"));
1152 if (!StrBuf_ServGetln(Buf) && (GetServerStatus(Buf, NULL) != 4))
1158 /* Make a vCard structure out of the data supplied in the form */
1159 StrBufPrintf(Buf, "begin:vcard\r\n%s\r\nend:vcard\r\n",
1162 v = VCardLoad(Buf); /* Start with the extra fields */
1164 AppendImportantMessage(_("An error has occurred."), -1);
1170 SubStr = NewStrBuf();
1171 it = GetNewHashPos(DefineToToken, 0);
1172 while (GetNextHashPos(DefineToToken, it, &len, &Key, &pv) &&
1177 vcField *t = (vcField*) pv;
1179 if (t->Sub != NULL){
1181 FlushStrBuf(SubStr);
1182 itSub = GetNewHashPos(DefineToToken, 0);
1183 while (GetNextHashPos(DefineToToken, itSub, &len, &Key, &pv) &&
1186 Sub = (vcField*) pv;
1187 if (Sub->parentCVal == t->cval) {
1188 if (StrLength(SubStr) > 0)
1189 StrBufAppendBufPlain(SubStr, HKEY(";"), 0);
1193 blen = snprintf(buf, sizeof(buf), "%ld", Sub->cval);
1194 s = SSubBstr(postVcard, buf, blen);
1196 if ((s != NULL) && (StrLength(s) > 0)) {
1198 StrBufAppendBuf(SubStr, s, 0);
1202 if (StrLength(SubStr) > 0) {
1203 vcard_add_prop(v, t->STR.Key, ChrPtr(SubStr));
1205 DeleteHashPos(&itSub);
1207 else if (t->parentCVal == 0) {
1208 blen = snprintf(buf, sizeof(buf), "%ld", t->cval);
1209 s = SSubBstr(postVcard, buf, blen);
1211 if ((s != NULL) && (StrLength(s) > 0)) {
1212 vcard_add_prop(v, t->STR.Key, ChrPtr(s));
1217 vcField *t = (vcField*) vvcField;
1218 if (t->layer == 0) switch (t->Type) {
1238 case Base64BinaryAttachment:
1251 s = sbstr("other_inetemail");
1252 if (StrLength(s) > 0) {
1253 FlushStrBuf(SubStr);
1254 while (StrBufSipLine(SubStr, s, &Pos), ((Pos!=StrBufNOTNULL) && (Pos!=NULL)) ) {
1255 if (StrLength(SubStr) > 0) {
1256 vcard_add_prop(v, "email;internet", ChrPtr(SubStr));
1261 FreeStrBuf(&SubStr);
1264 serialized_vcard = vcard_serialize(v);
1266 if (serialized_vcard == NULL) {
1267 AppendImportantMessage(_("An error has occurred."), -1);
1273 printf("%s", serialized_vcard);
1274 serv_write(HKEY("Content-type: text/x-vcard; charset=UTF-8\n"));
1275 serv_write(HKEY("\n"));
1276 serv_printf("%s\r\n", serialized_vcard);
1277 serv_write(HKEY("000\n"));
1278 free(serialized_vcard);
1280 if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
1281 select_user_to_edit(NULL);
1283 else if (!strcmp(bstr("return_to"), "do_welcome")) {
1286 else if (!IsEmptyStr(bstr("return_to"))) {
1287 http_redirect(bstr("return_to"));
1290 readloop(readnew, eUseDefault);
1298 * Extract an embedded photo from a vCard for display on the client
1300 void display_vcard_photo_img(void)
1306 const char *contentType;
1307 wcsession *WCC = WC;
1309 msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
1311 vcard = load_mimepart(msgnum,"1");
1312 v = VCardLoad(vcard);
1314 photosrc = vcard_get_prop(v, "PHOTO", 1,0,0);
1315 FlushStrBuf(WCC->WBuf);
1316 StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0);
1317 if (StrBufDecodeBase64(WCC->WBuf) <= 0) {
1318 FlushStrBuf(WCC->WBuf);
1320 hprintf("HTTP/1.1 500 %s\n","Unable to get photo");
1321 output_headers(0, 0, 0, 0, 0, 0);
1322 hprintf("Content-Type: text/plain\r\n");
1324 wc_printf(_("Could Not decode vcard photo\n"));
1328 contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
1329 http_transmit_thing(contentType, 0);
1334 typedef struct _vcardview_struct {
1336 addrbookent *addrbook;
1341 int vcard_GetParamsGetServerCall(SharedMessageStatus *Stat,
1342 void **ViewSpecific,
1349 vcardview_struct *VS;
1351 VS = (vcardview_struct*) malloc (sizeof(vcardview_struct));
1352 memset(VS, 0, sizeof(vcardview_struct));
1353 *ViewSpecific = (void*)VS;
1355 VS->is_singlecard = ibstr("is_singlecard");
1356 if (VS->is_singlecard != 1) {
1357 if (oper == do_search) {
1358 snprintf(cmd, len, "MSGS SEARCH|%s", bstr("query"));
1361 strcpy(cmd, "MSGS ALL");
1363 Stat->maxmsgs = 9999999;
1368 int vcard_LoadMsgFromServer(SharedMessageStatus *Stat,
1369 void **ViewSpecific,
1370 message_summary* Msg,
1374 vcardview_struct *VS;
1377 VS = (vcardview_struct*) *ViewSpecific;
1380 fetch_ab_name(Msg, &ab_name);
1381 if (ab_name == NULL)
1384 VS->addrbook = realloc(VS->addrbook,
1385 (sizeof(addrbookent) * VS->num_ab) );
1386 safestrncpy(VS->addrbook[VS->num_ab-1].ab_name, ab_name,
1387 sizeof(VS->addrbook[VS->num_ab-1].ab_name));
1388 VS->addrbook[VS->num_ab-1].ab_msgnum = Msg->msgnum;
1394 int vcard_RenderView_or_Tail(SharedMessageStatus *Stat, void **ViewSpecific, long oper)
1397 vcardview_struct *VS;
1399 VS = (vcardview_struct*) *ViewSpecific;
1400 if (VS->is_singlecard)
1401 read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
1403 do_addrbook_view(VS->addrbook, VS->num_ab); /* Render the address book */
1407 int vcard_Cleanup(void **ViewSpecific)
1409 vcardview_struct *VS;
1411 VS = (vcardview_struct*) *ViewSpecific;
1414 (VS->addrbook != NULL))
1422 ServerStartModule_VCARD
1425 ///VCToEnum = NewHash(0, NULL);
1430 ServerShutdownModule_VCARD
1433 DeleteHash(&DefineToToken);
1434 DeleteHash(&vcNames);
1435 DeleteHash(&VCTokenToDefine);
1436 /// DeleteHash(&VCToEnum);
1443 RegisterCTX(CTX_VCARD);
1444 RegisterCTX(CTX_VCARD_TYPE);
1445 RegisterReadLoopHandlerset(
1447 vcard_GetParamsGetServerCall,
1451 vcard_LoadMsgFromServer,
1452 vcard_RenderView_or_Tail,
1454 WebcitAddUrlHandler(HKEY("edit_vcard"), "", 0, edit_vcard, 0);
1455 WebcitAddUrlHandler(HKEY("submit_vcard"), "", 0, submit_vcard, 0);
1456 WebcitAddUrlHandler(HKEY("vcardphoto"), "", 0, display_vcard_photo_img, NEED_URL);
1458 Put(VCToEnum, HKEY("n"), (void*)VC_n, reference_free_handler);
1459 Put(VCToEnum, HKEY("fn"), (void*)VC_fn, reference_free_handler);
1460 Put(VCToEnum, HKEY("title"), (void*)VC_title, reference_free_handler);
1461 Put(VCToEnum, HKEY("org"), (void*)VC_org, reference_free_handler);
1462 Put(VCToEnum, HKEY("email"), (void*)VC_email, reference_free_handler);
1463 Put(VCToEnum, HKEY("tel"), (void*)VC_tel, reference_free_handler);
1464 Put(VCToEnum, HKEY("work"), (void*)VC_work, reference_free_handler);
1465 Put(VCToEnum, HKEY("home"), (void*)VC_home, reference_free_handler);
1466 Put(VCToEnum, HKEY("cell"), (void*)VC_cell, reference_free_handler);
1467 Put(VCToEnum, HKEY("adr"), (void*)VC_adr, reference_free_handler);
1468 Put(VCToEnum, HKEY("photo"), (void*)VC_photo, reference_free_handler);
1469 Put(VCToEnum, HKEY("version"), (void*)VC_version, reference_free_handler);
1470 Put(VCToEnum, HKEY("rev"), (void*)VC_rev, reference_free_handler);
1471 Put(VCToEnum, HKEY("label"), (void*)VC_label, reference_free_handler);
1474 RegisterNamespace("VC", 1, 2, tmplput_VCARD_ITEM, NULL, CTX_VCARD);
1476 REGISTERTokenParamDefine(VC_n);
1477 REGISTERTokenParamDefine(VC_fn);
1478 REGISTERTokenParamDefine(VC_title);
1479 REGISTERTokenParamDefine(VC_org);
1480 REGISTERTokenParamDefine(VC_email);
1481 REGISTERTokenParamDefine(VC_tel);
1482 REGISTERTokenParamDefine(VC_work);
1483 REGISTERTokenParamDefine(VC_home);
1484 REGISTERTokenParamDefine(VC_cell);
1485 REGISTERTokenParamDefine(VC_adr);
1486 REGISTERTokenParamDefine(VC_photo);
1487 REGISTERTokenParamDefine(VC_version);
1488 REGISTERTokenParamDefine(VC_rev);
1489 REGISTERTokenParamDefine(VC_label);
1493 StrBuf *Prefix = NewStrBufPlain(HKEY("VC:"));
1494 DefineToToken = NewHash(1, lFlathash);
1495 vcNames = NewHash(1, lFlathash);
1496 VCTokenToDefine = NewHash(1, NULL);
1497 autoRegisterTokens(&VCEnumCounter, VCStrE, Prefix, 0, 0);
1498 FreeStrBuf(&Prefix);
1500 RegisterCTX(CTX_VCARD);
1501 RegisterNamespace("VC:ITEM", 2, 2, tmpl_vcard_item, preeval_vcard_item, CTX_VCARD);
1502 RegisterNamespace("VC:CTXITEM", 1, 1, tmpl_vcard_context_item, NULL, CTX_VCARD_TYPE);
1503 RegisterNamespace("VC:NAME", 1, 1, tmpl_vcard_name_str, preeval_vcard_name_str, CTX_VCARD);
1504 RegisterNamespace("VC:CTXNAME", 1, 1, tmpl_vcard_context_name_str, NULL, CTX_VCARD_TYPE);
1505 REGISTERTokenParamDefine(FlatString);
1506 REGISTERTokenParamDefine(StringCluster);
1507 REGISTERTokenParamDefine(PhoneNumber);
1508 REGISTERTokenParamDefine(EmailAddr);
1509 REGISTERTokenParamDefine(Street);
1510 REGISTERTokenParamDefine(Number);
1511 REGISTERTokenParamDefine(AliasFor);
1512 REGISTERTokenParamDefine(Base64BinaryAttachment);
1513 REGISTERTokenParamDefine(TerminateList);
1514 REGISTERTokenParamDefine(Address);
1516 RegisterConditional("VC:HAVE:TYPE", 1, conditional_VC_Havetype, CTX_VCARD);
1517 RegisterFilteredIterator("VC:TYPE", 1, DefineToToken, NULL, NULL, NULL, filter_VC_ByType, CTX_VCARD_TYPE, CTX_VCARD, IT_NOFLAG);
1518 RegisterFilteredIterator("VC:TYPE:ITEMS", 0, NULL, getContextVcard, NULL, NULL, filter_VC_ByContextType, CTX_STRBUF, CTX_VCARD_TYPE, IT_NOFLAG);