/* ** Copyright 1998 - 2018 Double Precision, Inc. ** See COPYING for distribution information. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #if HAVE_FCNTL_H #include #endif #if HAVE_UNISTD_H #include #endif #include #if HAVE_SYS_STAT_H #include #endif #include #include #include "maildircreate.h" #include "maildirmisc.h" #include "maildirinfo.h" #include "maildirsharedrc.h" #include "maildirquota.h" #include "maildirfilter.h" #include static void usage() { printf("Usage: maildirmake [ options ] maildir\n"); exit(1); } extern FILE *maildir_shared_fopen(const char *, const char *); extern void maildir_shared_fparse(char *, char **, char **); static void add(const char *dir, const char *name) { char *c=malloc(strlen(name)+1); char *s; FILE *fp; char buf[BUFSIZ]; char *ptr, *dirp; if (!c) { perror("malloc"); exit(1); } strcpy(c, name); if ((s=strchr(c, '=')) == 0) usage(); *s++=0; if (*s != '/') usage(); if (access(s, R_OK)) { perror(s); exit(1); } if (strchr(c, '.') || strchr(c, '/')) { fprintf(stderr, "%s: invalid name\n", c); exit(1); } if (chdir(dir)) { perror(dir); exit(1); } if ((fp=fopen(MAILDIRSHAREDRC, "r")) != 0) { while ((ptr=fgets(buf, sizeof(buf), fp)) != 0) { char *namep; maildir_shared_fparse(buf, &namep, &dirp); if (!namep) continue; if (strcmp(namep, c) == 0) { fclose(fp); fprintf(stderr, "%s: already defined as a sharable maildir in %s.\n", namep, MAILDIRSHAREDRC); exit(2); } } fclose(fp); } if ((fp=maildir_shared_fopen(".", "a+")) == 0) { perror(dir); exit(1); } if (fseek(fp, 0L, SEEK_SET) < 0) { perror(dir); exit(1); } while ((ptr=fgets(buf, sizeof(buf), fp)) != 0) { char *namep; maildir_shared_fparse(buf, &namep, &dirp); if (!namep) continue; if (strcmp(namep, c) == 0) { fclose(fp); fprintf(stderr, "%s: already defined as a sharable maildir.\n", namep); exit(2); } } fprintf(fp, "%s\t%s\n", c, s); if (fflush(fp) || ferror(fp) || fclose(fp)) { perror("dir"); exit(1); } exit(0); } static void del(const char *dir, const char *n) { FILE *fp; char buf[BUFSIZ]; char buf2[BUFSIZ]; FILE *fp2; int found; struct maildir_tmpcreate_info createInfo; if (chdir(dir)) { perror(dir); exit(1); } if ((fp=maildir_shared_fopen(".", "r")) == 0) { perror(dir); exit(1); } maildir_tmpcreate_init(&createInfo); createInfo.uniq="shared"; createInfo.msgsize=0; createInfo.doordie=1; if ((fp2=maildir_tmpcreate_fp(&createInfo)) == NULL) { perror(dir); exit(1); } found=0; while (fgets(buf, sizeof(buf), fp)) { char *name, *dirp; strcpy(buf2, buf); maildir_shared_fparse(buf2, &name, &dirp); if (name && strcmp(name, n) == 0) { found=1; continue; } fprintf(fp2, "%s", buf); } fclose(fp); if (fflush(fp2) || ferror(fp2) || fclose(fp2) || rename(createInfo.tmpname, "shared-maildirs")) { perror(createInfo.tmpname); unlink(createInfo.tmpname); exit(1); } maildir_tmpcreate_free(&createInfo); if (!found) { fprintf(stderr, "%s: not found.\n", n); exit(1); } exit(0); } /***************************************************************************** Convert modified-UTF7 folder names to UTF-8 (sort-of). *****************************************************************************/ struct convertutf8_list { struct convertutf8_list *next; char *rename_from; char *rename_to; }; struct convertutf8_status { struct convertutf8_list *list; int status; }; /* Find folders that need to change */ static void convertutf8_build_list(const char *inbox_name, void *arg) { struct convertutf8_status *status= (struct convertutf8_status *)arg; struct convertutf8_list *list; char *converted=imap_foldername_to_filename(0, inbox_name); if (!converted) { fprintf(stderr, "Error: %s: does not appear to be valid" " modified-UTF7\n", inbox_name); status->status=1; return; } if (strcmp(converted, inbox_name) == 0) { free(converted); return; } list=(struct convertutf8_list *)malloc(sizeof(struct convertutf8_list)); if (!list || !(list->rename_from=strdup(inbox_name))) { perror("malloc"); exit(1); } list->rename_to=converted; list->next=status->list; status->list=list; } void convertutf8(const char *maildir, const char *mailfilter, int doit) { struct convertutf8_status status; struct convertutf8_list *list; char *courierimapsubscribed; char *courierimapsubscribed_new; FILE *courierimapsubscribed_fp; struct maildirfilter mf; int mf_status; char *mailfilter_newname=0; memset(&status, 0, sizeof(status)); memset(&mf, 0, sizeof(mf)); printf("Checking %s:\n", maildir); maildir_list(maildir, convertutf8_build_list, &status); if (status.status) exit(status.status); if (mailfilter) { struct maildirfilterrule *r; /* ** Try to convert folder references from mailfilter */ mailfilter_newname=malloc(strlen(mailfilter)+10); strcat(strcpy(mailfilter_newname, mailfilter), ".new"); mf_status=maildir_filter_loadrules(&mf, mailfilter); if (mf_status != MF_LOADOK && mf_status != MF_LOADNOTFOUND) { fprintf(stderr, "Error: cannot load %s\n", mailfilter); } for (r=mf.first; r; r=r->next) { char *converted; /* Look for deliveries to a folder */ if (strncmp(r->tofolder, "INBOX.", 6)) continue; converted=imap_foldername_to_filename(0, r->tofolder); if (!converted) { fprintf(stderr, "Error: %s: " "%s: does not appear to be valid " "modified-UTF7\n", mailfilter, r->tofolder); status.status=1; } if (strcmp(converted, r->tofolder) == 0) { free(converted); continue; } printf("Mail filter to %s updated to %s\n", r->tofolder, converted); free(r->tofolder); r->tofolder=converted; } if (mf_status == MF_LOADOK) { FILE *fp=fopen(mailfilter, "r"); char detected_from[1024]; char detected_tomaildir[1024]; char buffer[1024]; struct stat st_buf; /* ** We need to know the FROM address and the MAILDIR ** reference. Attempt to ham-fistedly parse the ** current filter file. */ detected_from[0]=0; detected_tomaildir[0]=0; if (!fp) { perror(mailfilter); exit(1); } while (fgets(buffer, sizeof(buffer), fp)) { char *p, *q; if (strncmp(buffer, "FROM='", 6) == 0) { char *p=strchr(buffer+6, '\''); if (!p) { fprintf(stderr, "Cannot parse %s\n", mailfilter); status.status=1; } else { *p=0; strcpy(detected_from, buffer+6); /* Unescape, because saverules() escapes it. */ for (q=p=detected_from; *p; p++) { if (*p == '\\' && p[1]) ++p; *q=*p; ++q; } *q=0; } } if (strncmp(buffer, "to \"", 4) == 0) { p=strstr(buffer+4, "/.\""); if (!p) { fprintf(stderr, "Cannot parse %s\n", mailfilter); status.status=1; } else { *p=0; } strcpy(detected_tomaildir, buffer+4); } } fclose(fp); if (detected_from[0] == 0 || detected_tomaildir[0] == 0) { fprintf(stderr, "Failed to parse %s\n", mailfilter); status.status=1; } maildir_filter_saverules(&mf, mailfilter_newname, detected_tomaildir, detected_from); if (stat(mailfilter, &st_buf) || chmod(mailfilter_newname, st_buf.st_mode)) { perror(mailfilter); exit(1); } /* ** If we're root, preserve the ownership and permission */ if (geteuid() == 0) { if (chown(mailfilter_newname, st_buf.st_uid, st_buf.st_gid)) { perror(mailfilter_newname); exit(1); } } } } else { mf_status=MF_LOADNOTFOUND; } courierimapsubscribed=malloc(strlen(maildir)+100); courierimapsubscribed_new=malloc(strlen(maildir)+100); strcat(strcpy(courierimapsubscribed, maildir), "/courierimapsubscribed"); strcat(strcpy(courierimapsubscribed_new, maildir), "/courierimapsubscribed.new"); /* ** Update folder references in the IMAP subscription file. */ if ((courierimapsubscribed_fp=fopen(courierimapsubscribed, "r")) != NULL) { char buffer[2048]; FILE *new_fp=fopen(courierimapsubscribed_new, "w"); char *converted; struct stat st_buf; while (fgets(buffer, sizeof(buffer), courierimapsubscribed_fp)) { char *p=strchr(buffer, '\n'); if (!p) { fprintf(stderr, "Error: courierimapsubscribed: " "folder name too long\n"); status.status=1; continue; } *p=0; converted=imap_foldername_to_filename(0, buffer); if (!converted) { fprintf(stderr, "Error: courierimapsubscribed: " "%s: does not appear to be valid " "modified-UTF7\n", buffer); status.status=1; continue; } fprintf(new_fp, "%s\n", converted); if (strcmp(buffer, converted)) { printf("Subscription to %s changed to %s\n", buffer, converted); } free(converted); } if (fflush(new_fp) || fclose(new_fp)) { exit(1); } fclose(courierimapsubscribed_fp); /* ** If we're root, preserve the ownership and permission */ if (stat(courierimapsubscribed, &st_buf) || chmod(courierimapsubscribed_new, st_buf.st_mode)) { perror(courierimapsubscribed); exit(1); } if (geteuid() == 0) { if (chown(courierimapsubscribed_new, st_buf.st_uid, st_buf.st_gid)) { perror(courierimapsubscribed_new); exit(1); } } } if (status.status) { unlink(courierimapsubscribed_new); if (mf_status == MF_LOADOK) unlink(mailfilter_newname); exit(status.status); } for (list=status.list; list; list=list->next) { char *frompath, *topath; printf("Rename %s to %s\n", list->rename_from, list->rename_to); frompath=malloc(strlen(maildir)+strlen(list->rename_from)); topath=malloc(strlen(maildir)+strlen(list->rename_to)); if (!frompath || !topath) { perror("malloc"); exit(1); } strcat(strcpy(frompath, maildir), "/"); strcat(strcpy(topath, maildir), "/"); /* They all have the INBOX. prefix, strip it off */ strcat(frompath, strchr(list->rename_from, '.')); strcat(topath, strchr(list->rename_to, '.')); if (doit) { if (rename(frompath, topath)) { fprintf(stderr, "FATAL ERROR RENAMING %s to %s: %s\n", frompath, topath, strerror(errno)); status.status=1; } } free(frompath); free(topath); } if (doit) { if (courierimapsubscribed_fp) { printf("Updating %s\n", courierimapsubscribed); if (rename(courierimapsubscribed_new, courierimapsubscribed)) { fprintf(stderr, "FATAL ERROR RENAMING %s to %s: %s\n", courierimapsubscribed_new, courierimapsubscribed, strerror(errno)); status.status=1; } } if (mf_status == MF_LOADOK) { printf("Updating %s\n", mailfilter); if (rename(mailfilter_newname, mailfilter)) { fprintf(stderr, "FATAL ERROR RENAMING %s to %s: %s\n", mailfilter_newname, mailfilter, strerror(errno)); status.status=1; } } } else { if (courierimapsubscribed_fp) { printf("Verified %s\n", courierimapsubscribed); } if (mf_status == MF_LOADOK) { printf("Verified %s\n", mailfilter); } } exit(status.status); } int main(int argc, char *argv[]) { const char *maildir, *folder=0; char *p; int argn; int perm=0700; int musthavefolder=0; int subdirperm; char *addshared=0, *delshared=0; const char *quota=0; char *tmp=0; for (argn=1; argn 1) { addshared=argv[++argn]; continue; } if (strcmp(argv[argn], "--del") == 0 && argc-argn > 1) { delshared=argv[++argn]; continue; } if (strcmp(argv[argn], "--convutf8") == 0 && argc-argn > 1) { char *maildir=argv[++argn]; char *mailfilter=argn < argc ? argv[++argn]:0; convertutf8(maildir, mailfilter, 1); exit(0); } if (strcmp(argv[argn], "--checkutf8") == 0 && argc-argn > 1) { char *maildir=argv[++argn]; char *mailfilter=argn < argc ? argv[++argn]:0; convertutf8(maildir, mailfilter, 0); exit(0); } usage(); } if (argn == argc) usage(); maildir=argv[argn]; if (addshared) { add(maildir, addshared); exit (0); } if (delshared) { del(maildir, delshared); exit (0); } if (folder && *folder == '.') { printf("Invalid folder name: %s\n", folder); exit(1); } if (folder) { if ((p=(char *)malloc(strlen(maildir)+strlen(folder)+4)) == 0) { perror("maildirmake malloc"); exit(1); } strcat(strcat(strcpy(p, maildir), "/."), folder); maildir=p; } else if (musthavefolder) usage(); if (quota) { struct stat stat_buf; if (stat(maildir, &stat_buf) < 0 && errno == ENOENT) { if (maildir_make(maildir, perm & ~0022, (perm & ~0022), 0) < 0) { perror(maildir); exit(1); } } maildir_quota_set(maildir, quota); exit(0); } subdirperm=perm; if (!folder) subdirperm=0700; umask(0); if (maildir_make(maildir, perm & ~0022, subdirperm, folder ? 1:0) < 0) { tmp=(char *)malloc(strlen(maildir)+14); if (!tmp) { perror("maildirmake malloc"); exit(1); } snprintf(tmp, 14+strlen(maildir), "maildirmake: %s", maildir); perror(tmp); exit(1); } exit(0); return (0); }