/* ** Copyright 1998 - 2013 Double Precision, Inc. See COPYING for ** distribution information. */ /* */ #include "config.h" #include #include #include #include #include #include "sqwebmail.h" #include "maildir/maildirmisc.h" #include "maildir/maildircreate.h" #include "maildir/maildirnewshared.h" #include "maildir/maildirinfo.h" #include "maildir/maildiraclt.h" #include "rfc822/rfc822.h" #include "rfc822/rfc2047.h" #include "rfc2045/rfc2045.h" #include "md5/md5.h" #include "gpglib/gpglib.h" #include "maildir.h" #include "mailfilter.h" #include "maildir/maildirquota.h" #include "maildir/maildirgetquota.h" #include "maildir/maildirinfo.h" #include "numlib/numlib.h" #include "courierauth.h" #include "folder.h" #include "addressbook.h" #include "cgi/cgi.h" #include "pref.h" #include "token.h" #include "filter.h" #include "buf.h" #include "pref.h" #include "newmsg.h" #include "htmllibdir.h" #include "gpg.h" #include "acl.h" #include "auth.h" #include "msg2html.h" #if HAVE_LOCALE_H #if HAVE_SETLOCALE #include #endif #endif #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #if HAVE_SYS_STAT_H #include #endif #include #include #include #if HAVE_WCHAR_H #include #endif #include "strftime.h" extern FILE *open_langform(const char *lang, const char *formname, int print_header); extern const char *sqwebmail_content_language; extern char sqwebmail_folder_rights[]; extern const char *sqwebmail_mailboxid; extern char *get_imageurl(); extern const char *sqwebmail_content_locale; extern void print_attrencodedlen(const char *, size_t, int, FILE *); extern const char *showsize(unsigned long); extern void maildir_cleanup(); extern const char *nonloginscriptptr(); extern int pref_flagpagesize; extern int ishttps(); extern const char *sqwebmail_content_charset; extern int verify_shared_index_file; extern time_t rfc822_parsedt(const char *); static time_t current_time; static const char *folder_err_msg=0; extern const char *sqwebmail_folder; extern void output_scriptptrget(); extern void output_scriptptr(); extern void output_scriptptrpostinfo(); extern void output_attrencoded(const char *); extern void output_urlencoded(const char *); extern char *scriptptrget(); void print_safe_len(const char *p, size_t n, void (*func)(const char *, size_t)) { char buf[10]; const char *q=p; while (n) { --n; if (*p == '<') strcpy(buf, "<"); else if (*p == '>') strcpy(buf, ">"); else if (*p == '&') strcpy(buf, "&"); else if (*p == ' ') strcpy(buf, " "); else if (*p == '\n') strcpy(buf, "
"); else if (ISCTRL(*p)) sprintf(buf, "&#%d;", (int)(unsigned char)*p); else { p++; continue; } (*func)(q, p-q); (*func)(buf, strlen(buf)); p++; q=p; } (*func)(q, p-q); } static void print_safe_to_stdout(const char *p, size_t cnt) { if (cnt == 0) return; if (fwrite(p, cnt, 1, stdout) != 1) exit(1); } void print_safe(const char *p) { print_safe_len(p, strlen(p), print_safe_to_stdout); } void call_print_safe_to_stdout(const char *p, size_t cnt) { print_safe_len(p, cnt, print_safe_to_stdout); } void folder_contents_title() { const char *lab; const char *f; const char *inbox_lab, *drafts_lab, *trash_lab, *sent_lab; int in_utf8; lab=getarg("FOLDERTITLE"); if (*cgi("search")) lab=getarg("SEARCHRESULTSTITLE"); inbox_lab=getarg("INBOX"); drafts_lab=getarg("DRAFTS"); trash_lab=getarg("TRASH"); sent_lab=getarg("SENT"); f=sqwebmail_folder; in_utf8=1; if (strcmp(f, INBOX) == 0) f=inbox_lab; else if (strcmp(f, INBOX "." DRAFTS) == 0) f=drafts_lab; else if (strcmp(f, INBOX "." SENT) == 0) f=sent_lab; else if (strcmp(f, INBOX "." TRASH) == 0) f=trash_lab; else in_utf8=0; if (lab) { char *ff, *origff; printf("%s", lab); origff=ff=in_utf8 ? unicode_convert_fromutf8(f, sqwebmail_content_charset, NULL) : folder_fromutf8(f); if (strcmp(ff, NEWSHAREDSP) == 0 || strncmp(ff, NEWSHAREDSP ".", sizeof(NEWSHAREDSP)) == 0) { printf("%s", getarg("PUBLICFOLDERS")); ff=strchr(ff, '.'); if (!ff) ff=""; } output_attrencoded(ff); free(origff); } } static int group_movedel(const char *folder, int (*func)(const char *, const char *, size_t)) { struct cgi_arglist *arg; if (*cgi("SELECTALL")) /* Everything is selected */ { for (arg=cgi_arglist; arg; arg=arg->next) { const char *f; if (strncmp(arg->argname, "MOVEFILE-", 9)) continue; f=cgi(arg->argname); CHECKFILENAME(f); if ((*func)(folder, f, atol(arg->argname+9))) return (-1); } return (0); } for (arg=cgi_arglist; arg; arg=arg->next) { unsigned long l; char movedel[MAXLONGSIZE+10]; const char *f; if (strncmp(arg->argname, "MOVE-", 5)) continue; l=atol(arg->argname+5); sprintf(movedel, "MOVEFILE-%lu", l); f=cgi(movedel); CHECKFILENAME(f); if ((*func)(folder, f, l)) return (-1); } return (0); } static int groupdel(const char *folder, const char *file, size_t pos) { maildir_msgdeletefile(folder, file, pos); return (0); } static int groupmove(const char *folder, const char *file, size_t pos) { return (maildir_msgmovefile(folder, file, cgi("moveto"), pos)); } static const char *do_folder_delmsgs(const char *dir, size_t pos) { int rc=0; char buf[2]; char *cur; strcpy(buf, ACL_DELETEMSGS); acl_computeRightsOnFolder(dir, buf); #if 0 { FILE *fp=fopen("/tmp/pid", "w"); if (fp) { fprintf(fp, "%d", (int)getpid()); fclose(fp); sleep(10); } } #endif if (buf[0] == 0) return "nodel"; if (*cgi("cmddel")) { rc=group_movedel( dir, &groupdel ); maildir_savefoldermsgs(dir); } else if (*cgi("cmdpurgeall")) { char *deldir; struct maildir_info minfo; if (maildir_info_imap_find(&minfo, dir, login_returnaddr())<0) return "othererror"; if ((deldir=maildir_name2dir(minfo.homedir, minfo.maildir)) == NULL) { maildir_info_destroy(&minfo); return "othererror"; } cur = malloc(strlen(deldir)+5); strcpy(cur, deldir); strcat(cur, "/cur"); rc=maildir_del_content(cur); maildir_quota_recalculate("."); maildir_info_destroy(&minfo); free(deldir); free(cur); } else if (*cgi("cmdmove")) { const char *p=cgi("moveto"); CHECKFILENAME(p); strcpy(buf, ACL_INSERT); acl_computeRightsOnFolder(p, buf); if (buf[0] == 0) return "noinsert"; rc=group_movedel( dir, &groupmove ); maildir_savefoldermsgs(dir); } maildir_cleanup(); return rc ? "quota":""; } void folder_delmsgs(const char *dir, size_t pos) { const char *status=do_folder_delmsgs(dir, pos); if (*cgi("search")) http_redirect_argsss("&error=%s&form=folder&pos=%s&search=1&" SEARCHRESFILENAME "=%s", status, cgi("pos"), cgi(SEARCHRESFILENAME)); else http_redirect_argss("&error=%s&form=folder&pos=%s", status, cgi("pos")); } static void savepath(const char *path, const char *maildir) { char buf[BUFSIZ]; FILE *ofp; FILE *fp; struct maildir_tmpcreate_info createInfo; maildir_tmpcreate_init(&createInfo); createInfo.maildir="."; createInfo.uniq="sharedpath"; createInfo.doordie=1; ofp=maildir_tmpcreate_fp(&createInfo); fp=fopen(SHAREDPATHCACHE, "r"); if (fp) { int cnt=0; while (cnt < 1) { char *p; if (fgets(buf, sizeof(buf), fp) == NULL) break; if ((p=strchr(buf, '\n')) != NULL) *p=0; if (strcmp(buf, maildir) == 0) { if (fgets(buf, sizeof(buf), fp) == NULL) break; continue; } fprintf(ofp, "%s\n", buf); if (fgets(buf, sizeof(buf), fp) == NULL) strcpy(buf, ""); if ((p=strchr(buf, '\n')) != NULL) *p=0; fprintf(ofp, "%s\n", buf); ++cnt; } fclose(fp); } fprintf(ofp, "%s\n%s\n", maildir, path); fclose(ofp); rename(createInfo.tmpname, SHAREDPATHCACHE); maildir_tmpcreate_free(&createInfo); } void folder_search(const char *dir, size_t pos) { maildir_reload(dir); maildir_search(dir, pos, cgi("searchtxt"), sqwebmail_content_charset, pref_flagpagesize); http_redirect_argss("&search=1&form=folder&pos=%s&" SEARCHRESFILENAME "=%s", cgi("pos"), cgi(SEARCHRESFILENAME)); } static void folder_msg_link(const char *, int, size_t, char); static void folder_msg_unlink(const char *, int, size_t, char); static char *truncate_at(const char *, const char *, size_t); static void show_msg(const char *dir, MSGINFO *msg, MATCHEDSTR *matches, int row, const char *charset); void folder_contents(const char *dir, size_t pos) { MSGINFO **contents; MATCHEDSTR **matches; int i, found; int morebefore, moreafter; const char *nomsg, *selectalllab; const char *unselectalllab; const char *qerrmsg; long highend; unsigned long last_message_searched=0; unsigned long *last_message_searched_ptr=NULL; qerrmsg=getarg("PERMERR"); if (strcmp(cgi("error"), "quota") == 0) printf("%s", qerrmsg); if (strcmp(cgi("error"), "nodel") == 0) printf("%s", getarg("NODELPERM")); if (strcmp(cgi("error"), "noinsert") == 0) printf("%s", getarg("NOINSERTPERM")); if (strcmp(cgi("error"), "othererror") == 0) printf("%s", getarg("OTHERERROR")); if (strchr(sqwebmail_folder_rights, ACL_READ[0]) == NULL) { printf("%s", getarg("ACL")); return; } maildir_reload(dir); if (*cgi("search")) { #if 0 { FILE *fp=fopen("/tmp/pid", "w"); if (fp) { fprintf(fp, "%d", (int)getpid()); fclose(fp); sleep(10); } } #endif morebefore=0; moreafter=0; maildir_loadsearch(pref_flagpagesize, &contents, &matches, &last_message_searched); for (i=0; i%s %s%s%s%s\n", getarg("NUM"), getarg("DATE"), (strncmp(dir, INBOX "." SENT, sizeof(INBOX)+sizeof(SENT)-1) && strncmp(dir, INBOX "." DRAFTS, sizeof(INBOX)+sizeof(DRAFTS)-1)) ? getarg("FROM") : getarg("TO"), getarg("SUBJECT"), getarg("SIZE")); found=0; for (i=0; i
"); puts(" "); puts(""); puts("\n"); puts("\n"); printf("\n", selectalllab); puts(""); printf("\n"); folder_navigate(dir, pos, highend, morebefore, moreafter, last_message_searched_ptr); } if (!found && nomsg) { puts("

"); puts(nomsg); puts("

"); printf("\n"); } maildir_free(contents, pref_flagpagesize); if (matches) matches_free(matches, pref_flagpagesize); } static int folder_searchlink() { if (*cgi("search") == 0) return 0; printf("&search=1&" SEARCHRESFILENAME "="); output_urlencoded(cgi(SEARCHRESFILENAME)); return 1; } static void show_msg_match_str(const char *prefix, const char *utf8match, const char *suffix, const char *classname); static void show_msg(const char *dir, MSGINFO *msg, MATCHEDSTR *matches, int row, const char *charset) { const char *date, *from, *subj, *size; char *froms, *subjs; const char *p, *q; size_t l; char type[8]; char *folder_index_entry_start, *folder_index_entry_end; size_t msgnum=msg->msgnum; date=MSGINFO_DATE(msg); from=MSGINFO_FROM(msg); subj=MSGINFO_SUBJECT(msg); size=MSGINFO_SIZE(msg); type[0]=maildirfile_type(MSGINFO_FILENAME(msg)); type[1]='\0'; if (type[0] == '\0') strcpy(type, " "); folder_index_entry_start=""; folder_index_entry_end=""; if (type[0] == MSGTYPE_NEW) { folder_index_entry_start=""; folder_index_entry_end=""; } p=MSGINFO_FILENAME(msg); if ((q=strrchr(p, '/')) != 0) p=q+1; printf("%s%ld.%s %s%s%s%s", folder_index_entry_start, type, folder_index_entry_end, folder_index_entry_start ); if (!*date) date=" "; folder_msg_link(dir, row, msgnum, type[0]); print_safe(date); folder_msg_unlink(dir, row, msgnum, type[0]); printf("%s%s", folder_index_entry_end, folder_index_entry_start); if (!*from) from=" "; folder_msg_link(dir, row, msgnum, type[0]); froms=truncate_at(from, charset, 30); if (froms == 0) enomem(); print_safe(froms); free(froms); folder_msg_unlink(dir, row, msgnum, type[0]); printf("%s
%s", folder_index_entry_end, folder_index_entry_start); folder_msg_link(dir, row, msgnum, type[0]); #if 0 { static int foo=0; if (foo++ == 0) { FILE *fp=fopen("/tmp/pid", "w"); if (fp) { fprintf(fp, "%d", (int)getpid()); fclose(fp); sleep(10); } } } #endif subjs=truncate_at(subj, charset, 40); if (subjs == 0) enomem(); print_safe(subjs); l=strlen(subjs); while (l++ < 8) printf(" "); free(subjs); folder_msg_unlink(dir, row, msgnum, type[0]); printf("%s%s%s %s
\n", folder_index_entry_end, folder_index_entry_start, size, folder_index_entry_end); while (matches && matches->match) { printf(" ", (row & 1)+1); show_msg_match_str("...", matches->prefix, "", "searchmatch-affix"); show_msg_match_str("", matches->match, "", "searchmatch"); show_msg_match_str("", matches->suffix, "...", "searchmatch-affix"); printf("\n"); ++matches; } } static void show_msg_match_str(const char *prefix, const char *utf8match, const char *suffix, const char *classname) { char *p; p=unicode_convert_fromutf8(utf8match, sqwebmail_content_charset, NULL); if (p) { printf("%s", classname, prefix); output_attrencoded(p); free(p); printf("%s", suffix); } } static void do_folder_navigate(const char *dir, size_t pos, long highend, int morebefore, int moreafter) { const char *firstlab, *lastlab; const char *beforelab, *afterlab; const char *showncountlab, *jumplab, *golab; time(¤t_time); showncountlab=getarg("SHOWNCOUNT"); jumplab=getarg("JUMPTO"); golab=getarg("GO"); firstlab=getarg("FIRSTPAGE"); beforelab=getarg("PREVPAGE"); afterlab=getarg("NEXTPAGE"); lastlab=getarg("LASTPAGE"); printf("
"); if (morebefore) { printf(""); } printf("%s", firstlab); if (morebefore) printf(""); puts(" "); if (morebefore) { size_t beforepos; if (pos < pref_flagpagesize) beforepos=0; else beforepos=pos-pref_flagpagesize; printf("", (long)beforepos); } printf("%s", beforelab); if (morebefore) printf(""); printf("
\n"); if (maildir_countof(dir) > 0) { puts("\n"); puts("
\n"); printf(showncountlab, (long)(pos+1), (long)(highend+1), (long)maildir_countof(dir)); puts("
"); puts(""); } printf("\n"); printf("
"); if (moreafter) { printf("", (long)(pos+pref_flagpagesize)); } printf("%s", afterlab); if (moreafter) printf(""); puts(" "); if (moreafter) { printf("", (long)(maildir_countof(dir)-pref_flagpagesize)); } printf("%s", lastlab); if (moreafter) printf(""); printf("
\n"); } void folder_navigate(const char *dir, size_t pos, long highend, int morebefore, int moreafter, unsigned long *last_message_searched_ptr) { printf(""); if (*cgi("search")) { printf(""); printf(""); } else { printf(""); } printf("
"); printf("%s", (long)pos, getarg("RETURNTOFOLDER")); printf(""); if (last_message_searched_ptr) { printf(getarg("LASTMESSAGESEARCHED"), *last_message_searched_ptr+1); } else { printf(" "); } printf(""); do_folder_navigate(dir, pos, highend, morebefore, moreafter); printf("
"); } static void folder_msg_link(const char *dir, int row, size_t pos, char t) { #if 0 if (t == MSGTYPE_DELETED) { printf("", cgi("pos")); return; } #endif printf("", (long)pos); } else { size_t mpos=pos; char *filename=maildir_posfind(dir, &mpos); char *basename=filename ? maildir_basename(filename):NULL; output_scriptptrget(); printf("&form=open-draft&draft="); output_urlencoded(basename); printf("\">"); if (basename) free(basename); if (filename) free(filename); } } static void folder_msg_unlink(const char *dir, int row, size_t pos, char t) { printf(""); } size_t msg_pos, msg_count; static char *msg_posfile; static int msg_hasprev, msg_hasnext; static size_t msg_searchpos; static long msg_prevpos, msg_prev_searchpos; static long msg_nextpos, msg_next_searchpos; static const char *msg_nextlab=0, *msg_prevlab=0, *msg_deletelab=0, *msg_purgelab=0, *msg_folderlab=0; static char msg_type; static const char *msg_replylab=0; static const char *msg_replyalllab=0; static const char *msg_replylistlab=0; static const char *msg_forwardlab=0; static const char *msg_forwardattlab=0; static const char *msg_fullheaderlab=0; static const char *msg_movetolab=0; static const char *msg_print=0; static const char *folder_inbox=0; static const char *folder_drafts=0; static const char *folder_trash=0; static const char *folder_sent=0; static const char *msg_golab=0; static const char *msg_msglab; static const char *msg_add=0; static int initnextprevcnt; void folder_initnextprev(const char *dir, size_t pos) { MSGINFO **info; const char *p; const char *msg_numlab, *msg_numnewlab; static char *filename=0; int fd; MSGINFO *recp; MSGINFO **search_contents=NULL; MATCHEDSTR **search_matches=NULL; unsigned long last_message_searched=0; cgi_put(MIMEGPGFILENAME, ""); if (filename) free(filename); filename=0; if (*cgi("mimegpg") && (filename=maildir_posfind(dir, &pos)) != 0) { char *tptr; int nfd; fd=maildir_semisafeopen(filename, O_RDONLY, 0); if (fd >= 0) { struct maildir_tmpcreate_info createInfo; maildir_purgemimegpg(); maildir_tmpcreate_init(&createInfo); createInfo.uniq=":mimegpg:"; createInfo.doordie=1; if ((nfd=maildir_tmpcreate_fd(&createInfo)) < 0) { free(filename); error("Can't create new file."); } tptr=createInfo.tmpname; createInfo.tmpname=NULL; maildir_tmpcreate_free(&createInfo); chmod(tptr, 0600); /* ** Decrypt/check message into a temporary file ** that's immediately marked as deleted, so that it ** gets purged at the next sweep. */ if (gpgdecode(fd, nfd) < 0) { close(nfd); unlink(tptr); free(tptr); } else { close(fd); free(filename); filename=tptr; fd=nfd; cgi_put(MIMEGPGFILENAME, strrchr(filename, '/')+1); } close(fd); } } initnextprevcnt=0; msg_nextlab=getarg("NEXTLAB"); msg_prevlab=getarg("PREVLAB"); msg_deletelab=getarg("DELETELAB"); msg_purgelab=getarg("PURGELAB"); msg_folderlab=getarg("FOLDERLAB"); msg_replylab=getarg("REPLY"); msg_replyalllab=getarg("REPLYALL"); msg_replylistlab=getarg("REPLYLIST"); msg_forwardlab=getarg("FORWARD"); msg_forwardattlab=getarg("FORWARDATT"); msg_numlab=getarg("MSGNUM"); msg_numnewlab=getarg("MSGNEWNUM"); msg_fullheaderlab=getarg("FULLHDRS"); msg_movetolab=getarg("MOVETO"); msg_print=getarg("PRINT"); folder_inbox=getarg("INBOX"); folder_drafts=getarg("DRAFTS"); folder_trash=getarg("TRASH"); folder_sent=getarg("SENT"); p=getarg("CREATEFAIL"); if (strcmp(cgi("error"),"quota") == 0) printf("%s", p); msg_golab=getarg("GOLAB"); msg_add=getarg("QUICKADD"); msg_searchpos=atol(cgi("searchrow")); info=maildir_read(dir, 1, &pos, &msg_hasprev, &msg_hasnext); recp=info[0]; msg_pos=pos; msg_prevpos=msg_pos-1; msg_nextpos=msg_pos+1; msg_prev_searchpos=msg_prevpos; msg_next_searchpos=msg_nextpos; if (*cgi("search")) maildir_loadsearch(pref_flagpagesize, &search_contents, &search_matches, &last_message_searched); if (search_contents) { if (msg_searchpos < pref_flagpagesize && search_contents[msg_searchpos]) { recp=search_contents[msg_searchpos]; msg_pos=recp->msgnum; if ((msg_hasprev=msg_searchpos > 0 && search_contents[msg_searchpos-1]) != 0) { msg_prevpos=search_contents [msg_searchpos-1]->msgnum; msg_prev_searchpos=msg_searchpos-1; } if ((msg_hasnext=msg_searchpos + 1 < pref_flagpagesize && search_contents[msg_searchpos+1]) != 0) { msg_nextpos=search_contents [msg_searchpos+1]->msgnum; msg_next_searchpos=msg_searchpos+1; } } } p=strrchr(MSGINFO_FILENAME(recp), '/'); if (p) p++; else p=MSGINFO_FILENAME(recp); msg_posfile=strdup(p); if (!msg_posfile) enomem(); if ((msg_type=maildirfile_type(MSGINFO_FILENAME(recp))) == MSGTYPE_NEW) msg_numlab=msg_numnewlab; msg_msglab=msg_numlab; msg_count=maildir_countof(dir); maildir_free(info, 1); if (search_contents) maildir_free(search_contents, pref_flagpagesize); if (search_matches) matches_free(search_matches, pref_flagpagesize); } char *get_msgfilename(const char *folder, size_t *pos) { char *filename; if (*cgi(MIMEGPGFILENAME)) { const char *p=cgi(MIMEGPGFILENAME); CHECKFILENAME(p); filename=malloc(sizeof("tmp/")+strlen(p)); if (!filename) enomem(); strcat(strcpy(filename, "tmp/"), p); } else filename=maildir_posfind(folder, pos); if (!filename) error("Message not found."); return (filename); } void output_mimegpgfilename() { if (*cgi(MIMEGPGFILENAME)) { printf("&" MIMEGPGFILENAME "="); output_urlencoded(cgi(MIMEGPGFILENAME)); } } void folder_nextprev() { printf(""); printf("
"); /* PREV */ printf(""); /* NEXT */ printf(""); /* DEL */ printf("\n"); /* FOLDER */ printf("\n", (long)( (msg_pos/pref_flagpagesize)*pref_flagpagesize ), msg_folderlab); /* REPLY */ printf("\n", (long)msg_pos, msg_replylab); /* REPLY ALL */ printf("\n", (long)msg_pos, msg_replyalllab); /* REPLY LIST */ printf("\n", (long)msg_pos, msg_replylistlab); if (auth_getoptionenvint("wbnoimages")) printf("
"); if (msg_hasprev) { printf("", msg_prevpos); } printf("%s", msg_prevlab ? msg_prevlab:""); if (msg_hasprev) { printf(""); } printf(""); if (msg_hasnext) { printf("", msg_nextpos); } printf("%s", msg_nextlab ? msg_nextlab:""); if (msg_hasnext) { printf(""); } printf(""); if (msg_type != MSGTYPE_DELETED) { printf("", (long)msg_pos); } printf("%s", strcmp(sqwebmail_folder, INBOX "." TRASH) == 0 ? msg_purgelab : msg_deletelab); if (msg_type != MSGTYPE_DELETED) printf(""); printf("%s%s%s%s
"); /* FORWARD */ printf("\n", (long)msg_pos, msg_forwardlab); /* FORWARD AS ATTACHMENT*/ printf("\n", (long)msg_pos, msg_forwardattlab); /* FULL HEADERS */ if (!pref_flagfullheaders && !*cgi("fullheaders")) { printf("\n", (long)msg_pos, msg_fullheaderlab); } /* PRINT MESSAGE */ printf("\n", (long)msg_pos, ((pref_flagfullheaders || *cgi("fullheaders")) ? "&fullheaders=1" : ""), msg_print); /* SAVE MESSAGE */ printf("", (long)msg_pos, getarg("SAVEMESSAGE")); printf("
%s%s%s%s%s
"); printf("
 "); printf(msg_msglab, (int)msg_pos+1, (int)msg_count); printf(" 
"); printf("
\n"); } void list_folder(const char *p) { char *s=folder_fromutf8(p); print_safe(s); free(s); } void list_folder_xlate(const char *p, const char *path, const char *n_inbox, const char *n_drafts, const char *n_sent, const char *n_trash) { if (strcmp(p, INBOX) == 0) printf("%s", n_inbox); else if (strcmp(p, INBOX "." DRAFTS) == 0) printf("%s", n_drafts); else if (strcmp(p, INBOX "." TRASH) == 0) printf("%s", n_trash); else if (strcmp(p, INBOX "." SENT) == 0) printf("%s", n_sent); else list_folder(path); } static void parse_hierarchy(const char *hierarchy, void (*maildir_hier_cb) (const char *pfix, const char *homedir, const char *path, const char *inbox_name), void (*sharehier_cb) (const char *sharedhier, struct maildir_shindex_cache *cache)); static void show_transfer_dest_real(const char *, const char *, const char *, const char *); static void show_transfer_dest_fake(const char *, struct maildir_shindex_cache *); static void show_transfer_dest(const char *cur_folder) { parse_hierarchy(cur_folder, show_transfer_dest_real, show_transfer_dest_fake); } static void show_transfer_dest_fake(const char *dummy1, struct maildir_shindex_cache *dummy2) { } static void show_transfer_dest_real1(const char *inbox_pfix, const char *homedir, const char *cur_folder, const char *inbox_name); static void show_transfer_dest_real(const char *inbox_pfix, const char *homedir, const char *cur_folder, const char *inbox_name) { FILE *fp; char buf1[BUFSIZ]; char buf2[BUFSIZ]; show_transfer_dest_real1(inbox_pfix, homedir, cur_folder, inbox_name); if ((fp=fopen(SHAREDPATHCACHE, "r")) != NULL) { while (fgets(buf1, sizeof(buf1), fp) && fgets(buf2, sizeof(buf2), fp)) { char *p; p=strchr(buf1, '\n'); if (p) *p=0; p=strchr(buf2, '\n'); if (p) *p=0; if (homedir == NULL || strcmp(buf1, homedir)) { show_transfer_dest_real1(buf2, buf1, cur_folder, inbox_name); } } fclose(fp); } } static void show_transfer_dest_real1(const char *inbox_pfix, const char *homedir, const char *cur_folder, const char *inbox_name) { char **folders; size_t i; const char *p; int has_shared=0; maildir_listfolders(inbox_pfix, homedir, &folders); for (i=0; folders[i]; i++) { char acl_buf[2]; strcpy(acl_buf, ACL_INSERT); acl_computeRightsOnFolder(folders[i], acl_buf); if (acl_buf[0] == 0) continue; /* Transferring TO drafts is prohibited */ if (cur_folder == NULL || strcmp(cur_folder, INBOX "." DRAFTS)) { if (strcmp(folders[i], INBOX "." DRAFTS) == 0) continue; } else { if (strncmp(folders[i], SHARED ".", sizeof(SHARED)) && strcmp(folders[i], INBOX "." TRASH)) continue; } if (cur_folder && strcmp(cur_folder, folders[i]) == 0) continue; p=folders[i]; if (strcmp(p, INBOX) == 0) p=folder_inbox; else if (strcmp(p, INBOX "." DRAFTS) == 0) p=folder_drafts; else if (strcmp(p, INBOX "." TRASH) == 0) p=folder_trash; else if (strcmp(p, INBOX "." SENT) == 0) p=folder_sent; if (!p) p=folders[i]; if (strncmp(folders[i], SHARED ".", sizeof(SHARED)) == 0) { char *d=maildir_shareddir(".", strchr(folders[i], '.')+1); struct stat stat_buf; if (!d) { maildir_freefolders(&folders); enomem(); } if (stat(d, &stat_buf)) /* Not subscribed */ { free(d); continue; } free(d); if (!has_shared) { printf("\n"); has_shared=1; } } printf("\n"); } maildir_freefolders(&folders); } void folder_msgmove() { ++initnextprevcnt; printf("
\n"); printf("
 %s 
\n", (msg_type == MSGTYPE_DELETED ? " disabled":""), initnextprevcnt, msg_golab ? msg_golab:""); printf("", cgi("pos")); printf("
\n"); } void folder_delmsg(size_t pos) { MSGINFO **info; int dummy; const char *f=cgi("posfile"); size_t newpos; int rc=0; char nbuf[MAXLONGSIZE+10]; CHECKFILENAME(f); if (*cgi("move1")) { rc=maildir_msgmovefile(sqwebmail_folder, f, cgi("list1"), pos); maildir_savefoldermsgs(sqwebmail_folder); } else if (*cgi("move2")) { rc=maildir_msgmovefile(sqwebmail_folder, f, cgi("list2"), pos); maildir_savefoldermsgs(sqwebmail_folder); } else { maildir_msgdeletefile(sqwebmail_folder, f, pos); maildir_savefoldermsgs(sqwebmail_folder); } if (rc) { http_redirect_argu("&form=readmsg&pos=%s&error=quota", (unsigned long)pos); return; } newpos=pos+1; info=maildir_read(sqwebmail_folder, 1, &newpos, &dummy, &dummy); if (info[0] && newpos != pos) { sprintf(nbuf, "%lu", (unsigned long)newpos); } else { sprintf(nbuf, "%lu", (unsigned long)pos); } maildir_free(info, 1); if (*cgi("search")) { http_redirect_argss("&form=readmsg&pos=%s&search=1&" SEARCHRESFILENAME "=%s", nbuf, cgi(SEARCHRESFILENAME)); } else { http_redirect_argss("&form=readmsg&pos=%s", nbuf, ""); } } static int is_preview_mode() { /* We're in new message window, and we're previewing a draft */ return (*cgi("showdraft")); } static void dokeyimport(FILE *, struct rfc2045 *, int); static void charset_warning(const char *mime_charset, void *arg) { char charset_buf[32]; char *p; charset_buf[0]=0; strncat(charset_buf, mime_charset, sizeof(charset_buf)-1); for (p=charset_buf; *p; p++) if (*p < ' ' || *p > 0x7f || *p == '<' || *p == '>' || *p == '&') *p=' '; printf(getarg("CHSET"), charset_buf, sqwebmail_content_charset); } static void html_warning() { printf("%s", getarg("HTML")); } static void init_smileys(struct msg2html_info *info) { FILE *fp=open_langform(sqwebmail_content_language, "smileys.txt", 0); char buf[1024]; char imgbuf[3000]; if (!fp) return; while (fgets(buf, sizeof(buf), fp) != NULL) { char *p=strchr(buf, '#'); char *code; char *img; char *attr; if (p) *p=0; code=buf; for (p=buf; *p && !isspace(*p); p++) ; if (*p) *p++=0; while (*p && isspace(*p)) p++; img=p; while (*p && !isspace(*p)) p++; if (*p) *p++=0; while (*p && isspace(*p)) p++; attr=p; p=strchr(p, '\n'); if (p) *p=0; if (!*code || !*img) continue; snprintf(imgbuf, sizeof(imgbuf), "", get_imageurl(), img, attr); msg2html_add_smiley(info, code, imgbuf); } fclose(fp); } static void email_address_start(const char *name, const char *addr) { if (is_preview_mode()) return; printf("" "", msg_add ? msg_add:""); } static void email_address_end() { if (is_preview_mode()) return; printf(""); } static void email_header(const char *h, void (*cb_func)(const char *)) { char *hdrname; char *p; const char *hdrvalue; if ((hdrname=malloc(sizeof("DSPHDR_")+strlen(h))) == NULL) enomem(); strcpy (hdrname, "DSPHDR_"); strcat (hdrname, h); for (p=hdrname; *p; p++) *p=toupper((int)(unsigned char)*p); hdrvalue = getarg (hdrname); (*cb_func)(hdrvalue && *hdrvalue ? hdrvalue:h); free(hdrname); } static const char *email_header_date_fmt(const char *def) { const char *date_fmt = getarg ("DSPFMT_DATE"); if (date_fmt && *date_fmt) def=date_fmt; return def; } static void buf_cat_esc_amp(struct buf *b, const char *url) { for (; *url; ++url) { char c[2]; if (*url == '&') { buf_cat(b, "&"); } else if (*url == '<') { buf_cat(b, "<"); } else if (*url == '>') { buf_cat(b, ">"); } else if (*url == '"') { buf_cat(b, """); } else { c[0]=*url; c[1]=0; buf_cat(b, c); } } } extern const char *redirect_hash(const char *timestamp); static char *get_textlink(const char *s, void *arg) { char *t; struct buf b; buf_init(&b); if (strncmp(s, "mailto:", 7) == 0) { int i; buf_cat(&b, "') { buf_cat(&b, ">"); } else if (c[0] == '"') { buf_cat(&b, """); } else { buf_cat(&b, c); } } buf_cat(&b, "\">" ""); buf_cat_esc_amp(&b, s); buf_cat(&b, ""); } else if (strncmp(s, "http:", 5) == 0 || strncmp(s, "https:", 6) == 0) { char buffer[NUMBUFSIZE]; time_t now; char *hash; const char *n; time(&now); libmail_str_time_t(now, buffer); hash=cgiurlencode(redirect_hash(buffer)); t=cgiurlencode(s); buf_cat(&b, "" ""); buf_cat_esc_amp(&b, s); buf_cat(&b, ""); free(t); } t=strdup(b.ptr ? b.ptr:""); if (!t) enomem(); buf_free(&b); return (t); } static void message_rfc822_action(struct rfc2045id *idptr) { if (is_preview_mode()) return; printf(" "); printf("
%s  %s  %s  %s
\n", (long)msg_pos, msg_forwardattlab); printf("\n"); } static void output_mimeurl(struct rfc2045id *id, const char *form) { output_scriptptrget(); printf("&form=%s&pos=%ld", form, (long)msg_pos); msg2html_showmimeid(id, NULL); output_mimegpgfilename(); } static void inline_image_action(struct rfc2045id *id, const char *content_type, void *arg) { if (!is_preview_mode()) { printf(""); } printf("\"Inline%s\n", is_preview_mode() ? "":""); } static void showattname(const char *fmt, const char *name, const char *content_type) { char *t; if (!name || !*name) name=content_type; if (!name) name=""; t=malloc(strlen(name)+strlen(fmt)+100); if (!t) return; sprintf(t, fmt, name); output_attrencoded(t); free(t); } static void unknown_attachment_action(struct rfc2045id *id, const char *content_type, const char *content_name, off_t size, void *arg) { printf("
"); printf("
"); if (strcmp(cgi("form"), "print") == 0) { showattname(getarg("ATTSTUB"), content_name, content_type); printf(" ("); output_attrencoded(content_type); printf(")"); } else { printf("
"); showattname(getarg("ATTACHMENT"), content_name, content_type); printf(" ("); output_attrencoded(content_type); printf("; %s)
", showsize(size)); printf("
"); if (!is_preview_mode()) { printf(""); printf("%s / ", getarg("DISPATT")); printf(""); printf("%s", getarg("DOWNATT")); } printf("
\n"); } printf("
\n"); printf("
\n"); } static int is_gpg_enabled() { return *cgi(MIMEGPGFILENAME) && !is_preview_mode(); } static void application_pgp_keys_action(struct rfc2045id *id) { printf("
"); printf("
"); if (strcmp(cgi("form"), "print") == 0 || is_preview_mode()) { printf("%s", getarg("KEY")); } else { printf(""); printf("
\n"); printf(""); } printf("
\n"); printf("
\n
\n"); } static void gpg_message_action() { printf("
"); output_scriptptrpostinfo(); printf(""); printf("", cgi("pos")); printf("\n"); printf("" "
"); if ( *cgi(MIMEGPGFILENAME)) { printf("%s", getarg("NOTCOMPACTGPG")); } else { printf("%s\n", getarg("MIMEGPGNOTICE")); if (ishttps()) printf("%s\n", getarg("PASSPHRASE")); printf("%s", getarg("DECRYPT")); } printf("

\n"); } const char *redirect_hash(const char *timestamp) { struct stat stat_buf; char buffer[NUMBUFSIZE*2+10]; const char *p=getenv("SQWEBMAIL_RANDSEED"); if (strlen(timestamp) >= NUMBUFSIZE) return ""; strcat(strcpy(buffer, timestamp), " "); if (p && *p) strncat(buffer, p, NUMBUFSIZE); else { if (stat(SENDITSH, &stat_buf) < 0) return ""; libmail_str_ino_t(stat_buf.st_ino, buffer+strlen(buffer)); } return md5_hash_courier(buffer); } static char *get_url_to_mime_part(const char *mimeid, void *arg) { const char *mimegpgfilename=cgi(MIMEGPGFILENAME); const char *pos; char *p, *q; p=scriptptrget(); pos=cgi("pos"); q=malloc(strlen(p)+strlen(pos) + strlen(mimegpgfilename)+ strlen(mimeid)+ sizeof("&mimeid=&pos=&form=fetch&mimegpgfilename=")); if (!q) enomem(); strcpy(q, p); strcat(q, "&form=fetch&pos="); strcat(q, pos); strcat(q, "&mimeid="); strcat(q, mimeid); if (*mimegpgfilename) strcat(strcat(q, "&mimegpgfilename="), mimegpgfilename); return (q); } void folder_showmsg(const char *dir, size_t pos) { char *filename; FILE *fp; struct rfc2045 *rfc; char buf[BUFSIZ]; int n; int fd; struct msg2html_info *info; const char *script_name=nonloginscriptptr(); if (*cgi("addnick")) { const char *name=cgi("newname"); const char *addr=cgi("newaddr"); const char *nick1=cgi("newnick1"); const char *nick2=cgi("newnick2"); while (*nick1 && isspace((int)(unsigned char)*nick1)) ++nick1; while (*nick2 && isspace((int)(unsigned char)*nick2)) ++nick2; if (*nick2) nick1=nick2; if (*nick1) { ab_add(name, addr, nick1); } } filename=get_msgfilename(dir, &pos); fp=0; fd=maildir_semisafeopen(filename, O_RDONLY, 0); if (fd >= 0) { if ((fp=fdopen(fd, "r")) == 0) close(fd); } if (!fp) { free(filename); return; } msg_pos=pos; rfc=rfc2045_alloc(); while ((n=fread(buf, 1, sizeof(buf), fp)) > 0) rfc2045_parse(rfc, buf, n); rfc2045_parse_partial(rfc); info=script_name ? msg2html_alloc(sqwebmail_content_charset):NULL; if (info) { char nowbuffer[NUMBUFSIZE]; time_t now; char *hash; char *washpfix; char *washpfixmailto; char *scriptnameget=scriptptrget(); static const char formbuf[]="&form=newmsg&to="; info->mimegpgfilename=cgi(MIMEGPGFILENAME); if (*info->mimegpgfilename) CHECKFILENAME(info->mimegpgfilename); info->gpgdir=GPGDIR; info->fullheaders=pref_flagfullheaders || *cgi("fullheaders"); info->noflowedtext=pref_noflowedtext; info->showhtml=pref_showhtml; info->charset_warning=charset_warning; info->html_content_follows=html_warning; info->get_url_to_mime_part=get_url_to_mime_part; time(&now); libmail_str_time_t(now, nowbuffer); hash=cgiurlencode(redirect_hash(nowbuffer)); washpfix=malloc(strlen(script_name) + strlen(hash ? hash:"") + strlen(nowbuffer) + 100); if (!washpfix) enomem(); strcat(strcat(strcat(strcat(strcat(strcpy(washpfix, script_name), "?timestamp="), nowbuffer), "&md5="), (hash ? hash:"")), "&redirect="); if (hash) free(hash); washpfixmailto=malloc(strlen(scriptnameget)+sizeof(formbuf)); if (!washpfixmailto) enomem(); strcat(strcpy(washpfixmailto, scriptnameget), formbuf); free(scriptnameget); info->wash_http_prefix=washpfix; info->wash_mailto_prefix=washpfixmailto; init_smileys(info); info->email_address_start=email_address_start; info->email_address_end=email_address_end; info->email_header=email_header; info->email_header_date_fmt=email_header_date_fmt; info->get_textlink=get_textlink; info->message_rfc822_action=message_rfc822_action; info->inline_image_action=inline_image_action; info->unknown_attachment_action=unknown_attachment_action; info->application_pgp_keys_action= application_pgp_keys_action; info->gpg_message_action=gpg_message_action; info->is_gpg_enabled=is_gpg_enabled(); info->is_preview_mode=is_preview_mode(); msg2html(fp, rfc, info); msg2html_free(info); free(washpfix); free(washpfixmailto); } rfc2045_free(rfc); fclose(fp); if (*cgi(MIMEGPGFILENAME) == 0) maildir_markread(dir, pos); free(filename); } void folder_keyimport(const char *dir, size_t pos) { char *filename; FILE *fp; struct rfc2045 *rfc; int fd; filename=get_msgfilename(dir, &pos); fp=0; fd=maildir_semisafeopen(filename, O_RDONLY, 0); if (fd >= 0) { if ((fp=fdopen(fd, "r")) == 0) close(fd); } if (!fp) { free(filename); return; } rfc=rfc2045_fromfp(fp); if (libmail_gpg_has_gpg(GPGDIR) == 0) { struct rfc2045 *part; if (*cgi("pubkeyimport") && (part=rfc2045_find(rfc, cgi("keymimeid"))) != 0) { dokeyimport(fp, part, 0); } else if (*cgi("privkeyimport") && (part=rfc2045_find(rfc, cgi("keymimeid"))) != 0) { dokeyimport(fp, part, 1); } } rfc2045_free(rfc); fclose(fp); free(filename); printf("

%s", getarg("KEYIMPORT")); } static int importkey_func(const char *p, size_t cnt, void *voidptr); static int importkeyin_func(const char *p, size_t cnt, void *voidptr); static void dokeyimport(FILE *fp, struct rfc2045 *rfcp, int issecret) { off_t start_pos, end_pos, start_body, ldummy; char buf[BUFSIZ]; int cnt; static const char start_str[]= "
" "
%s
\n";

	static const char end_str[]=
		"

\n"; if (libmail_gpg_import_start(GPGDIR, issecret)) return; printf(start_str, getarg("IMPORTHDR")); rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &ldummy, &ldummy); if (fseek(fp, start_body, SEEK_SET) < 0) { error("Seek error."); libmail_gpg_import_finish(&importkey_func, NULL); printf("%s", end_str); return; } rfc2045_cdecode_start(rfcp, &importkeyin_func, 0); while (start_body < end_pos) { cnt=sizeof(buf); if (cnt > end_pos-start_body) cnt=end_pos-start_body; cnt=fread(buf, 1, cnt, fp); if (cnt <= 0) break; start_body += cnt; if (rfc2045_cdecode(rfcp, buf, cnt)) { rfc2045_cdecode_end(rfcp); printf("%s", end_str); return; } } if (rfc2045_cdecode_end(rfcp) == 0) { libmail_gpg_import_finish(&importkey_func, NULL); } printf("%s", end_str); } static int importkeyin_func(const char *p, size_t cnt, void *voidptr) { return (libmail_gpg_import_do(p, cnt, &importkey_func, NULL)); } static int importkey_func(const char *p, size_t cnt, void *voidptr) { print_attrencodedlen(p, cnt, 1, stdout); return (0); } /* ** If we're currently showing (INBOX|shared|#shared).foo.bar hierarchy, return ** "x.foo". If we're currently showing (INBOX|shared|#shared).foo, return ** an empty string. */ static char *get_parent_folder(const char *p) { const char *q; q=strrchr(p, '.'); if (q) { char *s; s=malloc(q-p+1); if (!s) enomem(); memcpy(s, p, q-p); s[q-p]=0; return (s); } return (strdup("")); } static int checkrename(const char *origfolder, const char *newfolder) { char acl_buf[2]; char *p, *q; strcpy(acl_buf, ACL_DELETEFOLDER); acl_computeRightsOnFolder(origfolder, acl_buf); if (acl_buf[0] == 0) { folder_err_msg=getarg("RENAME"); return -1; } strcpy(acl_buf, ACL_CREATE); p=strdup(newfolder); if (!p || !(q=strrchr(p, '.')) || (*q=0, acl_computeRightsOnFolder(p, acl_buf), acl_buf[0]) == 0) { if (p) free(p); folder_err_msg=getarg("RENAME"); return -1; } free(p); return 0; } static void dorename(const char *origfolder, struct maildir_info *mifrom, struct maildir_info *mito, const char *err_invalid, const char *err_cantdelete, const char *err_exists) { char *s; char *t; char *u; const char *p; struct stat stat_buf; if (mifrom->homedir == NULL || mifrom->maildir == NULL || mito->homedir == NULL || mito->maildir == NULL || strcmp(mifrom->homedir, mito->homedir)) { folder_err_msg=err_invalid; return; } s=maildir_name2dir(".", mifrom->maildir); t=maildir_name2dir(".", mito->maildir); if (!s || !t) { if (s) free(s); if (t) free(t); folder_err_msg=err_invalid; return; } p=s; if (strncmp(p, "./", 2) == 0) p += 2; if (strcmp(p, ".") == 0 || strcmp(p, "." SENT) == 0 || strcmp(p, "." DRAFTS) == 0 || strcmp(p, "." TRASH) == 0) { free(s); free(t); folder_err_msg=err_invalid; return; } u=maildir_name2dir(mito->homedir, mito->maildir); if (!u) { free(s); free(t); folder_err_msg=err_invalid; return; } if (stat(u, &stat_buf) == 0) { free(s); free(t); folder_err_msg=err_exists; return; } free(u); if (mailfilter_folderused(origfolder)) { free(s); free(t); folder_err_msg=err_cantdelete; return; } if (maildir_rename(mifrom->homedir, strncmp(s, "./", 2) == 0 ? s+2:s, strncmp(t, "./", 2) == 0 ? t+2:t, MAILDIR_RENAME_FOLDER, NULL)) folder_err_msg=err_cantdelete; free(s); free(t); } struct publicfolderlist_helper { char *name; char *homedir; char *maildir; }; static void freeph(struct publicfolderlist_helper *ph) { if (ph->name) free(ph->name); if (ph->homedir) free(ph->homedir); if (ph->maildir) free(ph->maildir); memset(ph, 0, sizeof(*ph)); } static void do_folderlist(const char *pfix, const char *homedir, const char *path, const char *inbox_name); static void do_sharedhierlist(const char *sharedhier, struct maildir_shindex_cache *cache); static int checkcreate(const char *f, int isrec) { char *s=strdup(f); char *q; char buf[2]; struct maildir_info minfo; if (!s) { folder_err_msg=getarg("CREATEPERMS"); return -1; } if (isrec) { if ((q=strrchr(s, '.')) == NULL || (*q=0, checkcreate(s, 0)) < 0) { free(s); folder_err_msg=getarg("CREATEPERMS"); return -1; } *q='.'; } if (maildir_info_imap_find(&minfo, s, login_returnaddr()) < 0) { free(s); folder_err_msg=getarg("CREATEPERMS"); return -1; } if (strchr(minfo.maildir, '.') == NULL) { free(s); folder_err_msg=getarg("CREATEPERMS"); return -1; } maildir_acl_delete(minfo.homedir, strchr(minfo.maildir, '.')); maildir_info_destroy(&minfo); strcpy(buf, ACL_CREATE); if ((q=strrchr(s, '.')) == NULL || (*q=0, acl_computeRightsOnFolder(s, buf), buf[0]) == 0) { free(s); folder_err_msg=getarg("CREATEPERMS"); return -1; } free(s); return 0; } void folder_list() { const char *err_invalid; const char *err_exists; const char *err_cantdelete; const char *msg_hasbeensent; err_invalid=getarg("INVALID"); err_exists=getarg("EXISTS"); err_cantdelete=getarg("DELETE"); msg_hasbeensent=getarg("WASSENT"); folder_err_msg=0; if (strcmp(cgi("foldermsg"), "sent") == 0) folder_err_msg=msg_hasbeensent; if (*cgi("do.create")) { const char *newfoldername=trim_spaces(cgi("foldername")); const char *newdirname=trim_spaces(cgi("dirname")); const char *folderdir=cgi("folderdir"); char *futf7; char *dutf7; /* ** New folder names cannot contain .s, and must be considered ** as valid by maildir_folderpath. */ if (!*folderdir) folderdir=INBOX; futf7=folder_toutf8(newfoldername); dutf7=folder_toutf8(newdirname);; if (!*newfoldername || strchr(futf7, '.') || strchr(dutf7, '.')) { free(futf7); free(dutf7); folder_err_msg=err_invalid; } else { char *p; struct maildir_info minfo; char *q; p=malloc(strlen(folderdir)+strlen(futf7) +strlen(dutf7)+3); if (!p) enomem(); strcpy(p, folderdir); if (*dutf7) { if (*p) strcat(p, "."); strcat(p, dutf7); } if (*p) strcat(p, "."); strcat(p, futf7); free(futf7); free(dutf7); if (maildir_info_imap_find(&minfo, p, login_returnaddr()) < 0) { folder_err_msg=err_invalid; } else if (minfo.homedir == NULL || minfo.maildir == NULL || (q=maildir_name2dir(minfo.homedir, minfo.maildir)) == NULL) { maildir_info_destroy(&minfo); folder_err_msg=err_invalid; } else if (access(q, 0) == 0) { free(q); maildir_info_destroy(&minfo); folder_err_msg=err_exists; } else { if (checkcreate(p, *newdirname != 0) == 0) { if (maildir_make(q, 0700, 0700, 1)) folder_err_msg=err_exists; else { char buf[1]; buf[0]=0; acl_computeRightsOnFolder(p, buf); /* Initialize ACLs correctly */ } } free(q); maildir_info_destroy(&minfo); } } } if (*cgi("do.delete")) { const char *p=cgi("DELETE"); char acl_buf[2]; strcpy(acl_buf, ACL_DELETEFOLDER); acl_computeRightsOnFolder(p, acl_buf); if (acl_buf[0] == 0) folder_err_msg=getarg("DELETEPERMS"); else if (mailfilter_folderused(p)) folder_err_msg=err_cantdelete; else if (maildir_delete(p, *cgi("deletecontent"))) folder_err_msg=err_cantdelete; else maildir_quota_recalculate("."); } if (*cgi("do.subunsub")) { const char *p=cgi("DELETE"); char *pp=strdup(p); char *d; if (pp && strncmp(pp, SHARED ".", sizeof(SHARED)) == 0 && (d=maildir_shareddir(".", pp+sizeof(SHARED))) != 0) { struct stat stat_buf; if (stat(d, &stat_buf) == 0) maildir_shared_unsubscribe(".", pp+sizeof(SHARED)); else maildir_shared_subscribe(".", pp+sizeof(SHARED)); free(d); } if (pp) free(pp); } if (*cgi("do.rename")) { const char *p=cgi("DELETE"); char *pp=strdup(p); struct maildir_info mifrom, mito; const char *qutf7=cgi("renametofolder"); const char *r=trim_spaces(cgi("renametoname")); char *s; char *rutf7; rutf7=folder_toutf8(r); s=malloc(strlen(qutf7)+strlen(rutf7)+1); if (!s) enomem(); strcat(strcpy(s, qutf7), rutf7); if (strchr(r, '.') == NULL && maildir_info_imap_find(&mifrom, pp, login_returnaddr()) == 0) { if (maildir_info_imap_find(&mito, s, login_returnaddr()) == 0) { if (checkrename(pp, s) == 0) dorename(pp, &mifrom, &mito, err_invalid, err_cantdelete, err_exists); maildir_info_destroy(&mifrom); } else { folder_err_msg=err_invalid; } maildir_info_destroy(&mito); } else { folder_err_msg=err_invalid; } free(rutf7); free(pp); free(s); maildir_quota_recalculate("."); } parse_hierarchy(cgi("folderdir"), do_folderlist, do_sharedhierlist); } static int do_publicfolderlist_cb(struct maildir_newshared_enum_cb *cb) { struct publicfolderlist_helper *h= (struct publicfolderlist_helper *)cb->cb_arg; h->name=strdup(cb->name); if (cb->homedir) h->homedir=strdup(cb->homedir); h->maildir=strdup(cb->maildir); return 0; } static void parse_hierarchy(const char *folderdir, void (*maildir_hier_cb) (const char *pfix, const char *homedir, const char *path, const char *inbox_name), void (*sharedhier_cb) (const char *sharedhier, struct maildir_shindex_cache *cache)) { struct maildir_shindex_cache *index; const char *indexfile; const char *subhierarchy; const char *p; const char *q; size_t l; size_t n; struct publicfolderlist_helper ph; int eof; if (strchr(folderdir, '/')) enomem(); if (strncmp(folderdir, NEWSHAREDSP, sizeof(NEWSHAREDSP)-1) == 0) switch (folderdir[sizeof(NEWSHAREDSP)-1]) { case 0: verify_shared_index_file=1; /* FALLTHRU */ case '.': break; default: (*maildir_hier_cb)(INBOX, NULL, folderdir, INBOX); return; } else { (*maildir_hier_cb)(INBOX, NULL, folderdir, INBOX); return; } index=NULL; indexfile=NULL; subhierarchy=NULL; p=folderdir; memset(&ph, 0, sizeof(ph)); while ((index=maildir_shared_cache_read(index, indexfile, subhierarchy)) != NULL) { q=strchr(p, '.'); if (!q) break; p=q+1; if ((q=strchr(p, '.')) != NULL) l=q-p; else l=strlen(p); for (n=0; nnrecords; n++) { char *m=maildir_info_imapmunge(index->records[n].name); if (!m) continue; if (strlen(m) == l && strncmp(m, p, l) == 0) { free(m); break; } free(m); } if (n >= index->nrecords) { index=NULL; break; } index->indexfile.startingpos=index->records[n].offset; freeph(&ph); if (maildir_newshared_nextAt(&index->indexfile, &eof, &do_publicfolderlist_cb, &ph) || eof) { index=NULL; break; } if (ph.homedir) { char *loc=maildir_location(ph.homedir, ph.maildir); char *m_path; char *m_inbox; if (loc) { while (*p) { if (*p == '.') break; ++p; } m_path=malloc(p-folderdir+1); if (!m_path) enomem(); memcpy(m_path, folderdir, p-folderdir); m_path[p-folderdir]=0; m_inbox=malloc(strlen(m_path)+1+strlen(p)); if (!m_inbox) enomem(); strcat(strcpy(m_inbox, m_path), p); savepath(m_path, loc); (*maildir_hier_cb)(m_path, loc, m_inbox, m_path); free(loc); free(m_path); free(m_inbox); } freeph(&ph); return; } indexfile=ph.maildir; subhierarchy=index->records[n].name; } freeph(&ph); (*sharedhier_cb)(folderdir, index); } static void do_sharedhierlist(const char *folderdir, struct maildir_shindex_cache *index) { const char *p; const char *q; size_t n; struct publicfolderlist_helper ph; const char *folders_img; const char *name_inbox; int eof; char *url, *url2; p=strrchr(folderdir, '.'); if (p) ++p; else p=folderdir; folders_img=getarg("FOLDERSICON"); name_inbox=getarg("INBOX"); memset(&ph, 0, sizeof(ph)); printf("\n" "\n"); while (*q && *q != '.') ++q; url=malloc(q-folderdir+1); if (!url) enomem(); memcpy(url, folderdir, q-folderdir); url[q-folderdir]=0; for (n=0; index && nnrecords; n++) { freeph(&ph); if (n == 0) index->indexfile.startingpos=0; if ((n == 0 ? &maildir_newshared_nextAt: &maildir_newshared_next)(&index->indexfile, &eof, &do_publicfolderlist_cb, &ph) || eof) { break; } if (ph.homedir) { char *d=maildir_location(ph.homedir, ph.maildir); if (d) { if (maildir_info_suppress(d)) { free(d); continue; } free(d); } } printf("\n"); } free(url); freeph(&ph); printf("
%s<<< ", folders_img); if (strcmp(folderdir, NEWSHAREDSP) == 0) { printf(""); print_safe(name_inbox); printf(""); } else { printf("%s", getarg("PUBLICFOLDERS")); } for (q=folderdir; q"); free(s); s=malloc(r-q+1); if (!s) enomem(); memcpy(s, q, r-q); s[r-q]=0; list_folder(s); free(s); printf(""); } q=r; } printf("
%s" ">>> "); list_folder(url2); free(url2); printf("
\n"); } static void do_folderlist(const char *inbox_pfix, const char *homedir, const char *folderdir, const char *inbox_name) { const char *name_inbox; const char *name_drafts; const char *name_sent; const char *name_trash; const char *folder_img; const char *folders_img; const char *unread_label; const char *acl_img; char acl_buf[4]; char **folders; size_t i; unsigned nnew, nother; size_t folderdir_l; name_inbox=getarg("INBOX"); name_drafts=getarg("DRAFTS"); name_sent=getarg("SENT"); name_trash=getarg("TRASH"); folder_img=getarg("FOLDERICON"); folders_img=getarg("FOLDERSICON"); sqwebmail_folder=0; unread_label=getarg("UNREAD"); acl_img=maildir_newshared_disabled ? NULL : getarg("ACLICON"); printf("\n"); maildir_listfolders(inbox_pfix, homedir, &folders); if (*folderdir && strcmp(folderdir, INBOX)) { char *parentfolder; size_t i; char *q, *r; const char *c; if (strncmp(folderdir, SHARED ".", sizeof(SHARED)) == 0) { for (c=folderdir; *c; c++) if (*c == '.') break; r=malloc(strlen(inbox_pfix)+strlen(c)+1); if (!r) enomem(); strcat(strcpy(r, inbox_pfix), c); parentfolder=get_parent_folder(r); free(r); } else parentfolder=get_parent_folder(folderdir); for (q=parentfolder; *q; q++) if (*q == '.') break; printf("\n"); free(parentfolder); } else if (strcmp(inbox_pfix, INBOX)) { size_t i; char *p; char *q; printf("\n"); free(p); } if (!folderdir || strchr(folderdir, '.') == 0) { folderdir=inbox_pfix; } folderdir_l=strlen(folderdir); for (i=0; folders[i]; i++) { const char *p; const char *shortname=folders[i]; size_t j; const char *pfix; int isunsubscribed=0; const char *img=folder_img; pfix=">>>"; if (strncmp(shortname, SHARED ".", sizeof(SHARED)) == 0) { char *dir; struct stat stat_buf; pfix="+++"; dir=maildir_shareddir(".", shortname+sizeof(SHARED)); if (!dir) continue; if (stat(dir, &stat_buf)) isunsubscribed=1; free(dir); } if (strcmp(shortname, inbox_name) == 0 && strcmp(folderdir, inbox_name) == 0) { /* List INBOX at the top level */ strcpy(acl_buf, ACL_LOOKUP ACL_ADMINISTER); acl_computeRightsOnFolder(shortname, acl_buf); if (acl_buf[0] == 0) continue; } else { if (strcmp(folderdir, INBOX) == 0 && strncmp(shortname, SHARED ".", sizeof(SHARED)) == 0) { shortname += sizeof(SHARED); strcpy(acl_buf, ACL_LOOKUP); } else { if (memcmp(shortname, folderdir, folderdir_l) || shortname[folderdir_l] != '.') { continue; } strcpy(acl_buf, ACL_LOOKUP ACL_ADMINISTER); acl_computeRightsOnFolder(shortname, acl_buf); if (acl_buf[0] == 0) continue; shortname += folderdir_l; ++shortname; } if ((p=strchr(shortname, '.')) != 0) { char *s; char *t; unsigned tot_nnew, tot_nother; s=malloc(p-folders[i]+1); if (!s) enomem(); memcpy(s, folders[i], p-folders[i]); s[p-folders[i]]=0; printf("\n\n", tot_nnew + tot_nother); continue; } } nnew=0; nother=0; if (strchr(acl_buf, ACL_LOOKUP[0]) == NULL) isunsubscribed=1; if (!isunsubscribed) maildir_count(folders[i], &nnew, ¬her); printf("\n\n"); } maildir_freefolders(&folders); if (strcmp(folderdir, INBOX) == 0 && !maildir_newshared_disabled) { char *sp=cgiurlencode(NEWSHAREDSP); printf("\n\n", sp, getarg("PUBLICFOLDERS")); free(sp); } printf("
%s", folders_img); printf("<<< "); #if 0 printf(""); print_safe(inbox_name); printf(""); #endif i=0; while (parentfolder[i]) { char *p=strchr(parentfolder+i, '.'); int c; if (!p) p=parentfolder+strlen(parentfolder); c= *p; *p=0; if (strchr(parentfolder, '.')) printf("."); printf(""); if (strcmp(parentfolder, NEWSHAREDSP) == 0) printf("%s", getarg("PUBLICFOLDERS")); else list_folder_xlate(parentfolder, parentfolder+i, name_inbox, name_drafts, name_sent, name_trash); printf(""); if ( (*p=c) != 0) ++p; i=p-parentfolder; } printf("
%s<<< ", folders_img); p=strdup(inbox_pfix); if (!p) enomem(); if ((q=strrchr(p, '.')) != 0) *q=0; for (i=0; p[i]; ) { size_t j; char save_ch; for (j=i; p[j]; j++) if (p[j] == '.') break; save_ch=p[j]; p[j]=0; if (i) printf("."); printf(""); if (strcmp(p, NEWSHAREDSP) == 0) printf("%s", getarg("PUBLICFOLDERS")); else list_folder(p+i); printf(""); p[j]=save_ch; if (save_ch) ++j; i=j; } printf("
"); if (acl_img && strchr(acl_buf, ACL_ADMINISTER[0])) { printf("%s ", acl_img); } printf("%s%s ", folders_img, pfix); if (acl_buf[0]) { printf(""); } free(s); t=malloc(p-shortname+1); if (!t) enomem(); memcpy(t, shortname, p-shortname); t[p-shortname]=0; list_folder_xlate(folders[i], t, name_inbox, name_drafts, name_sent, name_trash); free(t); if (strchr(acl_buf, ACL_LOOKUP[0])) { printf(""); } tot_nnew=0; tot_nother=0; j=i; while (folders[j] && memcmp(folders[j], folders[i], p-folders[i]+1) == 0) { strcpy(acl_buf, ACL_LOOKUP ACL_READ); acl_computeRightsOnFolder(folders[j], acl_buf); if (acl_buf[0] == 0) { ++j; continue; } maildir_count(folders[j], &nnew, ¬her); ++j; tot_nnew += nnew; tot_nother += nother; } i=j-1; if (tot_nnew) { printf(" "); printf(unread_label, tot_nnew); printf(""); } printf("%d  
", isunsubscribed ? " class=\"folderunsubscribed\"":""); if (acl_img && strchr(acl_buf, ACL_ADMINISTER[0])) { printf("%s ", acl_img); } printf("%s  "); if (!isunsubscribed) { printf(""); } list_folder_xlate(folders[i], strcmp(folders[i], inbox_name) == 0 ? INBOX:shortname, name_inbox, name_drafts, name_sent, name_trash); if (!isunsubscribed) printf(""); if (nnew) { printf(" "); printf(unread_label, nnew); printf(""); } printf(""); if (!isunsubscribed) { printf("%d  ", nnew + nother); } else printf(" \n"); printf("
%s>>> %s" " 
\n"); } void folder_list2() { if (folder_err_msg) { printf("%s\n", folder_err_msg); } } static void folder_rename_dest_fake(const char *dummy1, struct maildir_shindex_cache *dummy2); static void folder_rename_dest_real(const char *inbox_pfix, const char *homedir, const char *cur_folder, const char *inbox_name); void folder_rename_list() { parse_hierarchy(cgi("folderdir"), folder_rename_dest_real, folder_rename_dest_fake); } static void folder_rename_dest_fake(const char *dummy1, struct maildir_shindex_cache *dummy2) { } static void folder_rename_dest_real(const char *inbox_pfix, const char *homedir, const char *cur_folder, const char *inbox_name) { char **folders; int i; size_t pl=strlen(inbox_pfix); printf("\n"); } void folder_download(const char *folder, size_t pos, const char *mimeid) { char *filename; FILE *fp=NULL; int fd; filename=get_msgfilename(folder, &pos); fd=maildir_semisafeopen(filename, O_RDONLY, 0); if (fd >= 0) { if ((fp=fdopen(fd, "r")) == 0) close(fd); } if (!fp) { free(filename); error("Message not found."); return; } free(filename); cginocache(); msg2html_download(fp, mimeid, *cgi("download") == '1', sqwebmail_content_charset); fclose(fp); } void folder_showtransfer() { const char *deletelab, *purgelab, *movelab, *golab; deletelab=getarg("DELETE"); purgelab=getarg("PURGE"); movelab=getarg("ORMOVETO"); golab=getarg("GO"); folder_inbox=getarg("INBOX"); folder_drafts=getarg("DRAFTS"); folder_trash=getarg("TRASH"); folder_sent=getarg("SENT"); printf("", cgi("pos")); if (*cgi("search")) { printf("" ""); } if ((strcmp(sqwebmail_folder, INBOX "." TRASH) == 0) && (strlen(getarg("PURGEALL")))) printf("", getarg("PURGEALL")); printf("%s\n", golab); } void folder_showquota() { const char *quotamsg; struct maildirsize quotainfo; quotamsg=getarg("QUOTAUSAGE"); if (maildir_openquotafile("ainfo, ".")) return; if (quotainfo.quota.nmessages != 0 || quotainfo.quota.nbytes != 0) printf(quotamsg, maildir_readquota("ainfo)); maildir_closequotafile("ainfo); } void folder_cleanup() { msg_purgelab=0; msg_folderlab=0; folder_drafts=0; folder_inbox=0; folder_sent=0; folder_trash=0; msg_forwardattlab=0; msg_forwardlab=0; msg_fullheaderlab=0; msg_golab=0; msg_movetolab=0; msg_nextlab=0; msg_prevlab=0; msg_deletelab=0; msg_posfile=0; msg_replyalllab=0; msg_replylistlab=0; msg_replylab=0; folder_err_msg=0; msg_msglab=0; msg_add=0; msg_type=0; initnextprevcnt=0; msg_hasprev=0; msg_hasnext=0; msg_pos=0; msg_count=0; } /* ** Unicode-aware truncation of text at a specified column, if text length ** exceeds the given # of characters. */ static char *truncate_at(const char *str, const char *charset, size_t ncols) { char32_t *uc; size_t n; size_t cols, tp=0; char *retbuf; unicode_convert_handle_t h; int chopped=0; if (!str) return NULL; h=unicode_convert_tou_init("utf-8", &uc, &n, 1); if (h) { unicode_convert(h, str, strlen(str)); unicode_convert_deinit(h, NULL); } else { uc=NULL; } if (!uc) return NULL; for (cols=0, n=0; uc[n]; n++) { cols += unicode_wcwidth(uc[n]); tp = n; if (cols > ncols-3 && n > 0 && unicode_grapheme_break(uc[n-1], uc[n])) { chopped=1; break; } ++tp; } if (chopped) { uc = realloc(uc, sizeof(char32_t) * (tp+4)); if (uc == 0) enomem(); uc[tp]='.'; uc[tp+1]='.'; uc[tp+2]='.'; tp += 3; } h=unicode_convert_fromu_init(charset, &retbuf, &cols, 1); if (h) { unicode_convert_uc(h, uc, tp); unicode_convert_deinit(h, NULL); } else { retbuf=NULL; } free(uc); return retbuf; }