diff options
Diffstat (limited to 'sqwebmail/addressbook.c')
| -rw-r--r-- | sqwebmail/addressbook.c | 1050 |
1 files changed, 1050 insertions, 0 deletions
diff --git a/sqwebmail/addressbook.c b/sqwebmail/addressbook.c new file mode 100644 index 0000000..75c1aa8 --- /dev/null +++ b/sqwebmail/addressbook.c @@ -0,0 +1,1050 @@ +#include "config.h" +/* +** Copyright 2000-2011 Double Precision, Inc. See COPYING for +** distribution information. +*/ + +/* +*/ + +#include "sqwebmail.h" +#include "addressbook.h" +#include "maildir.h" +#include "cgi/cgi.h" +#include "rfc822/rfc822.h" +#include "maildir/maildirmisc.h" +#include "numlib/numlib.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#define ADDRESSBOOK "sqwebmail-addressbook" + +#define TOUTF8(what) \ + libmail_u_convert_toutf8((what), sqwebmail_content_charset, NULL) + +extern const char *sqwebmail_content_charset; + +extern void output_attrencoded(const char *); +extern void output_attrencoded_fp(const char *, FILE *); +extern void output_urlencoded(const char *); +extern void print_safe(const char *); +extern void call_print_safe_to_stdout(const char *p, size_t cnt); + +static char *q_escape(const char *name) +{ + char *names; + const char *cp; + size_t namelen; + char *p; + + namelen=1; + + for (cp=name; *cp; ++cp, ++namelen) + if (*cp == '"' || *cp == '\\') + ++namelen; + + names=malloc(namelen); + if (!names) + { + enomem(); + } + + for (p=names, cp=name; *cp; ++cp) + { + if (iscntrl((int)(unsigned char)*cp)) continue; + + if (*cp == '"' || *cp == '\\') + *p++='\\'; + *p++= *cp; + } + *p=0; + + return names; +} + +/* +** When adding a new name/address pair into the address book delete +** bad characters from both. +*/ + +static void fix_nameaddr(const char *name, const char *addr, + char **nameret, char **addrret) +{ + char *names, *addresss; + char *p, *q; + + names=q_escape(name); + + if ((addresss=strdup(addr)) == 0) + { + free(names); + enomem(); + } + + for (p=q=addresss; *p; p++) + { + if (isspace((int)(unsigned char)*p)) continue; + if (iscntrl((int)(unsigned char)*p)) continue; + if (*p == '<' || *p == '>' || *p == '(' || *p == ')' || + *p == '\\') + continue; + *q++=*p; + } + *q=0; + + *nameret=names; + *addrret=addresss; +} + +static void ab_add_int(const char *name, const char *address, const char *nick) +{ + char *nicks, *names, *addresss, *p, *q; + FILE *fp; + char *header, *value; + + int new_fd; + char *new_name; + FILE *new_fp; + int written; + + /* Delete bad characters from nickname, name, address */ + + if ((nicks=strdup(nick)) == 0) enomem(); + + for (p=q=nicks; *p; p++) + { + if (isspace((int)(unsigned char)*p)) continue; + if (iscntrl((int)(unsigned char)*p)) continue; + if (strchr(":;,<>@\\", *p)) continue; + *q++=*p; + } + *q=0; + + if (*nicks == 0) + { + free(nicks); + return; + } + + /* Remove quotes from name */ + + fix_nameaddr(name, address, &names, &addresss); + + if (*addresss == 0) + { + free(addresss); + free(nicks); + free(names); + return; + } + + fp=fopen(ADDRESSBOOK, "r"); + + new_fd=maildir_createmsg(INBOX, "addressbook", &new_name); + p=malloc(sizeof("tmp/")+strlen(new_name)); + if (!p) + { + close(new_fd); + free(new_name); + enomem(); + } + strcat(strcpy(p, "tmp/"), new_name); + free(new_name); + new_name=p; + + if (new_fd < 0 || (new_fp=fdopen(new_fd, "w")) == 0) + { + if (new_fd >= 0) close(new_fd); + free(addresss); + free(nicks); + free(names); + enomem(); + return; + } + + written=0; + while (fp && (header=maildir_readheader_nolc(fp, &value)) != 0) + { + if (strcmp(header, nicks) == 0) + { + fprintf(new_fp, "%s: %s,\n ", + nicks, value); + written=1; + break; + } + fprintf(new_fp, "%s: %s\n", header, value); + } + if (!written) + fprintf(new_fp, "%s: ", nicks); + if (*names) + fprintf(new_fp, "\"%s\" <%s>\n", + names, addresss); + else + fprintf(new_fp, "<%s>\n", addresss); + free(names); + free(addresss); + free(nicks); + + while (fp && (header=maildir_readheader_nolc(fp, &value)) != 0) + fprintf(new_fp, "%s: %s\n", header, value); + + if (fp) fclose(fp); + + if (fflush(new_fp) || ferror(new_fp)) + { + fclose(new_fp); + close(new_fd); + unlink(new_name); + free(new_name); + error("Unable to write out new address book -- write error, or out of disk space."); + return; + } + fclose(new_fp); + + rename(new_name, ADDRESSBOOK); + free(new_name); +} + +void ab_add(const char *name, const char *address, const char *nick) +{ + char *nick_utf8, *name_utf8, *addr_utf8; + + if (*nick == 0 || *address == 0) + return; + + nick_utf8=TOUTF8(nick); + name_utf8=TOUTF8(name ? name:""); + addr_utf8=TOUTF8(address); + + if (nick_utf8 && name_utf8 && addr_utf8) + ab_add_int(name_utf8, addr_utf8, nick_utf8); + + if (nick_utf8) + free(nick_utf8); + if (name_utf8) + free(name_utf8); + if (addr_utf8) + free(addr_utf8); +} + +/* note: we're always passing utf-8 to dodel() */ + +static void dodel(const char *nick, struct rfc822a *a, int n, + const char *replace_name, const char *replace_addr) +{ +char *p; +FILE *fp, *new_fp; +char *new_name; +int new_fd; +char *header, *value; + +char *namebuf=0, *addrbuf=0; +struct rfc822token namet, addresst; + + if (replace_name && replace_addr && n < a->naddrs) + { + fix_nameaddr(replace_name, replace_addr, &namebuf, &addrbuf); + namet.token='"'; + namet.ptr=namebuf; + namet.len=strlen(namebuf); + namet.next=0; + a->addrs[n].name= &namet; + + addresst.token=0; + addresst.ptr=addrbuf; + addresst.len=strlen(addrbuf); + addresst.next=0; + a->addrs[n].tokens= &addresst; + } + else + { + while (++n < a->naddrs) + a->addrs[n-1]=a->addrs[n]; + --a->naddrs; /* It's that simple... */ + } + + fp=fopen(ADDRESSBOOK, "r"); + + new_fd=maildir_createmsg(INBOX, "addressbook", &new_name); + if (new_fd < 0 || (new_fp=fdopen(new_fd, "w")) == 0) + { + if (new_fd >= 0) close(new_fd); + fclose(fp); + enomem(); + return; + } + p=malloc(sizeof("tmp/")+strlen(new_name)); + if (!p) + { + unlink(new_name); + fclose(fp); + fclose(new_fp); + free(new_name); + enomem(); + } + strcat(strcpy(p, "tmp/"), new_name); + free(new_name); + new_name=p; + + while (fp && (header=maildir_readheader_nolc(fp, &value)) != 0) + { + if (strcmp(header, nick) == 0) + { + char *s, *t; + + if (a->naddrs == 0) + continue; + s=rfc822_getaddrs_wrap(a, 70); + + if (!s) + { + fclose(new_fp); + close(new_fd); + fclose(fp); + unlink(new_name); + enomem(); + } + fprintf(new_fp, "%s: ", header); + for (t=s; *t; t++) + { + putc(*t, new_fp); + if (*t == '\n') + fprintf(new_fp, " "); + } + fprintf(new_fp, "\n"); + free(s); + continue; + } + fprintf(new_fp, "%s: %s\n", header, value); + } + if (fp) fclose(fp); + + if (namebuf) free(namebuf); + if (addrbuf) free(addrbuf); + + if (fflush(new_fp) || ferror(new_fp)) + { + fclose(new_fp); + unlink(new_name); + free(new_name); + error("Unable to write out new address book -- write error, or out of disk space."); + return; + } + + fclose(new_fp); + rename(new_name, ADDRESSBOOK); + free(new_name); +} + +static void dodelall(const char *nick) +{ +FILE *fp, *new_fp; +int new_fd; +char *new_name, *p; +char *header, *value; + + fp=fopen(ADDRESSBOOK, "r"); + + new_fd=maildir_createmsg(INBOX, "addressbook", &new_name); + if (new_fd < 0 || (new_fp=fdopen(new_fd, "w")) == 0) + { + if (new_fd >= 0) close(new_fd); + fclose(fp); + enomem(); + return; + } + p=malloc(sizeof("tmp/")+strlen(new_name)); + if (!p) + { + unlink(new_name); + fclose(fp); + fclose(new_fp); + free(new_name); + enomem(); + } + strcat(strcpy(p, "tmp/"), new_name); + free(new_name); + new_name=p; + + while (fp && (header=maildir_readheader_nolc(fp, &value)) != 0) + { + if (strcmp(header, nick) == 0) continue; + fprintf(new_fp, "%s: %s\n", header, value); + } + if (fp) fclose(fp); + + if (fflush(new_fp) || ferror(new_fp)) + { + fclose(new_fp); + unlink(new_name); + free(new_name); + error("Unable to write out new address book -- write error, or out of disk space."); + return; + } + + fclose(new_fp); + rename(new_name, ADDRESSBOOK); + free(new_name); +} + +void ab_listselect() +{ + ab_listselect_fp(stdout); +} + +struct abooklist { + struct abooklist *next; + char *name; + } ; + +static void abl_free(struct abooklist *a) +{ +struct abooklist *b; + + while (a) + { + b=a->next; + free(a->name); + free(a); + a=b; + } +} + +static int sortabook(const void *a, const void *b) +{ + return ( strcmp( (*(struct abooklist * const *)a)->name, + (*(struct abooklist * const *)b)->name)); +} + +void ab_listselect_fp(FILE *w) +{ + FILE *fp; + char *header, *value; + struct abooklist *a=0, *b, **aa; + size_t acnt=0, i; + + if ((fp=fopen(ADDRESSBOOK, "r")) != 0) + { + while ((header=maildir_readheader_nolc(fp, &value)) != NULL) + { + if ((b=malloc(sizeof(struct abooklist))) == 0 || + (b->name=strdup(header)) == 0) + { + if (b) free(b); + abl_free(a); + enomem(); + } + b->next=a; + a=b; + acnt++; + } + fclose(fp); + + if ((aa=malloc(sizeof(struct abooklist *)*(acnt+1))) == 0) + { + abl_free(a); + enomem(); + } + + for (acnt=0, b=a; b; b=b->next) + aa[acnt++]=b; + qsort(aa, acnt, sizeof(*aa), sortabook); + + for (i=0; i<acnt; i++) + { + char *p=libmail_u_convert_fromutf8(aa[i]->name, + sqwebmail_content_charset, + NULL); + + fprintf(w, "<option value=\""); + output_attrencoded_fp(p ? p:aa[i]->name, w); + fprintf(w, "\">"); + + output_attrencoded_fp(p ? p:aa[i]->name, w); + if (p) + free(p); + fprintf(w, "</option>\n"); + } + free(aa); + abl_free(a); + } +} + +/* +** Extract all name/address entries from the address book, for external +** processing (mostly calendaring). +*/ + +int ab_get_nameaddr( int (*callback_func)(const char *, const char *, + void *), + void *callback_arg) +{ + FILE *fp; + char *header, *value; + int rc=0; + + if ((fp=fopen(ADDRESSBOOK, "r")) != 0) + { + while ((header=maildir_readheader_nolc(fp, &value)) != NULL) + { + struct rfc822t *t; + struct rfc822a *a; + + if (!value) + continue; + + t=rfc822t_alloc_new(value, NULL, NULL); + a=t ? rfc822a_alloc(t):0; + + if (a) + { + int i; + + for (i=0; i<a->naddrs; i++) + { + char *addr; + char *name; + + if (a->addrs[i].tokens == NULL) + continue; + + addr=rfc822_display_addr_tobuf(a, i, + NULL); + if (!addr) + continue; + + name=a->addrs[i].name ? + rfc822_display_name_tobuf(a, i, + NULL): + NULL; + + rc=(*callback_func)(addr, name, + callback_arg); + if (name) + free(name); + free(addr); + if (rc) + break; + } + } + + if (a) rfc822a_free(a); + if (t) rfc822t_free(t); + if (rc) + break; + } + fclose(fp); + } + return (rc); +} + +struct ab_addrselect_s { + struct ab_addrselect_s *next; + char *name; + char *addr; +} ; + +static int ab_addrselect_cb(const char *a, const char *n, void *vp) +{ + struct ab_addrselect_s **p=(struct ab_addrselect_s **)vp, *q; + + if (!n || !a) + return (0); + + while ( (*p) && strcasecmp((*p)->name, n) < 0) + p= &(*p)->next; + + if ((q=malloc(sizeof(struct ab_addrselect_s))) == NULL) + return (-1); + + if ((q->name=strdup(n)) == NULL) + { + free(q); + return(-1); + } + + if ((q->addr=strdup(a)) == NULL) + { + free(q->name); + free(q); + return(-1); + } + + q->next= *p; + *p=q; + return (0); +} + +static void ab_show_utf8(const char *p) +{ + int err; + char *p_s=libmail_u_convert_fromutf8(p, sqwebmail_content_charset, + &err); + + if (!p_s) + return; + + if (err) + { + free(p_s); + return; + } + + p=p_s; + + while (*p) + { + size_t i; + + for (i=0; p[i]; ++i) + { + if (*p == '"' || *p == '\\') + break; + } + + if (i) + call_print_safe_to_stdout(p, i); + + p += i; + + if (*p) + { + putchar('\\'); + putchar(*p); + ++p; + } + } + free(p_s); + return; +} + + +void ab_nameaddr_show(const char *name, const char *addr) +{ + if (name) + { + printf("\""); + ab_show_utf8(name); + printf("\" "); + } + printf("<"); + + if (addr) + ab_show_utf8(addr); + + printf(">"); +} + +void ab_addrselect() +{ + struct ab_addrselect_s *list=NULL, *p; + + printf("<select name=\"addressbookname\"><option value=\"\"></option>\n"); + + if (ab_get_nameaddr(ab_addrselect_cb, &list) == 0) + { + for (p=list; p; p=p->next) + { + printf("<option value=\""); + output_attrencoded(p->addr); + printf("\">"); + ab_nameaddr_show(p->name, p->addr); + printf("</option>\n"); + } + } + printf("</select>\n"); + + while ((p=list) != NULL) + { + list=p->next; + free(p->name); + free(p->addr); + free(p); + } +} + +const char *ab_find(const char *nick) +{ +FILE *fp; +char *header, *value; + + if ((fp=fopen(ADDRESSBOOK, "r")) != 0) + { + while ((header=maildir_readheader_nolc(fp, &value)) != NULL) + { + if (strcmp(header, nick) == 0) + { + fclose(fp); + return (value); + } + } + fclose(fp); + } + return (0); +} + +void addressbook() +{ +FILE *fp; +char *header, *value; +const char *nick_prompt=getarg("PROMPT"); +const char *nick_submit=getarg("SUBMIT"); +const char *nick_title1=getarg("TITLE1"); +const char *nick_title2=getarg("TITLE2"); +const char *nick_delete=getarg("DELETE"); +const char *nick_name=getarg("NAME"); +const char *nick_address=getarg("ADDRESS"); +const char *nick_add=getarg("ADD"); +const char *nick_edit=getarg("EDIT"); +const char *nick_select1=getarg("SELECT1"); +const char *nick_select2=getarg("SELECT2"); +const char *nick1; +int do_edit; +char *s, *q, *r; + +char *edit_name=0; +char *edit_addr=0; +int replace_index=0; + +#if 0 + fp=fopen("/tmp/pid", "w"); + fprintf(fp, "%d", getpid()); + fclose(fp); + sleep(10); +#endif + + nick1=cgi("nick"); + do_edit=0; + if (*cgi("nick.edit")) + do_edit=1; + else if (*cgi("nick.edit2")) + { + do_edit=1; + nick1=cgi("nick2"); + } + else if (*cgi("editnick")) + { + do_edit=1; + nick1=cgi("editnick"); + } + + if (*cgi("ADDYCNT")) /* Import from LDAP */ + { + unsigned counter=atoi(cgi("ADDYCNT")); + char numbuf[NUMBUFSIZE]; + char numbuf2[NUMBUFSIZE+10]; + unsigned i; + + if (counter < 1 || counter > 1000) + counter=1000; + nick1=cgi("nick2"); + if (!*nick1) + nick1=cgi("nick1"); + + if (*nick1) + { + do_edit=1; + for (i=0; i<counter; i++) + { + const char *addy=cgi(strcat(strcpy(numbuf2, "ADDY"), + libmail_str_size_t(i, numbuf))); + char *addycpy; + char *name; + + if (*addy == 0) continue; + + addycpy=strdup(addy); + if (!addycpy) enomem(); + + name=strchr(addycpy, '>'); + if (!name) + { + free(addycpy); + continue; + } + *name++=0; + while (*name == ' ') ++name; + addy=addycpy; + if (*addy == '<') ++addy; + ab_add(name, addy, nick1); + } + } + } + + if (*cgi("nick.delete")) + { + char *p=TOUTF8(cgi("nick")); + + do_edit=0; + + if (p) + { + dodelall(p); + free(p); + } + } + else if (*cgi("add")) + { + const char *newname=cgi("newname"); + const char *newaddr=cgi("newaddress"); + const char *editnick=cgi("editnick"); + const char *replacenum=cgi("replacenick"); + + if (*replacenum) + { + if ((fp=fopen(ADDRESSBOOK, "r")) != 0) + { + char *editnick_utf8=TOUTF8(editnick); + + while ((header=maildir_readheader_nolc(fp, + &value)) != NULL) + if (editnick_utf8 && + strcmp(header, editnick_utf8) == 0) + break; + + if (header && editnick_utf8) + { + struct rfc822t *t; + struct rfc822a *a; + + t=rfc822t_alloc_new(value, NULL, NULL); + a=t ? rfc822a_alloc(t):0; + + if (a) + { + char *newname_utf8= + TOUTF8(newname); + + char *newaddr_utf8= + TOUTF8(newaddr); + + dodel(editnick_utf8, a, + atoi(replacenum), + newname_utf8 && newaddr_utf8 ? + newname_utf8:NULL, + newname_utf8 && newaddr_utf8 ? + newaddr_utf8:NULL); + rfc822a_free(a); + if (newname_utf8) + free(newname_utf8); + if (newaddr_utf8) + free(newaddr_utf8); + } + if (t) rfc822t_free(t); + } + if (editnick_utf8) + free(editnick_utf8); + + fclose(fp); + } + } + else + ab_add(newname, newaddr, editnick); + do_edit=1; + nick1=editnick; + } + + printf("%s", nick_prompt); + printf("%s\n", nick_select1); + + ab_listselect(); + + printf("%s\n", nick_select2); + printf("%s", nick_submit); + + s=strdup(nick1); + if (!s) enomem(); + for (q=r=s; *q; q++) + { + if (isspace((int)(unsigned char)*q) || + strchr(",;:()\"%@<>'!", *q)) + continue; + *r++=*q; + } + *r=0; + + if (do_edit && *s) + { + printf("<input type=\"hidden\" name=\"editnick\" value=\""); + output_attrencoded(s); + printf("\" />\n"); + + printf("<table border=\"0\" class=\"nickedit-box\">\n"); + printf("<tr><td colspan=\"3\">\n"); + + printf("%s%s%s", nick_title1, s, nick_title2); + printf("</td></tr>\n"); + + if ((fp=fopen(ADDRESSBOOK, "r")) != 0) + { + char *s_utf8=TOUTF8(s); + + while ((header=maildir_readheader_nolc(fp, &value)) + != NULL) + if (s_utf8 && strcmp(header, s_utf8) == 0) + break; + + if (s_utf8) + free(s_utf8); + + if (header) + { + struct rfc822t *t; + struct rfc822a *a; + char *save_value=strdup(value); + + if (!save_value) + { + fclose(fp); + free(s); + enomem(); + } + strcpy(save_value, value); + /* Need copy 'cause dodel also + ** calls maildir_readheader */ + + + t=rfc822t_alloc_new(save_value, NULL, NULL); + a=t ? rfc822a_alloc(t):0; + + if (a) + { + int i; + + for (i=0; i<a->naddrs; i++) + { + char buf[100]; + + sprintf(buf, "del%d", i); + if (*cgi(buf)) + { + dodel(s, a, i, 0, 0); + break; + } + sprintf(buf, "startedit%d", i); + if (*cgi(buf)) + { + if (edit_name) + free(edit_name); + edit_name= + rfc822_display_name_tobuf(a, i, NULL); + if (edit_addr) + free(edit_addr); + edit_addr= + rfc822_display_addr_tobuf(a, i, NULL); + replace_index=i; + break; + } + } + + for (i=0; i<a->naddrs; i++) + { + char *s; + + if (a->addrs[i].tokens == 0) + continue; + printf("<tr><td align=\"right\"" + " class=\"nickname\">"); + + if (a->addrs[i].name) + /* getname defaults it + ** here. + */ + { + char *n=rfc822_display_name_tobuf(a, i, NULL); + + if (n) + { + printf("\""); + ab_show_utf8(n); + printf("\""); + free(n); + } + + } + + printf("</td><td align=\"left\"" + " class=\"nickaddr\">" + "<"); + s=rfc822_display_addr_tobuf(a, i, NULL); + + if (s) + { + ab_show_utf8(s); + free(s); + } + printf("></td><td><input type=\"submit\" name=\"startedit%d\" value=\"%s\" /> <input type=\"submit\" name=\"del%d\" value=\"%s\" /></td></tr>\n", + i, nick_edit, + i, nick_delete); + } + rfc822a_free(a); + } + if (t) rfc822t_free(t); + free(save_value); + } + fclose(fp); + } + printf("<tr><td colspan=\"3\"><hr width=\"90%%\" /></td></tr>\n"); + printf("<tr><td align=\"right\">%s</td><td colspan=\"2\"><input type=\"text\" name=\"newname\" class=\"nicknewname\"", nick_name); + + if (edit_name) + { + int err; + char *edit_name_native= + libmail_u_convert_fromutf8(edit_name, + sqwebmail_content_charset, + &err); + + if (edit_name_native) + { + if (err == 0) + { + printf(" value=\""); + output_attrencoded(edit_name_native); + printf("\""); + } + free(edit_name_native); + } + } + printf(" /></td></tr>\n"); + + printf("<tr><td align=\"right\">%s</td><td><input type=\"text\" name=\"newaddress\" class=\"nicknewaddr\"", nick_address); + if (edit_addr) + { + int err; + char *edit_addr_native= + libmail_u_convert_fromutf8(edit_addr, + sqwebmail_content_charset, + &err); + + if (edit_addr_native) + { + if (err == 0) + { + printf(" value=\""); + output_attrencoded(edit_addr_native); + printf("\""); + } + free(edit_addr_native); + } + } + + printf(" /></td><td>"); + + if (edit_name || edit_addr) + printf("<input type=\"hidden\" name=\"replacenick\" value=\"%d\" />", + replace_index); + + printf("<input type=\"submit\" name=\"add\" value=\"%s\" /></td></tr>\n", + edit_name || edit_addr ? nick_edit:nick_add); + + printf("</table>\n"); + } + free(s); + + if (edit_name) + free(edit_name); + if (edit_addr) + free(edit_addr); +} |
