X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fvcard_edit.c;h=f56fd1c09e3f780f819734c6263917c2baa8b600;hb=808f3be91dd6b6677e380695e2f16e6473141a7e;hp=8365b54c837799f299703d98cac2403372e76877;hpb=728f8b13e0f45cd677895582efe985a596a49f61;p=citadel.git diff --git a/webcit/vcard_edit.c b/webcit/vcard_edit.c index 8365b54c8..f56fd1c09 100644 --- a/webcit/vcard_edit.c +++ b/webcit/vcard_edit.c @@ -1,60 +1,684 @@ /* - * vcard_edit.c - * - * Handles editing of vCard objects. - * * $Id$ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "webcit.h" -#include "vcard.h" + + +/** + * \brief Record compare function for sorting address book indices + * \param ab1 adressbook one + * \param ab2 adressbook two + */ +int abcmp(const void *ab1, const void *ab2) { + return(strcasecmp( + (((const addrbookent *)ab1)->ab_name), + (((const addrbookent *)ab2)->ab_name) + )); +} + + +/** + * \brief Helper function for do_addrbook_view() + * Converts a name into a three-letter tab label + * \param tabbuf the tabbuffer to add name to + * \param name the name to add to the tabbuffer + */ +void nametab(char *tabbuf, long len, char *name) { + stresc(tabbuf, len, name, 0, 0); + tabbuf[0] = toupper(tabbuf[0]); + tabbuf[1] = tolower(tabbuf[1]); + tabbuf[2] = tolower(tabbuf[2]); + tabbuf[3] = 0; +} + + +/** + * \brief display the adressbook overview + * \param msgnum the citadel message number + * \param alpha what???? + */ +void display_addressbook(long msgnum, char alpha) { + //char buf[SIZ]; + /* char mime_partnum[SIZ]; */ +/* char mime_filename[SIZ]; */ +/* char mime_content_type[SIZ]; */ + ///char mime_disposition[SIZ]; + //int mime_length; + char vcard_partnum[SIZ]; + char *vcard_source = NULL; + message_summary summ;////TODO: this will leak + + memset(&summ, 0, sizeof(summ)); + ///safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj); +///Load Message headers +// Msg = + if (!IsEmptyStr(vcard_partnum)) { + vcard_source = load_mimepart(msgnum, vcard_partnum); + if (vcard_source != NULL) { + + /** Display the summary line */ + display_vcard(WC->WBuf, vcard_source, alpha, 0, NULL,msgnum); + + /** If it's my vCard I can edit it */ + if ( (!strcasecmp(ChrPtr(WC->wc_roomname), USERCONFIGROOM)) + || (!strcasecmp(&(ChrPtr(WC->wc_roomname)[11]), USERCONFIGROOM)) + || (WC->wc_view == VIEW_ADDRESSBOOK) + ) { + wprintf("", + msgnum, vcard_partnum); + wprintf("[%s]", _("edit")); + } + + free(vcard_source); + } + } + +} + + + +/** + * \brief If it's an old "Firstname Lastname" style record, try to convert it. + * \param namebuf name to analyze, reverse if nescessary + */ +void lastfirst_firstlast(char *namebuf) { + char firstname[SIZ]; + char lastname[SIZ]; + int i; + + if (namebuf == NULL) return; + if (strchr(namebuf, ';') != NULL) return; + + i = num_tokens(namebuf, ' '); + if (i < 2) return; + + extract_token(lastname, namebuf, i-1, ' ', sizeof lastname); + remove_token(namebuf, i-1, ' '); + strcpy(firstname, namebuf); + sprintf(namebuf, "%s; %s", lastname, firstname); +} + +/** + * \brief fetch what??? name + * \param msgnum the citadel message number + * \param namebuf where to put the name in??? + */ +void fetch_ab_name(message_summary *Msg, char *namebuf) { + char buf[SIZ]; + char mime_partnum[SIZ]; + char mime_filename[SIZ]; + char mime_content_type[SIZ]; + char mime_disposition[SIZ]; + int mime_length; + char vcard_partnum[SIZ]; + char *vcard_source = NULL; + int i, len; + message_summary summ;/// TODO this will lak + + if (namebuf == NULL) return; + strcpy(namebuf, ""); + + memset(&summ, 0, sizeof(summ)); + //////safestrncpy(summ.subj, "(no subject)", sizeof summ.subj); + + sprintf(buf, "MSG0 %ld|0", Msg->msgnum); /** unfortunately we need the mime info now */ + serv_puts(buf); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') return; + + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if (!strncasecmp(buf, "part=", 5)) { + extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename); + extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum); + extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition); + extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); + mime_length = extract_int(&buf[5], 5); + + if ( (!strcasecmp(mime_content_type, "text/x-vcard")) + || (!strcasecmp(mime_content_type, "text/vcard")) ) { + strcpy(vcard_partnum, mime_partnum); + } + + } + } + + if (!IsEmptyStr(vcard_partnum)) { + vcard_source = load_mimepart(Msg->msgnum, vcard_partnum); + if (vcard_source != NULL) { + + /* Grab the name off the card */ + display_vcard(WC->WBuf, vcard_source, 0, 0, namebuf, Msg->msgnum); + + free(vcard_source); + } + } + + lastfirst_firstlast(namebuf); + striplt(namebuf); + len = strlen(namebuf); + for (i=0; i 0) { + if (original_name[len-1] == ' ') { + original_name[--len] = 0; + } + if (original_name[len-1] == ';') { + original_name[--len] = 0; + } + } + } + strcpy(name, ""); + j=0; + for (i=0; i"); + name = vcard_get_prop(v, "fn", 1, 0, 0); + if (name != NULL) { + StrEscAppend(Target, NULL, name, 0, 0); + } + else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) { + strcpy(fullname, name); + vcard_n_prettyize(fullname); + StrEscAppend(Target, NULL, fullname, 0, 0); + } + else { + StrBufAppendPrintf(Target, " "); + } + StrBufAppendPrintf(Target, ""); + return; + } + + StrBufAppendPrintf(Target, "
" + ""); + for (pass=1; pass<=2; ++pass) { + + if (v->numprops) for (i=0; i<(v->numprops); ++i) { + int len; + thisname = strdup(v->prop[i].name); + extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken); + + for (j=0; jprop[i].value); + /* if we have some untagged QP, detect it here. */ + if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL)) + utf8ify_rfc822_string(v->prop[i].value); + + if (is_qp) { + // %ff can become 6 bytes in utf8 + thisvalue = malloc(len * 2 + 3); + j = CtdlDecodeQuotedPrintable( + thisvalue, v->prop[i].value, + len); + thisvalue[j] = 0; + } + else if (is_b64) { + // ff will become one byte.. + thisvalue = malloc(len + 50); + CtdlDecodeBase64( + thisvalue, v->prop[i].value, + strlen(v->prop[i].value) ); + } + else { + thisvalue = strdup(v->prop[i].value); + } + + /** Various fields we may encounter ***/ + + /** N is name, but only if there's no FN already there */ + if (!strcasecmp(firsttoken, "n")) { + if (IsEmptyStr(fullname)) { + strcpy(fullname, thisvalue); + vcard_n_prettyize(fullname); + } + } + + /** FN (full name) is a true 'display name' field */ + else if (!strcasecmp(firsttoken, "fn")) { + strcpy(fullname, thisvalue); + } + + /** title */ + else if (!strcasecmp(firsttoken, "title")) { + strcpy(title, thisvalue); + } + + /** organization */ + else if (!strcasecmp(firsttoken, "org")) { + strcpy(org, thisvalue); + } + + else if (!strcasecmp(firsttoken, "email")) { + size_t len; + if (!IsEmptyStr(mailto)) strcat(mailto, "
"); + strcat(mailto, + ""); + + strcat(mailto, "\">"); + len = strlen(mailto); + stresc(mailto+len, SIZ - len, thisvalue, 1, 1); + strcat(mailto, ""); + } + else if (!strcasecmp(firsttoken, "tel")) { + if (!IsEmptyStr(phone)) strcat(phone, "
"); + strcat(phone, thisvalue); + for (j=0; j
\n"); + } + } + /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { + // Only output on second pass + StrBufAppendPrintf(Target, "\n"); + } */ + else if (!strcasecmp(firsttoken, "version")) { + /* ignore */ + } + else if (!strcasecmp(firsttoken, "rev")) { + /* ignore */ + } + else if (!strcasecmp(firsttoken, "label")) { + /* ignore */ + } + else { + + /*** Don't show extra fields. They're ugly. + if (pass == 2) { + StrBufAppendPrintf(Target, "\n"); + } + ***/ + } + + free(thisname); + free(thisvalue); + } + + if (pass == 1) { + StrBufAppendPrintf(Target, "" + "\n"); + + if (!IsEmptyStr(phone)) { + StrBufAppendPrintf(Target, "\n", phone); + } + if (!IsEmptyStr(mailto)) { + StrBufAppendPrintf(Target, "\n", mailto); + } + } + + } + + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Address:")); + StrBufAppendPrintf(Target, ""); + for (j=0; j"); + else StrBufAppendPrintf(Target, " "); + } + } + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Photo:")); + StrBufAppendPrintf(Target, ""); + StrBufAppendPrintf(Target, "\"Contact",msgnum); + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, thisname, 0, 0); + StrBufAppendPrintf(Target, ""); + StrEscAppend(Target, NULL, thisvalue, 0, 0); + StrBufAppendPrintf(Target, "
" + "" + ""); + StrEscAppend(Target, NULL, fullname, 0, 0); + StrBufAppendPrintf(Target, ""); + if (!IsEmptyStr(title)) { + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, title, 0, 0); + StrBufAppendPrintf(Target, "
"); + } + if (!IsEmptyStr(org)) { + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, org, 0, 0); + StrBufAppendPrintf(Target, "
"); + } + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Telephone:")); + StrBufAppendPrintf(Target, "%s
"); + StrBufAppendPrintf(Target, _("E-mail:")); + StrBufAppendPrintf(Target, "%s
\n"); +} + + + +/** + * \brief Display a textual vCard + * (Converts to a vCard object and then calls the actual display function) + * Set 'full' to nonzero to display the whole card instead of a one-liner. + * Or, if "storename" is non-NULL, just store the person's name in that + * buffer instead of displaying the card at all. + * \param vcard_source the buffer containing the vcard text + * \param alpha what??? + * \param full should we usse all lines? + * \param storename where to store??? + * \param msgnum Citadel message pointer + */ +void display_vcard(StrBuf *Target, const char *vcard_source, char alpha, int full, char *storename, + long msgnum) { + struct vCard *v; + char *name; + char buf[SIZ]; + char this_alpha = 0; + + v = vcard_load((char*)vcard_source); ///TODO + + if (v == NULL) return; + + name = vcard_get_prop(v, "n", 1, 0, 0); + if (name != NULL) { + utf8ify_rfc822_string(name); + strcpy(buf, name); + this_alpha = buf[0]; + } + + if (storename != NULL) { + fetchname_parsed_vcard(v, storename); + } + else if ( (alpha == 0) + || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) ) + || ((!isalpha(alpha)) && (!isalpha(this_alpha))) + ) { + display_parsed_vcard(Target, v, full,msgnum); + } + + vcard_free(v); +} + + + +/** + * \brief Render the address book using info we gathered during the scan + * \param addrbook the addressbook to render + * \param num_ab the number of the addressbook + */ +void do_addrbook_view(addrbookent *addrbook, int num_ab) { + int i = 0; + int displayed = 0; + int bg = 0; + static int NAMESPERPAGE = 60; + int num_pages = 0; + int tabfirst = 0; + char tabfirst_label[64]; + int tablast = 0; + char tablast_label[64]; + char this_tablabel[64]; + int page = 0; + char **tablabels; + + if (num_ab == 0) { + wprintf("


"); + wprintf(_("This address book is empty.")); + wprintf("
\n"); + return; + } + + if (num_ab > 1) { + qsort(addrbook, num_ab, sizeof(addrbookent), abcmp); + } + + num_pages = (num_ab / NAMESPERPAGE) + 1; + + tablabels = malloc(num_pages * sizeof (char *)); + if (tablabels == NULL) { + wprintf("


"); + wprintf(_("An internal error has occurred.")); + wprintf("
\n"); + return; + } + + for (i=0; i (num_ab - 1)) tablast = (num_ab - 1); + nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name); + nametab(tablast_label, 64, addrbook[tablast].ab_name); + sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label); + tablabels[i] = strdup(this_tablabel); + } + + tabbed_dialog(num_pages, tablabels); + page = (-1); + + for (i=0; i 0) { + wprintf("\n"); + end_tab(page-1, num_pages); + } + begin_tab(page, num_pages); + wprintf("\n"); + displayed = 0; + } + + if ((displayed % 4) == 0) { + if (displayed > 0) { + wprintf("\n"); + } + bg = 1 - bg; + wprintf("", + (bg ? "DDDDDD" : "FFFFFF") + ); + } + + wprintf("\n"); + ++displayed; + } + + /* Placeholders for empty columns at end */ + if ((num_ab % 4) != 0) { + for (i=0; i<(4-(num_ab % 4)); ++i) { + wprintf(""); + } + } + + wprintf("
"); + + wprintf("", bstr("alpha")); + vcard_n_prettyize(addrbook[i].ab_name); + escputs(addrbook[i].ab_name); + wprintf(" 
\n"); + end_tab((num_pages-1), num_pages); + + begin_tab(num_pages, num_pages); + /* FIXME there ought to be something here */ + end_tab(num_pages, num_pages); + + for (i=0; i\n"); + wprintf("\n", WC->nonce); + + if (force_room != NULL) { + wprintf("\n"); } - - vcard_free(v); - /* Display the form */ - wprintf("
\n"); - wprintf("

" - "Contact information for "); - escputs(whatuser); - wprintf("

\n"); - - wprintf("" - "" - "" - "" - "" - "\n"); - wprintf("", + wprintf("
" + "
PrefixFirstMiddleLastSuffix
\n"); + + wprintf("" + "" + "" + "" + "" + "\n", + _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix") + ); + wprintf("", prefix); - wprintf("", + wprintf("", firstname); - wprintf("", + wprintf("", middlename); - wprintf("", + wprintf("", lastname); - wprintf("
%s%s%s%s%s
\n", + wprintf("
\n", suffix); - wprintf("" - "\n", + wprintf("
PO box (optional):
"); + wprintf("\n", country); + wprintf("
"); + + wprintf(_("Display name:")); + wprintf("
" + "

\n", + fullname + ); + + wprintf(_("Title:")); + wprintf("
" + "

\n", + title + ); + + wprintf(_("Organization:")); + wprintf("
" + "

\n", + org + ); + + wprintf("
"); + + wprintf(""); + wprintf("\n", pobox); - wprintf("" - "\n", + wprintf("\n", extadr); - wprintf("" - "\n", + wprintf("\n", street); - wprintf("" - "\n", city); - wprintf(" State: " - "\n", + wprintf("\n", state); - wprintf(" ZIP code: " - "\n", + wprintf("\n", zipcode); - wprintf("" - "
"); + wprintf(_("PO box:")); + wprintf("" + "
Address line 1:
"); + wprintf(_("Address:")); + wprintf("" + "
Address line 2:
" + "
City:\n", + wprintf("
"); + wprintf(_("City:")); + wprintf("" + "
"); + wprintf(_("State:")); + wprintf("" + "
"); + wprintf(_("ZIP code:")); + wprintf("" + "
Country:
\n", + wprintf("
"); + wprintf(_("Country:")); + wprintf("" + "
\n"); - wprintf("" - "\n", + wprintf("
Home telephone:
\n"); + + wprintf("" + "\n", hometel); - wprintf("" - "
"); + wprintf(_("Home telephone:")); + wprintf("
Work telephone:
\n", + wprintf(""); + wprintf(_("Work telephone:")); + wprintf("" + "\n", worktel); + wprintf(""); + wprintf(_("Mobile telephone:")); + wprintf("" + "\n", + mobiletel); + wprintf(""); + wprintf(_("Fax number:")); + wprintf("" + "\n", + faxtel); + + wprintf(""); + wprintf("
"); - wprintf("
Internet e-mail addresses:
" - "For addresses in the Citadel directory, " - "the topmost address will be used in outgoing mail." - "
" - "

\n"); + wprintf("" + "
"); + wprintf(_("Primary Internet e-mail address")); + wprintf("
" + "
" + "
"); + wprintf(_("Internet e-mail aliases")); + wprintf("
" + "
\n"); - wprintf("
\n"); + + wprintf("\n"); - wprintf("\n"); - wprintf("
\n"); - wprintf(""); - wprintf(""); - wprintf("
\n"); - + wprintf("
\n" + "" + " " + "" + "
\n", + _("Save changes"), + _("Cancel") + ); + + wprintf("\n"); + do_template("endbox", NULL); wDumpContent(1); } - +/** + * commit the edits to the citadel server + */ void edit_vcard(void) { long msgnum; char *partnum; - msgnum = atol(bstr("msgnum")); + msgnum = lbstr("msgnum"); partnum = bstr("partnum"); - do_edit_vcard(msgnum, partnum, ""); + do_edit_vcard(msgnum, partnum, "", NULL); } - +/** + * parse edited vcard from the browser + */ void submit_vcard(void) { + struct vCard *v; + char *serialized_vcard; char buf[SIZ]; int i; - if (strcmp(bstr("sc"), "OK")) { - readloop("readnew"); + if (!havebstr("ok_button")) { + readloop(readnew); return; } + if (havebstr("force_room")) { + gotoroom(sbstr("force_room")); + } + sprintf(buf, "ENT0 1|||4||"); serv_puts(buf); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] != '4') { edit_vcard(); return; } - serv_puts("Content-type: text/x-vcard"); - serv_puts(""); - serv_puts("begin:vcard"); - serv_printf("n:%s;%s;%s;%s;%s", + /** Make a vCard structure out of the data supplied in the form */ + + snprintf(buf, sizeof buf, "begin:vcard\r\n%s\r\nend:vcard\r\n", + bstr("extrafields") + ); + v = vcard_load(buf); /** Start with the extra fields */ + if (v == NULL) { + safestrncpy(WC->ImportantMessage, + _("An error has occurred."), + sizeof WC->ImportantMessage + ); + edit_vcard(); + return; + } + + snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s", bstr("lastname"), bstr("firstname"), bstr("middlename"), bstr("prefix"), bstr("suffix") ); - serv_printf("adr:%s;%s;%s;%s;%s;%s;%s", + vcard_add_prop(v, "n", buf); + + vcard_add_prop(v, "title", bstr("title")); + vcard_add_prop(v, "fn", bstr("fullname")); + vcard_add_prop(v, "org", bstr("org")); + + snprintf(buf, sizeof buf, "%s;%s;%s;%s;%s;%s;%s", bstr("pobox"), bstr("extadr"), bstr("street"), @@ -301,27 +1072,95 @@ void submit_vcard(void) { bstr("state"), bstr("zipcode"), bstr("country") ); - serv_printf("tel;home:%s", bstr("hometel") ); - serv_printf("tel;work:%s", bstr("worktel") ); - - for (i=0; i 0) { - serv_printf("email;internet:%s", buf); + vcard_add_prop(v, "adr", buf); + + vcard_add_prop(v, "tel;home", bstr("hometel")); + vcard_add_prop(v, "tel;work", bstr("worktel")); + vcard_add_prop(v, "tel;fax", bstr("faxtel")); + vcard_add_prop(v, "tel;cell", bstr("mobiletel")); + vcard_add_prop(v, "email;internet", bstr("primary_inetemail")); + + for (i=0; iImportantMessage, + _("An error has occurred."), + sizeof WC->ImportantMessage + ); + edit_vcard(); + return; + } + + serv_puts("Content-type: text/x-vcard; charset=UTF-8"); + serv_puts(""); + serv_printf("%s\r\n", serialized_vcard); serv_puts("000"); + free(serialized_vcard); - if (!strcmp(bstr("return_to"), "/select_user_to_edit")) { + if (!strcmp(bstr("return_to"), "select_user_to_edit")) { select_user_to_edit(NULL, NULL); } - else if (!strcmp(bstr("return_to"), "/do_welcome")) { + else if (!strcmp(bstr("return_to"), "do_welcome")) { do_welcome(); } else { - readloop("readnew"); + readloop(readnew); + } +} + + + +/* + * Extract an embedded photo from a vCard for display on the client + */ +void display_vcard_photo_img(void) +{ + long msgnum = 0L; + char *vcard; + struct vCard *v; + char *photosrc; + const char *contentType; + wcsession *WCC = WC; + + msgnum = StrTol(WCC->UrlFragment2); + + vcard = load_mimepart(msgnum,"1"); + v = vcard_load(vcard); + + photosrc = vcard_get_prop(v, "PHOTO", 1,0,0); + FlushStrBuf(WCC->WBuf); + StrBufAppendBufPlain(WCC->WBuf, photosrc, -1, 0); + if (StrBufDecodeBase64(WCC->WBuf) <= 0) { + FlushStrBuf(WCC->WBuf); + + hprintf("HTTP/1.1 500 %s\n","Unable to get photo"); + output_headers(0, 0, 0, 0, 0, 0); + hprintf("Content-Type: text/plain\r\n"); + wprintf(_("Could Not decode vcard photo\n")); + end_burst(); + return; } + contentType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf)); + http_transmit_thing(contentType, 0); + free(v); + free(photosrc); } + + + +void +InitModule_VCARD +(void) +{ + WebcitAddUrlHandler(HKEY("edit_vcard"), edit_vcard, 0); + WebcitAddUrlHandler(HKEY("submit_vcard"), submit_vcard, 0); + WebcitAddUrlHandler(HKEY("vcardphoto"), display_vcard_photo_img, NEED_URL); +} +