diff -rc alpine-2.24/alpine/alpine.c alpine-2.24.maildir/alpine/alpine.c *** alpine-2.24/alpine/alpine.c 2020-10-10 00:24:28.216554908 -0600 --- alpine-2.24.maildir/alpine/alpine.c 2020-10-10 00:26:49.964167282 -0600 *************** *** 591,596 **** --- 591,601 ---- if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global)) mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE); + #ifndef _WINDOWS + rv = F_ON(F_COURIER_FOLDER_LIST, ps_global) ? 1 : 0; + mail_parameters(NULL,SET_COURIERSTYLE, (void *) &rv); + #endif + rvl = 0L; if(pine_state->VAR_NNTPRANGE){ if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)) diff -rc alpine-2.24/alpine/confscroll.c alpine-2.24.maildir/alpine/confscroll.c *** alpine-2.24/alpine/confscroll.c 2020-10-10 00:24:28.216554908 -0600 --- alpine-2.24.maildir/alpine/confscroll.c 2020-10-10 00:26:49.968167384 -0600 *************** *** 5567,5572 **** --- 5567,5578 ---- (void *)var->current_val.p); } #endif + #ifndef _WINDOWS + else if(var == &ps->vars[V_MAILDIR_LOCATION]){ + if(var->current_val.p && var->current_val.p[0]) + mail_parameters(NULL, SET_MDINBOXPATH, (void *)var->current_val.p); + } + #endif else if(revert && standard_radio_var(ps, var)){ cur_rule_value(var, TRUE, FALSE); diff -rc alpine-2.24/imap/src/c-client/mail.c alpine-2.24.maildir/imap/src/c-client/mail.c *** alpine-2.24/imap/src/c-client/mail.c 2020-10-10 00:24:28.200554500 -0600 --- alpine-2.24.maildir/imap/src/c-client/mail.c 2020-10-10 00:26:49.968167384 -0600 *************** *** 1063,1069 **** MAILSTREAM *ts; char *s,*t,tmp[MAILTMPLEN]; size_t i; ! DRIVER *d; /* never allow names with newlines */ if ((s = strpbrk (mailbox,"\015\012")) != NULL) { MM_LOG ("Can't create mailbox with such a name",ERROR); --- 1063,1069 ---- MAILSTREAM *ts; char *s,*t,tmp[MAILTMPLEN]; size_t i; ! DRIVER *d, *md; /* never allow names with newlines */ if ((s = strpbrk (mailbox,"\015\012")) != NULL) { MM_LOG ("Can't create mailbox with such a name",ERROR); *************** *** 1087,1092 **** --- 1087,1094 ---- return NIL; } + /* Hack, we should do this better, but it works */ + for (md = maildrivers; md && strcmp (md->name, "md"); md = md->next); /* see if special driver hack */ if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) && ((mailbox[2] == 'r') || (mailbox[2] == 'R')) && *************** *** 1117,1122 **** --- 1119,1131 ---- (((*mailbox == '{') || (*mailbox == '#')) && (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) d = stream->dtb; + else if(mailbox[0] == '#' + && (mailbox[1] == 'm' || mailbox[1] == 'M') + && (mailbox[2] == 'd' || mailbox[2] == 'D' + || mailbox[2] == 'c' || mailbox[2] == 'C') + && mailbox[3] == '/' + && mailbox[4] != '\0') + return (*md->create)(stream, mailbox); else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; else { /* failed utterly */ sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); diff -rc alpine-2.24/imap/src/c-client/mail.h alpine-2.24.maildir/imap/src/c-client/mail.h *** alpine-2.24/imap/src/c-client/mail.h 2020-10-10 00:24:28.200554500 -0600 --- alpine-2.24.maildir/imap/src/c-client/mail.h 2020-10-10 00:26:49.968167384 -0600 *************** *** 383,388 **** --- 383,392 ---- #define SET_SCANCONTENTS (long) 573 #define GET_MHALLOWINBOX (long) 574 #define SET_MHALLOWINBOX (long) 575 + #define GET_COURIERSTYLE (long) 576 + #define SET_COURIERSTYLE (long) 577 + #define SET_MDINBOXPATH (long) 578 + #define GET_MDINBOXPATH (long) 579 /* Driver flags */ diff -rc alpine-2.24/imap/src/osdep/unix/dummy.c alpine-2.24.maildir/imap/src/osdep/unix/dummy.c *** alpine-2.24/imap/src/osdep/unix/dummy.c 2020-10-10 00:24:28.192554297 -0600 --- alpine-2.24.maildir/imap/src/osdep/unix/dummy.c 2020-10-10 00:26:49.972167485 -0600 *************** *** 104,116 **** * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ ! DRIVER *dummy_valid (char *name) { ! char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ ! if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { --- 104,122 ---- * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ ! char * maildir_remove_root(char *); DRIVER *dummy_valid (char *name) { ! char *s,tmp[MAILTMPLEN], *rname; struct stat sbuf; + + if(strlen(name) > MAILTMPLEN) + name[MAILTMPLEN] = '\0'; + + strcpy(tmp, name); + rname = maildir_remove_root(tmp); /* must be valid local mailbox */ ! if (rname && *rname && (*rname != '{') && (s = mailboxfile (tmp,rname))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { *************** *** 119,126 **** return &dummydriver; } /* blackbox INBOX does not exist yet */ ! else if (!compare_cstring (name,"INBOX")) return &dummydriver; } return NIL; } --- 125,133 ---- return &dummydriver; } /* blackbox INBOX does not exist yet */ ! else if (!compare_cstring (rname,"INBOX")) return &dummydriver; } + if(rname) fs_give((void **)&rname); return NIL; } *************** *** 453,458 **** --- 460,467 ---- { char *s,tmp[MAILTMPLEN]; long ret = NIL; + if(!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4)) + return maildir_create(stream, mailbox); /* validate name */ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); *************** *** 518,523 **** --- 527,540 ---- { struct stat sbuf; char *s,tmp[MAILTMPLEN]; + if (!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4) + || is_valid_maildir(&mailbox)){ + char tmp[MAILTMPLEN] = {'\0'}; + strcpy(tmp, mailbox); + if(tmp[strlen(tmp) - 1] != '/') + tmp[strlen(tmp)] = '/'; + return maildir_delete(stream, tmp); + } if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); MM_LOG (tmp,ERROR); *************** *** 543,554 **** long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; ! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; /* no trailing / allowed */ ! if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { ! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); MM_LOG (mbx,ERROR); return NIL; } --- 560,582 ---- long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; ! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN], *rold, *rnewname; ! ! if(strlen(old) > MAILTMPLEN) ! old[MAILTMPLEN] = '\0'; ! ! if(strlen(newname) > MAILTMPLEN) ! newname[MAILTMPLEN] = '\0'; ! ! strcpy(tmp, old); ! rold = maildir_remove_root(tmp); ! strcpy(tmp, newname); ! rnewname = maildir_remove_root(tmp); /* no trailing / allowed */ ! if (!dummy_file (oldname,rold) || !(s = dummy_file (mbx,rnewname)) || stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { ! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",rold,rnewname); MM_LOG (mbx,ERROR); return NIL; } *************** *** 564,577 **** } } /* rename of non-ex INBOX creates dest */ ! if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { ! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } --- 592,607 ---- } } /* rename of non-ex INBOX creates dest */ ! if (!compare_cstring (rold,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { ! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",rold,rnewname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } + if(rold) fs_give((void **)&rold); + if(rnewname) fs_give((void **)&rnewname); return T; /* return success */ } diff -rc alpine-2.24/imap/src/osdep/unix/maildir.c alpine-2.24.maildir/imap/src/osdep/unix/maildir.c *** alpine-2.24/imap/src/osdep/unix/maildir.c 2020-10-10 00:26:50.012168504 -0600 --- alpine-2.24.maildir/imap/src/osdep/unix/maildir.c 2020-10-10 00:26:49.972167485 -0600 *************** *** 0 **** --- 1,2671 ---- + /* + * Maildir driver for Alpine 2.20 + * + * Written by Eduardo Chappa + * Last Update: June 10, 2014 + * + */ + + #include + #include + #include + extern int errno; /* just in case */ + #include "mail.h" + #include + #include + #include + #include "osdep.h" + #include "rfc822.h" + #include "fdstring.h" + #include "misc.h" + #include "dummy.h" + #include "maildir.h" + + /* Driver dispatch used by MAIL */ + DRIVER maildirdriver = { + "md", /* driver name, yes it's md, not maildir */ + DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ + (DRIVER *) NIL, /* next driver */ + maildir_valid, /* mailbox is valid for us */ + maildir_parameters, /* manipulate parameters */ + NIL, /* scan mailboxes */ + maildir_list, /* find mailboxes */ + maildir_lsub, /* find subscribed mailboxes */ + maildir_sub, /* subscribe to mailbox */ + maildir_unsub, /* unsubscribe from mailbox */ + maildir_create, /* create mailbox */ + maildir_delete, /* delete mailbox */ + maildir_rename, /* rename mailbox */ + mail_status_default, /* status of mailbox */ + maildir_open, /* open mailbox */ + maildir_close, /* close mailbox */ + maildir_fast, /* fetch message "fast" attributes */ + NIL, /* fetch message flags */ + NIL, /* fetch overview */ + NIL, /* fetch message structure */ + maildir_header, /* fetch message header */ + maildir_text, /* fetch message body */ + NIL, /* fetch partial message text */ + NIL, /* unique identifier */ + NIL, /* message number */ + NIL, /* modify flags */ + maildir_flagmsg, /* per-message modify flags */ + NIL, /* search for message based on criteria */ + NIL, /* sort messages */ + NIL, /* thread messages */ + maildir_ping, /* ping mailbox to see if still alive */ + maildir_check, /* check for new messages */ + maildir_expunge, /* expunge deleted messages */ + maildir_copy, /* copy messages to another mailbox */ + maildir_append, /* append string message to mailbox */ + NIL /* garbage collect stream */ + }; + + + DRIVER courierdriver = { + "mc", /* Why a separate driver? So that createproto will work */ + DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ + (DRIVER *) NIL, /* next driver */ + maildir_valid, /* mailbox is valid for us */ + maildir_parameters, /* manipulate parameters */ + NIL, /* scan mailboxes */ + courier_list, /* find mailboxes */ + maildir_lsub, /* find subscribed mailboxes */ + maildir_sub, /* subscribe to mailbox */ + maildir_unsub, /* unsubscribe from mailbox */ + maildir_create, /* create mailbox */ + maildir_delete, /* delete mailbox */ + maildir_rename, /* rename mailbox */ + mail_status_default, /* status of mailbox */ + maildir_open, /* open mailbox */ + maildir_close, /* close mailbox */ + maildir_fast, /* fetch message "fast" attributes */ + NIL, /* fetch message flags */ + NIL, /* fetch overview */ + NIL, /* fetch message structure */ + maildir_header, /* fetch message header */ + maildir_text, /* fetch message body */ + NIL, /* fetch partial message text */ + NIL, /* unique identifier */ + NIL, /* message number */ + NIL, /* modify flags */ + maildir_flagmsg, /* per-message modify flags */ + NIL, /* search for message based on criteria */ + NIL, /* sort messages */ + NIL, /* thread messages */ + maildir_ping, /* ping mailbox to see if still alive */ + maildir_check, /* check for new messages */ + maildir_expunge, /* expunge deleted messages */ + maildir_copy, /* copy messages to another mailbox */ + maildir_append, /* append string message to mailbox */ + NIL /* garbage collect stream */ + }; + + MAILSTREAM maildirproto = {&maildirdriver}; /* prototype stream */ + MAILSTREAM courierproto = {&courierdriver}; /* prototype stream */ + + long maildir_dirfmttest (char *name) + { + int i; + for (i = 0; mdstruct[i] && strcmp(name, mdstruct[i]); i++); + return (i < EndDir) || !strcmp(name, MDDIR) + || !strncmp(name, MDUIDLAST, strlen(MDUIDLAST)) + || !strncmp(name, MDUIDTEMP, strlen(MDUIDTEMP)) ? LONGT : NIL; + } + + void + md_domain_name(void) + { + int i, j; + + strcpy(mdlocaldomain, mylocalhost ()); + for (i = 0; mdlocaldomain[i] != '\0' ;) + if(mdlocaldomain[i] == '/' || mdlocaldomain[i] == ':'){ + for(j = strlen(mdlocaldomain); j >= i; j--) + mdlocaldomain[j+4] = mdlocaldomain[j]; + mdlocaldomain[i++] = '\\'; + mdlocaldomain[i++] = '0'; + if(mdlocaldomain[i] == '/'){ + mdlocaldomain[i++] = '5'; + mdlocaldomain[i++] = '7'; + } else { + mdlocaldomain[i++] = '7'; + mdlocaldomain[i++] = '2'; + } + } + else + i++; + } + + char * + myrootdir(char *name) + { + return myhomedir(); + } + + char * + mdirpath(void) + { + char *path = maildir_parameters(GET_MDINBOXPATH, NIL); + return path ? (path[0] ? path : ".") : "Maildir"; + } + + /* remove the "#md/" or "#mc/" part from a folder name + * memory freed by caller + */ + char * + maildir_remove_root (char *name) + { + int courier = IS_COURIER(name), offset; + char realname[MAILTMPLEN]; + + offset = maildir_valid_name(name) ? (name[3] == '/' ? 4 : 3) : 0; + if(courier) + courier_realname(name+offset, realname); + else + strcpy(realname, name+offset); + return cpystr(realname); + } + + + /* Check validity of the name, we accept: + * a) #md/directory/folder + * b) #md/inbox + * A few considerations: We can only accept as valid + * a) names that start with #md/ and the directory exists or + * b) names that do not start with #md/ but are maildir directories (have + * the /cur, /tmp and /new structure) + */ + int maildir_valid_name (char *name) + { + char tmpname[MAILTMPLEN] = {'\0'}; + + if (mdfpath) + fs_give((void **)&mdfpath); + if (name && (name[0] != '#')) + snprintf(tmpname, sizeof(tmpname), "%s%s",MDPREFIX(CCLIENT), name); + mdfpath = cpystr(tmpname[0] ? tmpname : name); + + return IS_CCLIENT(name) || IS_COURIER(name); + } + + /* Check if the directory whose path is given by name is a valid maildir + * directory (contains /cur, /tmp and /new) + */ + int maildir_valid_dir (char *name) + { + int len; + DirNamesType i; + struct stat sbuf; + char tmp[MAILTMPLEN]; + + if(name[strlen(name) - 1] == '/') + name[strlen(name) - 1] = '\0'; + len = strlen(name); + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, name, i); + if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) + break; + } + name[len] = '\0'; + return (i == EndDir) ? T : NIL; + } + + void courier_realname(char *name, char *realname) + { + int i,j; + + if(!name) + return; + + for (i = 0, j = 0; i < MAILTMPLEN && j < strlen(name); j++, i++){ + realname[i] = name[j]; + if(name[j] == '/' && name[j+1] != '.' && name[j+1] != '%' + && name[j+1] != '*') + realname[++i] = '.'; + } + if(realname[i-1] == '.') + i--; + realname[i] = '\0'; + } + + + /* given a maildir folder, return its path. Memory freed by caller. Directory + * does not contain the trailing slash "/". On error NULL is returned. + */ + int maildir_file_path (char *name, char *tmp, size_t sizeoftmp) + { + char *maildirpath = mdirpath(), *rname; + int courier = IS_COURIER(name); + + /* There are several ways in which the path can come, so we will handle + them here. First we deal with #mc/ or #md/ prefix by removing the + prefix, if any */ + + if(strlen(name) >= MAILTMPLEN) + name[MAILTMPLEN] = '\0'; + strcpy(tmp, name); + rname = maildir_remove_root(tmp); + tmp[0] = '\0'; /* just in case something fails */ + + if (strlen(myrootdir(rname)) + + max(strlen(rname), strlen(maildirpath)) > sizeoftmp){ + errno = ENAMETOOLONG; + snprintf(tmp, sizeoftmp, "Error opening \"%s\": %s", rname, strerror (errno)); + mm_log(tmp,ERROR); + if(rname) fs_give((void **)&rname); + return NIL; + } + + /* There are two ways in which the name can come here, either as a + full path or not. If it is not a full path it can come in two ways, + either as a file system path (Maildir/.Drafts) or as a maildir path + (INBOX.Drafts) + */ + + if(*rname == '/'){ /* full path */ + strncpy(tmp, rname, sizeoftmp); /* do nothing */ + tmp[sizeoftmp-1] = '\0'; + } + else + snprintf (tmp, sizeoftmp, "%s/%s%s%s", myrootdir (rname), + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? rname : maildirpath, + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? "" : (courier ? "/" : ""), + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? "" : (*(rname+5) == MDSEPARATOR(courier) ? rname+5 : "")); + if(rname) fs_give((void **)&rname); + return tmp[0] ? T : NIL; + } + + /* This function is given a full path for a mailbox and returns + * if it is a valid maildir transformed to canonical notation + */ + int + is_valid_maildir (char **name) + { + if (!strncmp(*name, myrootdir (*name), strlen(myrootdir(*name)))){ + (*name) += strlen(myrootdir(*name)); + if (**name == '/') (*name)++; + } + return maildir_valid(*name) ? T : NIL; + } + + /* Check validity of mailbox. This routine does not send errors to log, other + * routines calling this one may do so, though + */ + + DRIVER *maildir_valid (char *name) + { + char tmpname[MAILTMPLEN]; + + maildir_file_path(name, tmpname, sizeof(tmpname)); + + return maildir_valid_dir(tmpname) + ? (IS_COURIER(name) ? &courierdriver : &maildirdriver) : NIL; + } + + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags) + { + unsigned long i; + MESSAGECACHE *elt; + /* get sequence */ + if (stream && LOCAL && ((flags & FT_UID) ? + mail_uid_sequence (stream,sequence) : + mail_sequence (stream,sequence))) + for (i = 1L; i <= stream->nmsgs; i++) { + if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) && + !(elt->day && elt->rfc822_size)) { + ENVELOPE **env = NIL; + ENVELOPE *e = NIL; + if (!stream->scache) env = &elt->private.msg.env; + else if (stream->msgno == i) env = &stream->env; + else env = &e; + if (!*env || !elt->rfc822_size) { + STRING bs; + unsigned long hs; + char *ht = (*stream->dtb->header) (stream,i,&hs,NIL); + + if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST, + stream->dtb->flags); + if (!elt->rfc822_size) { + (*stream->dtb->text) (stream,i,&bs,FT_PEEK); + elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs); + } + } + + if (!elt->day && *env && (*env)->date) + mail_parse_date (elt,(*env)->date); + + if (!elt->day) elt->day = elt->month = 1; + mail_free_envelope (&e); + } + } + } + + int + maildir_eliminate_duplicate (char *name, struct direct ***flist, unsigned long *nfiles) + { + int i, j, k, error = 0, scanr; + char new[MAILTMPLEN], old[MAILTMPLEN], tmp[MAILTMPLEN], *str; + struct direct **names = NIL; + + if((scanr = maildir_doscandir(name, &names, CCLIENT)) < 0) + return -1; + + if(nfiles) *nfiles = scanr; + for(i = 0, j = 1, k = 0; j < scanr; i++, j++){ + if(k) + names[i] = names[i+k]; + if(same_maildir_file(names[i]->d_name, names[j]->d_name)){ + int d, f, r, s; + maildir_getflag(names[i]->d_name, &d, &f, &r, &s, NIL); + snprintf(old, sizeof(old), "%s/%s", name, names[i]->d_name); + snprintf(new, sizeof(new), "%s/.%s", name, names[i]->d_name); + if(rename(old, new) < 0 && errno != EEXIST) + error++; + if(!error){ + for(; j < scanr + && same_maildir_file(names[i]->d_name, names[j]->d_name) + ; j++, k++){ + maildir_getflag(names[j]->d_name, (d ? NIL : &d), + (f ? NIL : &f), (r ? NIL : &r), (s ? NIL : &s), NIL); + snprintf(tmp, sizeof(tmp), "%s/%s", name, names[j]->d_name); + if(unlink(tmp) < 0){ /* Hmmm... a problem, let's see */ + struct stat sbuf; + if (stat(tmp, &sbuf) == 0 && (sbuf.st_mode & S_IFMT) == S_IFREG) + error++; + } + } + if((str = strrchr(names[i]->d_name,FLAGSEP)) != NULL) *str = '\0'; + snprintf (old, sizeof(old), "%s/%s%s%s%s%s%s", name, names[i]->d_name, MDSEP(2), + MDFLAG(Draft, d), MDFLAG(Flagged, f), MDFLAG(Replied, r), + MDFLAG(Seen, s)); + if(rename(new, old) < 0) + error++; + } + } + + } + if(k > 0) + fs_give((void **)&names); + else + *flist = names; + return error ? -1 : k; + } + + int + maildir_doscandir(char *name, struct direct ***flist, int flag) + { + return scandir(name, flist, + flag == CCLIENT ? maildir_select : courier_dir_select, + flag == CCLIENT ? maildir_namesort : courier_dir_sort); + } + + /* + * return all files in a given directory. This is a separate call + * so that if there are warnings during compilation this only appears once. + */ + unsigned long + maildir_scandir (char *name, struct direct ***flist, + unsigned long *nfiles, int *scand, int flag) + { + struct stat sbuf; + int rv = -2; /* impossible value */ + + if (scand) + *scand = -1; /* assume error for safety */ + *nfiles = 0; + if((stat(name,&sbuf) < 0) + || (flag == CCLIENT + && ((rv = maildir_eliminate_duplicate(name, flist, nfiles)) < 0))) + return 0L; + + if (scand && (rv > 0 || rv == -2)) + *nfiles = maildir_doscandir(name, flist, flag); + + if(scand) *scand = *nfiles; + + return (unsigned long) sbuf.st_ctime; + } + + /* Does a message with given name exists (or was it removed)? + * Returns: 1 - yes, such message exist, + * 0 - No, that message does not exist anymore + * + * Parameters: stream, name of mailbox, new name if his message does not + * exist. + */ + + int maildir_message_exists(MAILSTREAM *stream, char *name, char *newfile) + { + char tmp[MAILTMPLEN]; + int gotit = NIL; + DIR *dir; + struct direct *d; + struct stat sbuf; + + /* First check directly if it exists, if not there, look for it */ + snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], name); + if ((stat(tmp, &sbuf) == 0) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) + return T; + + if (!(dir = opendir (LOCAL->path[Cur]))) + return NIL; + + while ((d = readdir(dir)) && gotit == NIL){ + if (d->d_name[0] == '.') + continue; + if (same_maildir_file(d->d_name, name)){ + gotit = T; + strcpy(newfile, d->d_name); + } + } + closedir(dir); + return gotit; + } + + /* Maildir open */ + + MAILSTREAM *maildir_open (MAILSTREAM *stream) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + if (!stream) return &maildirproto; + if (stream->local) fatal ("maildir recycle stream"); + md_domain_name(); /* get domain name for maildir files in mdlocaldomain */ + if(mypid == (pid_t) 0) + mypid = getpid(); + if (!stream->rdonly){ + stream->perm_seen = stream->perm_deleted = stream->perm_flagged = + stream->perm_answered = stream->perm_draft = T; + } + stream->local = (MAILDIRLOCAL *) fs_get (sizeof (MAILDIRLOCAL)); + memset(LOCAL, 0, sizeof(MAILDIRLOCAL)); + LOCAL->fd = -1; + + LOCAL->courier = IS_COURIER(stream->mailbox); + strcpy(tmp, stream->mailbox); + if (maildir_file_path (stream->mailbox, tmp, sizeof(tmp))) + LOCAL->dir = cpystr (tmp); + LOCAL->candouid = maildir_can_assign_uid(stream); + maildir_read_uid(stream, &stream->uid_last, &stream->uid_validity); + if (LOCAL->dir){ + LOCAL->path = (char **) fs_get(EndDir*sizeof(char *)); + MDFLD(tmp, LOCAL->dir, Cur); LOCAL->path[Cur] = cpystr (tmp); + MDFLD(tmp, LOCAL->dir, New); LOCAL->path[New] = cpystr (tmp); + MDFLD(tmp, LOCAL->dir, Tmp); LOCAL->path[Tmp] = cpystr (tmp); + if (stat (LOCAL->path[Cur],&sbuf) < 0) { + snprintf (tmp, sizeof(tmp), "Can't open folder %s: %s", + stream->mailbox,strerror (errno)); + mm_log (tmp,ERROR); + maildir_close(stream, 0); + return NIL; + } + } + + if(maildir_file_path (stream->mailbox, tmp, sizeof(tmp))){ + fs_give ((void **) &stream->mailbox); + stream->mailbox = cpystr(tmp); + } + + LOCAL->buf = (char *) fs_get (CHUNKSIZE); + LOCAL->buflen = CHUNKSIZE - 1; + stream->sequence++; + stream->nmsgs = stream->recent = 0L; + + maildir_parse_folder(stream, 1); + + return stream; + } + + /* Maildir initial parsing of the folder */ + void + maildir_parse_folder (MAILSTREAM *stream, int full) + { + char tmp[MAILTMPLEN]; + struct direct **namescur = NIL, **namesnew = NIL; + unsigned long i, nfilescur = 0L, nfilesnew = 0L, oldpos, newpos, total; + int scan_err, rescan, loop = 0; + + if (!stream) /* what??? */ + return; + + MM_CRITICAL(stream); + + maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); + if (scan_err < 0) + maildir_abort(stream); + + /* Scan old messages first, escoba! */ + if(stream->rdonly || + (LOCAL && ((maildir_initial_check(stream, Cur) == 0) + || nfilesnew > 0L))){ + LOCAL->scantime = maildir_scandir (LOCAL->path[Cur], &namescur, &nfilescur, + &scan_err, CCLIENT); + if (scan_err < 0){ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + maildir_abort(stream); + } + } + if(LOCAL && (maildir_initial_check(stream, New) == 0) + && (nfilescur > 0L)){ + while(LOCAL && loop < 10){ + if(nfilesnew == 0L) + maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); + if (scan_err < 0){ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + maildir_abort(stream); + break; + } + for(i = 0L, rescan = 0, newpos = oldpos = 0L; + newpos < nfilescur && i < nfilesnew; i++){ + if(maildir_message_in_list(namesnew[i]->d_name, namescur, oldpos, + nfilescur - 1L, &newpos)){ + oldpos = newpos; + snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[New], namesnew[i]->d_name); + if(unlink(tmp) < 0) + scan_err = -1; + rescan++; + } + else + newpos = oldpos; + } + if(scan_err < 0) + maildir_abort(stream); + if(rescan == 0) + break; + else{ /* restart */ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + nfilesnew = 0L; + loop++; + } + } + } + if(loop == 10) + maildir_abort(stream); + if(LOCAL){ + if(stream->rdonly) + stream->recent = 0L; + total = namescur || stream->rdonly + ? maildir_parse_dir(stream, 0L, Cur, namescur, + nfilescur, full) : stream->nmsgs; + stream->nmsgs = maildir_parse_dir(stream, total, New, namesnew, + nfilesnew, full); + } + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + if(namescur){ + for(i = 0L; i < nfilescur; i++) + fs_give((void **)&namescur[i]); + fs_give((void **) &namescur); + } + MM_NOCRITICAL(stream); + } + + int + maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype) + { + char *tmp; + struct stat sbuf; + + if (access (LOCAL->path[dirtype], R_OK|W_OK|X_OK) != 0){ + maildir_abort(stream); + return -1; + } + + if (dirtype != New && + (stat(LOCAL->path[Cur], &sbuf) < 0 || sbuf.st_ctime == LOCAL->scantime)) + return -1; + return 0; + } + + + /* Return the number of messages in the directory, while filling the + * elt structure. + */ + + unsigned long + maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, + DirNamesType dirtype, struct direct **names, + unsigned long nfiles, int full) + { + char tmp[MAILTMPLEN], file[MAILTMPLEN], newfile[MAILTMPLEN], *mdstr; + struct stat sbuf; + unsigned long i, new = 0L, l, uid_last; + unsigned long recent = stream ? stream->recent : 0L; + int d = 0, f = 0, r = 0, s = 0, t = 0; + int we_compute, in_list; + int silent = stream ? stream->silent : NIL; + MESSAGECACHE *elt; + + if (dirtype == Cur && !stream->rdonly) + for (i = 1L; i <= stream->nmsgs;){ + elt = mail_elt(stream, i); + in_list = elt && elt->private.spare.ptr && nfiles > 0L + ? (MDPOS(elt) < nfiles + ? same_maildir_file(MDFILE(elt), names[MDPOS(elt)]->d_name) + : NIL) + || maildir_message_in_list(MDFILE(elt), names, 0L, + nfiles - 1L, &MDPOS(elt)) + : NIL; + if (!in_list){ + if (elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + + if (elt->recent) --recent; + mail_expunged(stream,i); + } + else i++; + } + + stream->silent = T; + uid_last = 0L; + for (we_compute = 0, i = l = 1L; l <= nfiles; l++){ + unsigned long pos, uid; + if (dirtype == New && !stream->rdonly){ /* move new messages to cur */ + pos = l - 1L; + snprintf (file, sizeof(file), "%s/%s", LOCAL->path[New], names[pos]->d_name); + if(lstat(file,&sbuf) == 0) + switch(sbuf.st_mode & S_IFMT){ + case S_IFREG: + strcpy(tmp, names[pos]->d_name); + if((mdstr = strstr(tmp,MDSEP(3))) + || (mdstr = strstr(tmp,MDSEP(2)))) + *(mdstr+1) = '2'; + else + strcat(tmp, MDSEP(2)); + snprintf(newfile, sizeof(newfile), "%s/%s", LOCAL->path[Cur], tmp); + if(rename (file, newfile) != 0){ + mm_log("Unable to read new mail!", WARN); + continue; + } + unlink (file); + new++; + break; + case S_IFLNK: /* clean up, clean up, everybody, everywhere */ + if(unlink(file) < 0){ + if(LOCAL->link == NIL){ + mm_log("Unable to remove symbolic link", WARN); + LOCAL->link = T; + } + } + continue; + break; + default: + if(LOCAL && LOCAL->link == NIL){ + mm_log("Unrecognized file or link in folder", WARN); + LOCAL->link = T; + } + continue; + break; + } + } + mail_exists(stream, i + nmsgs); + elt = mail_elt(stream, i + nmsgs); + pos = (elt && elt->private.spare.ptr) ? MDPOS(elt) : l - 1L; + if (dirtype == New) elt->recent = T; + maildir_getflag(names[pos]->d_name, &d, &f, &r ,&s, &t); + if (elt->private.spare.ptr) + maildir_free_file_only ((void **)&elt->private.spare.ptr); + else{ + maildir_get_file((MAILDIRFILE **)&elt->private.spare.ptr); + we_compute++; + } + MDFILE(elt) = cpystr(names[pos]->d_name); + MDPOS(elt) = pos; + MDLOC(elt) = dirtype; + if (dirtype == Cur){ /* deal with UIDs */ + if(elt->private.uid == 0L) + elt->private.uid = maildir_get_uid(MDFILE(elt)); + if(elt->private.uid <= uid_last){ + uid = (we_compute ? uid_last : stream->uid_last) + 1L; + if(LOCAL->candouid) + maildir_assign_uid(stream, i + nmsgs, uid); + else + elt->private.uid = uid; + } + else + uid = elt->private.uid; + uid_last = uid; + if(uid_last > stream->uid_last) + stream->uid_last = uid_last; + } + if(dirtype == New && !stream->rdonly){ + maildir_free_file_only((void **)&elt->private.spare.ptr); + MDFILE(elt) = cpystr(tmp); + MDSIZE(elt) = sbuf.st_size; + MDMTIME(elt) = sbuf.st_mtime; + MDLOC(elt) = Cur; + } + if (elt->draft != d || elt->flagged != f || + elt->answered != r || elt->seen != s || elt->deleted != t){ + elt->draft = d; elt->flagged = f; elt->answered = r; + elt->seen = s; elt->deleted = t; + if (!we_compute && !stream->rdonly) + MM_FLAGS(stream, i+nmsgs); + } + maildir_get_date(stream, i+nmsgs); + elt->valid = T; + i++; + } + stream->silent = silent; + if(LOCAL->candouid && dirtype == Cur) + maildir_read_uid(stream, NULL, &stream->uid_validity); + if (dirtype == New && stream->rdonly) + new = nfiles; + mail_exists(stream, nmsgs + ((dirtype == New) ? new : nfiles)); + mail_recent(stream, recent + ((dirtype == New) ? new : 0L)); + + return (nmsgs + (dirtype == New ? new : nfiles)); + } + + long maildir_ping (MAILSTREAM *stream) + { + maildir_parse_folder(stream, 0); + if(stream && LOCAL){ + if(LOCAL->candouid < 0) + LOCAL->candouid++; + else if(LOCAL->candouid) + maildir_uid_renew_tempfile(stream); + else /* try again to get uids */ + LOCAL->candouid = maildir_can_assign_uid(stream); + } + return stream && LOCAL ? LONGT : NIL; + } + + int maildir_select (const struct direct *name) + { + return (name->d_name[0] != '.'); + } + + /* + * Unfortunately, there is no way to sort by arrival in this driver, this + * means that opening a folder in this driver using the scandir function + * will always make this driver slower than any driver that has a natural + * way of sorting by arrival (like a flat file format, "mbox", "mbx", etc). + */ + int maildir_namesort (const struct direct **d1, const struct direct **d2) + { + const struct direct *e1 = *(const struct direct **) d1; + const struct direct *e2 = *(const struct direct **) d2; + + return comp_maildir_file((char *) e1->d_name, (char *) e2->d_name); + } + + /* Maildir close */ + + void maildir_close (MAILSTREAM *stream, long options) + { + MESSAGECACHE *elt; + unsigned long i; + int silent = stream ? stream->silent : 0; + mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); + + if (!stream) return; + + for (i = 1L; i <= stream->nmsgs; i++) + if((elt = (MESSAGECACHE *) (*mc)(stream,i,CH_ELT)) && elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + stream->silent = T; + if (options & CL_EXPUNGE) maildir_expunge (stream, NIL, NIL); + maildir_abort(stream); + if (mdfpath) fs_give((void **)&mdfpath); + if (mypid) mypid = (pid_t) 0; + stream->silent = silent; + } + + void maildir_check (MAILSTREAM *stream) + { + if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL); + } + + long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags) + { + char tmp[MAILTMPLEN]; + unsigned long i; + MESSAGECACHE *elt; + char *s; + /* UID call "impossible" */ + if (flags & FT_UID || !LOCAL) return NIL; + elt = mail_elt (stream, msgno); + + if (!(flags & FT_PEEK) && !elt->seen){ + elt->seen = T; + maildir_flagmsg (stream, elt); + MM_FLAGS(stream, elt->msgno); + } + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0 && (errno == EACCES || errno == ENOENT)){ + INIT (bs, mail_string, "", 0); + elt->rfc822_size = 0L; + return NIL; + } + + s = maildir_text_work(stream, elt, &i, flags); + INIT (bs, mail_string, s, i); + return LONGT; + } + + char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, + unsigned long *length,long flags) + { + FDDATA d; + STRING bs; + char *s,tmp[CHUNK]; + unsigned long msgno = elt->msgno; + static int try = 0; + + if (length) + *length = 0L; + LOCAL->buf[0] = '\0'; + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0){ /* flag change? */ + if (try < 5){ + try++; + if (maildir_update_elt_maildirp(stream, msgno) > 0) + try = 0; + return maildir_text_work(stream, mail_elt(stream, msgno),length, flags); + } + try = 0; + return NULL; + } + + lseek (LOCAL->fd, elt->private.msg.text.offset,L_SET); + + if (flags & FT_INTERNAL) { /* initial data OK? */ + if (elt->private.msg.text.text.size > LOCAL->buflen) { + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = + elt->private.msg.text.text.size) + 1); + } + read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); + LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; + } + else { + if (elt->rfc822_size > LOCAL->buflen) { + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); + } + d.fd = LOCAL->fd; /* yes, set up file descriptor */ + d.pos = elt->private.msg.text.offset; + d.chunk = tmp; /* initial buffer chunk */ + d.chunksize = CHUNK; + INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); + for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) { + case '\r': /* carriage return seen */ + *s++ = SNX (&bs); /* copy it and any succeeding LF */ + if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); + break; + case '\n': + *s++ = '\r'; /* insert a CR */ + default: + *s++ = SNX (&bs); /* copy characters */ + } + *s = '\0'; /* tie off buffer */ + *length = s - (char *) LOCAL->buf; /* calculate length */ + } + close(LOCAL->fd); LOCAL->fd = -1; + return LOCAL->buf; + } + + /* maildir parse, fill the elt structure... well not all of it... */ + unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, + DirNamesType dirtype) + { + char *b, *s, *t, c; + char tmp[MAILTMPLEN]; + struct stat sbuf; + unsigned long i, len; + int d, f, r, se, dt; + MESSAGECACHE *elt; + + elt = mail_elt (stream,msgno); + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), dirtype); + if(stat(tmp, &sbuf) == 0) + MDSIZE(elt) = sbuf.st_size; + + maildir_get_date(stream, msgno); + maildir_getflag(MDFILE(elt), &d, &f, &r ,&se, &dt); + elt->draft = d; elt->flagged = f; elt->answered = r; elt->seen = se; + elt->deleted = dt; elt->valid = T; + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd >= 0){ + s = (char *) fs_get (MDSIZE(elt) + 1); + read (LOCAL->fd,s,MDSIZE(elt)); + s[MDSIZE(elt)] = '\0'; + t = s + strlen(s); /* make t point to the end of s */ + for (i = 0L, b = s; b < t && !(i && (*b == '\n')); i = (*b++ == '\n')); + len = (*b ? ++b : b) - s; + elt->private.msg.header.text.size = + elt->private.msg.text.offset = len; + elt->private.msg.text.text.size = MDSIZE(elt) - len; + for (i = 0L, b = s, c = *b; b && + ((c < '\016' && ((c == '\012' && ++i) + ||(c == '\015' && *(b+1) == '\012' && ++b && (i +=2)))) + || b < t); i++, c= *++b); + elt->rfc822_size = i; + fs_give ((void **) &s); + close(LOCAL->fd); LOCAL->fd = -1; + } + return elt->rfc822_size; + } + + int + maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno) + { + struct direct **names = NIL; + unsigned long i, nfiles, pos; + int d = 0, f = 0 , r = 0, s = 0, t = 0, in_list, scan_err; + MESSAGECACHE *elt; + + maildir_scandir (LOCAL->path[Cur], &names, &nfiles, &scan_err, CCLIENT); + + elt = mail_elt (stream,msgno); + + in_list = nfiles > 0L + ? maildir_message_in_list(MDFILE(elt), names, 0L, nfiles - 1L, &pos) + : NIL; + + if (in_list && pos >= 0L && pos < nfiles + && !strcmp(MDFILE(elt), names[pos]->d_name)){ + in_list = NIL; + maildir_abort(stream); + } + + if (in_list && pos >= 0L && pos < nfiles){ + maildir_free_file_only((void **)&elt->private.spare.ptr); + MDFILE(elt) = cpystr(names[pos]->d_name); + maildir_getflag(MDFILE(elt), &d, &f, &r ,&s, &t); + if (elt->draft != d || elt->flagged != f || + elt->answered != r || elt->seen != s || elt->deleted != t){ + elt->draft = d; elt->flagged = f; elt->answered = r; + elt->seen = s; elt->deleted = t; + MM_FLAGS(stream, msgno); + } + } + for (i = 0L; i < nfiles; i++) + fs_give((void **) &names[i]); + if (names) + fs_give((void **) &names); + return in_list ? 1 : -1; + } + + /* Maildir fetch message header */ + + char *maildir_header (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags) + { + char tmp[MAILTMPLEN], *s; + MESSAGECACHE *elt; + static int try = 0; + + if (length) *length = 0; + if (flags & FT_UID || !LOCAL) return ""; /* UID call "impossible" */ + elt = mail_elt (stream,msgno); + if(elt->private.msg.header.text.size == 0) + maildir_parse_message(stream, msgno, MDLOC(elt)); + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) + LOCAL->fd = open (tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0 && errno == EACCES){ + mm_log ("Message exists but can not be read. Envelope and body lost!",ERROR); + return NULL; + } + + if (LOCAL->fd < 0){ /* flag change? */ + if (try < 5){ + try++; + if (maildir_update_elt_maildirp(stream, msgno) > 0) + try = 0; + return maildir_header(stream, msgno, length, flags); + } + try = 0; + return NULL; + } + + if (flags & FT_INTERNAL){ + if(elt->private.msg.header.text.size > LOCAL->buflen){ + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = + elt->private.msg.header.text.size) + 1); + } + read (LOCAL->fd, (void *)LOCAL->buf, elt->private.msg.header.text.size); + LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; + } + else{ + s = (char *) fs_get(elt->private.msg.header.text.size+1); + read (LOCAL->fd, (void *)s, elt->private.msg.header.text.size); + s[elt->private.msg.header.text.size] = '\0'; + *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, + elt->private.msg.header.text.size); + fs_give ((void **) &s); + } + elt->private.msg.text.offset = elt->private.msg.header.text.size; + elt->private.msg.text.text.size = MDSIZE(elt) - elt->private.msg.text.offset; + close(LOCAL->fd); LOCAL->fd = -1; + return LOCAL->buf; + } + + /* Maildir find list of subscribed mailboxes + * Accepts: mail stream + * pattern to search + */ + + void maildir_list (MAILSTREAM *stream,char *ref, char *pat) + { + char *s,test[MAILTMPLEN],file[MAILTMPLEN]; + long i = 0L; + + if((!pat || !*pat) && maildir_canonicalize (test,ref,"*") + && maildir_valid_name(test)){ /* there is a #md/ leading here */ + for (i = 3L; test[i] && test[i] != '/'; i++); + if ((s = strchr (test+i+1,'/')) != NULL) *++s = '\0'; + else test[0] = '\0'; + mm_list (stream,'/',test, LATT_NOSELECT); + } + else if (maildir_canonicalize (test,ref,pat)) { + if (test[3] == '/') { /* looking down levels? */ + /* yes, found any wildcards? */ + if ((s = strpbrk (test,"%*")) != NULL){ + /* yes, copy name up to that point */ + strncpy (file,test+4,i = s - (test+4)); + file[i] = '\0'; /* tie off */ + } + else strcpy (file,test+4);/* use just that name then */ + /* find directory name */ + if ((s = strrchr (file, '/')) != NULL){ + *s = '\0'; /* found, tie off at that point */ + s = file; + } + /* do the work */ + if(IS_COURIER(test)) + courier_list_work (stream,s,test,0); + else + maildir_list_work (stream,s,test,0); + } + /* always an INBOX */ + if (!compare_cstring (test,"#MD/INBOX")) + mm_list (stream,NIL,"#MD/INBOX",LATT_NOINFERIORS); + if (!compare_cstring (test,"#MC/INBOX")) + mm_list (stream,NIL,"#MC/INBOX",LATT_NOINFERIORS); + } + } + + void courier_list (MAILSTREAM *stream,char *ref, char *pat) + { + /* I am too lazy to do anything. Do you care to ask maildir list, please? + The real reason why this is a dummy function is because we do not want to + see the same folder listed twice. + */ + } + + /* For those that want to hide things, we give them a chance to do so */ + void *maildir_parameters (long function, void *value) + { + void *ret = NIL; + switch ((int) function) { + case SET_MDINBOXPATH: + if(strlen((char *) value ) > 49) + strcpy(myMdInboxDir, "Maildir"); + else + strcpy(myMdInboxDir, (char *) value); + case GET_MDINBOXPATH: + if (myMdInboxDir[0] == '\0') strcpy(myMdInboxDir,"Maildir"); + ret = (void *) myMdInboxDir; + break; + case SET_COURIERSTYLE: + CourierStyle = (long) value; + case GET_COURIERSTYLE: + ret = (void *) CourierStyle; + break; + case GET_DIRFMTTEST: + ret = (void *) maildir_dirfmttest; + break; + default: + break; + } + return ret; + } + + int maildir_create_folder(char *mailbox) + { + char tmp[MAILTMPLEN], err[MAILTMPLEN]; + DirNamesType i; + + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, mailbox, i); + if (mkdir(tmp, 0700) && errno != EEXIST){ /* try to make new dir */ + snprintf (err, sizeof(err), "Can't create %s: %s", tmp, strerror(errno)); + mm_log (err,ERROR); + return NIL; + } + } + return T; + } + + int maildir_create_work(char *mailbox, int loop) + { + char *s, c, err[MAILTMPLEN], tmp[MAILTMPLEN], tmp2[MAILTMPLEN], mbx[MAILTMPLEN]; + int fnlen, create_dir = 0, courier, mv; + struct stat sbuf; + long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL); + + courier = IS_COURIER(mailbox); + strcpy(mbx, mailbox); + mv = maildir_valid(mbx) ? 1 : 0; + maildir_file_path(mailbox, tmp, sizeof(tmp)); + if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ + create_dir++; + mailbox[strlen(mailbox) - 1] = '\0'; + } + + if(!loop && courier){ + if(mv){ + if(create_dir){ + if(style == CCLIENT) + strcpy (err,"Can not create directory: folder exists. Create subfolder"); + else + strcpy(err,"Folder and Directory already exist"); + } + else + strcpy (err, "Can't create mailbox: mailbox already exists"); + } + else{ + if(create_dir) + strcpy(err, "Can not create directory. Cread folder instead"); + else + err[0] = '\0'; + } + if(err[0]){ + mm_log (err,ERROR); + return NIL; + } + } + + fnlen = strlen(tmp); + if ((s = strrchr(mailbox,MDSEPARATOR(courier))) != NULL){ + c = *++s; + *s = '\0'; + if ((stat(tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && + !maildir_create_work (mailbox, ++loop)) + return NIL; + *s = c; + } + tmp[fnlen] = '\0'; + + if (mkdir(tmp,0700) && errno != EEXIST) + return NIL; + + if (create_dir) + mailbox[fnlen] = '/'; + + if (create_dir){ + if(style == CCLIENT){ + if(!courier){ + FILE *fp = NULL; + snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, MDDIR); + if ((fp = fopen(tmp2,"w")) == NULL){ + snprintf (err, sizeof(err), "Problem creating %s: %s", tmp2, strerror(errno)); + mm_log (err,ERROR); + return NIL; + } + fclose(fp); + } + } + return T; + } + else + return maildir_create_folder(tmp); + } + + long maildir_create (MAILSTREAM *stream,char *mailbox) + { + char tmp[MAILTMPLEN], err[MAILTMPLEN]; + int rv, create_dir; + + create_dir = mailbox ? + (mailbox[strlen(mailbox) - 1] == + MDSEPARATOR(IS_COURIER(mailbox))) : 0; + maildir_file_path(mailbox, tmp, sizeof(tmp)); + strcpy(tmp, mailbox); + rv = maildir_create_work(mailbox, 0); + strcpy(mailbox, tmp); + if (rv == 0){ + snprintf (err, sizeof(err), "Can't create %s %s", + (create_dir ? "directory" : "mailbox"), mailbox); + mm_log (err,ERROR); + } + return rv ? LONGT : NIL; + } + + #define MAXTRY 10000 + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) + { + char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN]; + char *s; + int ren, try = 0; + + if (elt->valid){ + for (try = 1; try > 0 && try < MAXTRY; try++){ + /* build the new filename */ + snprintf (oldfile, sizeof(oldfile), "%s/%s",LOCAL->path[Cur], MDFILE(elt)); + fn[0] = '\0'; + if ((ren = maildir_message_exists(stream, MDFILE(elt), fn)) == 0){ + errno = ENOENT; + try = MAXTRY; + } + if (*fn) /* new oldfile! */ + snprintf (oldfile,sizeof(oldfile),"%s/%s", LOCAL->path[Cur], fn); + if ((s = strrchr (MDFILE(elt), FLAGSEP))) *s = '\0'; + snprintf (fn, sizeof(fn), "%s%s%s%s%s%s%s", MDFILE(elt), MDSEP(2), + MDFLAG(Draft, elt->draft), MDFLAG(Flagged, elt->flagged), + MDFLAG(Replied, elt->answered), MDFLAG(Seen, elt->seen), + MDFLAG(Trashed, elt->deleted)); + snprintf (newfile, sizeof(newfile), "%s/%s",LOCAL->path[Cur],fn); + if (ren != 0 && rename (oldfile,newfile) >= 0) + try = -1; + } + + if (try > 0){ + snprintf(oldfile, sizeof(oldfile), "Unable to write flags to disk: %s", + (errno == ENOENT) ? "message is gone!" : strerror (errno)); + mm_log(oldfile,ERROR); + return; + } + #ifdef __CYGWIN__ + utime(LOCAL->path[Cur], NIL); /* make sure next scan will catch the change */ + #endif + maildir_free_file_only ((void **) &elt->private.spare.ptr); + MDFILE(elt) = cpystr (fn); + } + } + + long maildir_expunge (MAILSTREAM *stream, char *sequence, long options) + { + long ret; + MESSAGECACHE *elt; + unsigned long i, n = 0L; + unsigned long recent = stream->recent; + char tmp[MAILTMPLEN]; + + mm_critical (stream); /* go critical */ + ret = sequence ? ((options & EX_UID) ? + mail_uid_sequence (stream,sequence) : + mail_sequence (stream,sequence)) : LONGT; + if(ret == 0L) + return 0L; + for (i = 1L; i <= stream->nmsgs;){ + elt = mail_elt (stream,i); + if (elt->deleted && (sequence ? elt->sequence : T)){ + snprintf (tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], MDFILE(elt)); + if (unlink (tmp) < 0) {/* try to delete the message */ + snprintf (tmp, sizeof(tmp), "Expunge of message %ld failed, aborted: %s",i, + strerror (errno)); + if (!stream->silent) + mm_log (tmp,WARN); + break; + } + if (elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + if (elt->recent) --recent;/* if recent, note one less recent message */ + mail_expunged (stream,i); /* notify upper levels */ + n++; /* count up one more expunged message */ + } + else i++; + } + if(n){ /* output the news if any expunged */ + snprintf (tmp, sizeof(tmp), "Expunged %ld messages", n); + if (!stream->silent) + mm_log (tmp,(long) NIL); + } + else + if (!stream->silent) + mm_log ("No messages deleted, so no update needed",(long) NIL); + mm_nocritical (stream); /* release critical */ + /* notify upper level of new mailbox size */ + mail_exists (stream, stream->nmsgs); + mail_recent (stream, recent); + return ret; + } + + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) + { + STRING st; + MESSAGECACHE *elt; + unsigned long len; + int fd; + unsigned long i; + struct stat sbuf; + char tmp[MAILTMPLEN], flags[MAILTMPLEN], path[MAILTMPLEN], *s; + /* copy the messages */ + if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : + mail_sequence (stream,sequence)) + for (i = 1L; i <= stream->nmsgs; i++) + if ((elt = mail_elt (stream,i))->sequence){ + MSGPATH(path, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (((fd = open (path,O_RDONLY,NIL)) < 0) + ||((!elt->rfc822_size && + ((stat(path, &sbuf) < 0) || !S_ISREG (sbuf.st_mode))))) + return NIL; + if(!elt->rfc822_size) + MDSIZE(elt) = sbuf.st_size; + s = (char *) fs_get(MDSIZE(elt) + 1); + read (fd,s,MDSIZE(elt)); + s[MDSIZE(elt)] = '\0'; + close (fd); + len = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen, s, MDSIZE(elt)); + INIT (&st,mail_string, LOCAL->buf, len); + elt->rfc822_size = len; + fs_give ((void **)&s); + + flags[0] = flags[1] = '\0'; + if (elt->seen) strcat (flags," \\Seen"); + if (elt->draft) strcat (flags," \\Draft"); + if (elt->deleted) strcat (flags," \\Deleted"); + if (elt->flagged) strcat (flags," \\Flagged"); + if (elt->answered) strcat (flags," \\Answered"); + flags[0] = '('; /* open list */ + strcat (flags,")"); /* close list */ + mail_date (tmp,elt); /* generate internal date */ + if (!mail_append_full (NIL, mailbox, flags, tmp, &st)) + return NIL; + if (options & CP_MOVE) elt->deleted = T; + } + return LONGT; /* return success */ + } + + long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) + { + int fd, k, done, fail; + STRING *message; + char c,*s, *flags, *date; + char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; + MESSAGECACHE elt; + long i, size = 0L, ret = LONGT, f; + unsigned long uf, ti; + static unsigned int transact = 0; + struct stat sbuf; + + if (!maildir_valid(mailbox)) { + snprintf (tmp, sizeof(tmp), "Not a valid Maildir mailbox: %s", mailbox); + mm_log (tmp,ERROR); + return NIL; + } + + if (!*mdlocaldomain) + md_domain_name(); /* get domain name for maildir files in mdlocaldomain now! */ + + if (mypid == (pid_t) 0) + mypid = getpid(); + + if (!stream){ + stream = &maildirproto; + + for (k = 0; k < NUSERFLAGS && stream->user_flags[k]; ++k) + fs_give ((void **) &stream->user_flags[k]); + } + + if (!(*af)(stream, data, &flags, &date, &message)) return NIL; + + mm_critical (stream); /* go critical */ + do { + fail = done = 0; /* we have not determined name of message file yet */ + if (!SIZE (message)) { /* guard against zero-length */ + mm_log ("Append of zero-length message", ERROR); + ret = NIL; + break; + } + + if (date && !mail_parse_date(&elt,date)){ + snprintf (tmp, sizeof(tmp), "Bad date in append: %.80s", date); + mm_log (tmp, ERROR); + ret = NIL; + break; + } + + if(date){ + struct tm tm; + + tm.tm_sec = elt.seconds; + tm.tm_min = elt.minutes; + tm.tm_hour = elt.hours; + tm.tm_mday = elt.day; + tm.tm_mon = elt.month - 1; + tm.tm_year = BASEYEAR + elt.year - 1900; + + ti = mktime(&tm); + } else ti = time(0); + + f = mail_parse_flags (stream,flags,&uf); + do { + /* build file name we will use */ + snprintf (file, sizeof(file), "%lu.%d_%09u.%s%s%s%s%s%s", + ti, mypid, transact++, mdlocaldomain, (f ? MDSEP(2) : ""), + MDFLAG(Draft, f&fDRAFT), MDFLAG(Flagged, f&fFLAGGED), + MDFLAG(Replied, f&fANSWERED), MDFLAG(Seen, f&fSEEN)); + /* build tmp file name */ + if (maildir_file_path(mailbox, tmp, sizeof(tmp))) /* copy in TMP */ + MSGPATH(path1, tmp, file, Tmp); + /* build final filename to use */ + if (maildir_file_path(mailbox, tmp, sizeof(tmp))) + MSGPATH(path2, tmp, file, New); /* copy in NEW */ + if(stat(path1, &sbuf) < 0 && errno == ENOENT + && stat(path2, &sbuf) < 0 && errno == ENOENT) + done++; + else + fail++; + if(fail == 1000){ + snprintf (tmp, sizeof(tmp), "Failure to create append message name"); + mm_log (tmp, ERROR); + return NIL; + } + } while (done == 0); + + if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { + snprintf (tmp, sizeof(tmp), "Can't open append mailbox: %s", strerror (errno)); + mm_log (tmp, ERROR); + return NIL; + } + for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) + if ((c = SNX (message)) != '\015') s[size++] = c; + if ((write (fd, s, size) < 0) || fsync (fd)) { + unlink (path1); /* delete message */ + snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); + mm_log (tmp, ERROR); + ret = NIL; + } + fs_give ((void **) &s); /* flush the buffer */ + close (fd); /* close the file */ + + if (rename (path1,path2) < 0) { + snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); + mm_log (tmp, ERROR); + ret = NIL; + } + unlink (path1); + if(date){ + time_t tp[2]; + tp[0] = tp[1] = ti; + utime (path2,tp); + } + + if (ret) + if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; + + } while (ret && message); /* write the data */ + mm_nocritical (stream); /* release critical */ + return ret; + } + + long maildir_delete (MAILSTREAM *stream,char *mailbox) + { + DIR *dirp; + struct direct *d; + int i, remove_dir = 0, mddir = 0, rv, error = 0; + char tmp[MAILTMPLEN],tmp2[MAILTMPLEN], realname[MAILTMPLEN]; + struct stat sbuf; + int courier = IS_COURIER(mailbox); + + if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ + remove_dir++; + mailbox[strlen(mailbox) -1] = '\0'; + } + + if (!maildir_valid(mailbox)){ + maildir_file_path(mailbox, tmp, sizeof(tmp)); + if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)){ + snprintf(tmp, sizeof(tmp), "Can not remove %s", mailbox); + error++; + } + } + + if (!error && remove_dir && !maildir_dir_is_empty(mailbox)){ + snprintf(tmp, sizeof(tmp), "Can not remove directory %s/: directory not empty", mailbox); + error++; + } + + if(error){ + mm_log (tmp,ERROR); + return NIL; + } + + maildir_close(stream,0); /* even if stream was NULL */ + + maildir_file_path(mailbox, realname, sizeof(realname)); + + if (remove_dir){ + snprintf(tmp, sizeof(tmp), "%s/%s", realname, MDDIR); + if ((rv = stat (tmp,&sbuf)) == 0 && S_ISREG(sbuf.st_mode)) + rv = unlink(tmp); + else if (errno == ENOENT) + rv = 0; + if (rv != 0){ + snprintf(tmp, sizeof(tmp), "Can not remove %s/%s: %s", tmp2, MDDIR, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + if (!maildir_valid(realname) && rmdir(realname) != 0){ + snprintf(tmp, sizeof(tmp), "Can not remove %s/: %s", mailbox, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; + } + return LONGT; + } + /* else remove just the folder. Remove all hidden files, except MDDIR */ + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, realname, i); + + if (!(dirp = opendir (tmp))){ + snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", mailbox, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; + } + + while ((d = readdir(dirp)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")){ + snprintf(tmp2, sizeof(tmp2), "%s/%s", tmp, d->d_name); + if (unlink(tmp2) != 0){ + snprintf(tmp2, sizeof(tmp2), "Can not remove %s: %s", mailbox, strerror(errno)); + mm_log (tmp2, ERROR); + return NIL; + } + } + } + closedir(dirp); + if (rmdir(tmp) != 0){ + snprintf(tmp, sizeof(tmp), "Can not remove %s: %s", mailbox, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; + } + } + /* + * ok we have removed all subdirectories of the folder mailbox, Remove the + * hidden files. + */ + + if(!(dirp = opendir (realname))){ + snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", realname, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; + } + + while ((d = readdir(dirp)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && (!strcmp(d->d_name, MDDIR) + || !strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST)) + || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)))){ + if(strcmp(d->d_name, MDDIR) == 0) + mddir++; + snprintf(tmp, sizeof(tmp), "%s/%s", realname, d->d_name); + if (unlink(tmp) != 0) + error++; + } + } + closedir(dirp); + if (error || + (maildir_dir_is_empty(mailbox) && mddir == 0 && rmdir(realname) < 0)){ + snprintf(tmp, sizeof(tmp), "Can not remove folder %s: %s", mailbox, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; + } + return LONGT; + } + + long maildir_rename (MAILSTREAM *stream, char *old, char *new) + { + char tmp[MAILTMPLEN], tmpnew[MAILTMPLEN], realold[MAILTMPLEN]; + char realnew[MAILTMPLEN]; + int courier = IS_COURIER(old) && IS_COURIER(new); + int i; + long rv = LONGT; + COURIER_S *cdir; + + if((IS_COURIER(old) || IS_COURIER(new)) && !courier){ + snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s", old, new); + mm_log (tmp, ERROR); + return NIL; + } + + if (!maildir_valid(old)){ + snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s: folder not in maildir format",old); + mm_log (tmp, ERROR); + return NIL; + } + maildir_file_path(old, realold, sizeof(realold)); + if (!maildir_valid_name(new) && new[0] == '#'){ + snprintf (tmp, sizeof(tmp), "Cannot rename mailbox %s: folder not in maildir format", new); + mm_log (tmp, ERROR); + return NIL; + } + maildir_file_path(new, realnew, sizeof(realnew)); + if (access(tmpnew,F_OK) == 0){ /* new mailbox name must not exist */ + snprintf (tmp, sizeof(tmp), "Cannot rename to mailbox %s: destination already exists", new); + mm_log (tmp, ERROR); + return NIL; + } + + if(!courier){ + if (rename(realold, realnew)){ /* try to rename the directory */ + snprintf(tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, + strerror(errno)); + mm_log(tmp,ERROR); + return NIL; + } + return LONGT; /* return success */ + } + + cdir = courier_list_dir(old); + for (i = 0; cdir && i < cdir->total; i++){ + if(strstr(cdir->data[i]->name, old)){ + snprintf(tmp, sizeof(tmp), "%s%s", new, cdir->data[i]->name+strlen(old)); + maildir_file_path(cdir->data[i]->name, realold, sizeof(realold)); + maildir_file_path(tmp, realnew, sizeof(realnew)); + if (rename(realold, realnew)){ + snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, + strerror(errno)); + mm_log(tmp,ERROR); + rv = NIL; + } + } + } + courier_free_cdir(&cdir); + return rv; + } + + long maildir_sub(MAILSTREAM *stream,char *mailbox) + { + return sm_subscribe(mailbox); + } + + long maildir_unsub(MAILSTREAM *stream,char *mailbox) + { + return sm_unsubscribe(mailbox); + } + + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat) + { + void *sdb = NIL; + char *s, test[MAILTMPLEN], tmp[MAILTMPLEN]; + /* get canonical form of name */ + if (maildir_canonicalize (test, ref, pat) && (s = sm_read (tmp, &sdb))) { + do if (pmatch_full (s, test, '/')) mm_lsub (stream, '/', s, NIL); + while ((s = sm_read (tmp, &sdb)) != NULL); /* until no more subscriptions */ + } + } + + long maildir_canonicalize (char *pattern,char *ref,char *pat) + { + if (ref && *ref) { /* have a reference */ + strcpy (pattern,ref); /* copy reference to pattern */ + /* # overrides mailbox field in reference */ + if (*pat == '#') strcpy (pattern,pat); + /* pattern starts, reference ends, with / */ + else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) + strcat (pattern,pat + 1); /* append, omitting one of the period */ + + else strcat (pattern,pat); /* anything else is just appended */ + } + else strcpy (pattern,pat); /* just have basic name */ + return maildir_valid_name(pattern) ? LONGT : NIL; + } + + void maildir_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) + { + DIR *dp; + struct direct *d; + struct stat sbuf; + char curdir[MAILTMPLEN],name[MAILTMPLEN], tmp[MAILTMPLEN]; + char realpat[MAILTMPLEN]; + long i; + char *maildirpath = mdirpath(); + + snprintf(curdir, sizeof(curdir), "%s/%s/", myrootdir(pat), dir ? dir : maildirpath); + if ((dp = opendir (curdir)) != NULL){ + if (dir) snprintf (name, sizeof(name), "%s%s/",MDPREFIX(CCLIENT),dir); + else strcpy (name, pat); + + if (level == 0 && !strpbrk(pat,"%*")){ + if(maildir_valid(pat)){ + i = maildir_contains_folder(pat, NULL) + ? LATT_HASCHILDREN + : (maildir_is_dir(pat, NULL) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS); + maildir_file_path(pat, realpat, sizeof(realpat)); + i += maildir_any_new_msgs(realpat) + ? LATT_MARKED : LATT_UNMARKED; + mm_list (stream,'/', pat, i); + } + else + if(pat[strlen(pat) - 1] == '/') + mm_list (stream,'/', pat, LATT_NOSELECT); + } + + while ((d = readdir (dp)) != NULL) + if(strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New))){ + + if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); + else strcpy(tmp, d->d_name); + + if(pmatch_full (tmp, pat,'/')){ + snprintf(tmp, sizeof(tmp), "%s/%s/%s", myrootdir(d->d_name), + (dir ? dir : maildirpath), d->d_name); + if(stat (tmp,&sbuf) == 0 + && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){ + if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); + else strcpy(tmp, d->d_name); + i = maildir_valid(tmp) + ? (maildir_contains_folder(dir, d->d_name) + ? LATT_HASCHILDREN + : (maildir_is_dir(dir, d->d_name) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS)) + : LATT_NOSELECT; + i += maildir_any_new_msgs(tmp) + ? LATT_MARKED : LATT_UNMARKED; + mm_list (stream,'/',tmp, i); + strcat (tmp, "/"); + if(dmatch (tmp, pat,'/') && + (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))){ + snprintf(tmp, sizeof(tmp), "%s/%s",dir,d->d_name); + maildir_list_work (stream,tmp,pat,level+1); + } + } + } + } + closedir (dp); + } + } + + void courier_list_work (MAILSTREAM *stream, char *dir, char *pat, long level) + { + char c, curdir[MAILTMPLEN], tmp[MAILTMPLEN]; + char realname[MAILTMPLEN], realpat[MAILTMPLEN] = {'\0'}; + int i, found; + long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL), j; + char *maildirpath = mdirpath(); + COURIER_S *cdir; + + if(!strpbrk(pat,"%*")){ /* a mailbox */ + maildir_file_path(pat, curdir, sizeof(curdir)); + i = strlen(curdir) - 1; + if(curdir[i] == '/') + curdir[i] = '\0'; + cdir = courier_list_dir(curdir); + if(cdir){ + found = 0; j = 0L; + if(maildir_valid_name(pat)){ + for(i = 0; !found && i < cdir->total; i++) + if(strstr(curdir, cdir->data[i]->name)){ + if(strlen(curdir) < strlen(cdir->data[i]->name)) + found += 2; + else if(strlen(curdir) == strlen(cdir->data[i]->name)) + found -= 1; + } + if(found > 0) + j = LATT_HASCHILDREN; + else if(found == 0) + j = (style == COURIER) ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; + } + else + j = LATT_NOSELECT; + j += maildir_any_new_msgs(curdir) ? LATT_MARKED : LATT_UNMARKED; + if (found) + mm_list (stream, '.', pat, j); + courier_free_cdir(&cdir); + } + return; + } + + strcpy(tmp,pat + 4); /* a directory */ + j = strlen(pat) - 1; + maildir_file_path(pat, realpat, sizeof(realpat)); + c = pat[j]; + pat[j] = '\0'; + realname[0] = '\0'; + if(dir) + maildir_file_path(dir, realname, sizeof(realname)); + snprintf(curdir, sizeof(curdir), "%s%s%s/%s", (dir ? "" : myrootdir(pat)), (dir ? "" : "/"), + (dir ? realname : maildirpath), (dir ? "" : ".")); + snprintf(tmp, sizeof(tmp), "%s%s/.", MDPREFIX(COURIER), dir ? dir : maildirpath); + if (level == 0 && tmp && pmatch_full (tmp, realpat, '.')) + mm_list (stream,'.', tmp, LATT_NOSELECT); + + cdir = courier_list_dir(pat); + pat[j] = c; + for (i = 0; cdir && i < cdir->total; i++) + if(pmatch_full (cdir->data[i]->name, pat, '.')){ + snprintf(tmp, sizeof(tmp), "%s.", cdir->data[i]->name); + courier_list_info(&cdir, tmp, i); + mm_list (stream,'.',cdir->data[i]->name, cdir->data[i]->attribute); + } + courier_free_cdir(&cdir); + } + + int + same_maildir_file(char *name1, char *name2) + { + char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN]; + char *s; + + strcpy(tmp1, name1 ? name1 : ""); + strcpy(tmp2, name2 ? name2 : ""); + if ((s = strrchr(tmp1, FLAGSEP)) != NULL) + *s = '\0'; + if (((s = strrchr(tmp1, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) + *s = '\0'; + if ((s = strrchr(tmp2, FLAGSEP)) != NULL) + *s = '\0'; + if (((s = strrchr(tmp2, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) + *s = '\0'; + + return !strcmp(tmp1, tmp2); + } + + unsigned long antoul(char *seed) + { + int i, error = 0; + unsigned long val = 0L, rv1 = 0L, t; + char c, *p; + if(!seed) + return 0L; + t = strtoul(seed, &p, 10); + if(p && (*p == '.' || *p == '_')) + return t; + /* else */ + if((p = strchr(seed,'.')) != NULL) + *p = '\0'; + error = (strlen(seed) > 6); /* too long */ + for(i= strlen(seed)-1; error == 0 && i >= 0; i--){ + c = seed[i]; + if (c >= 'A' && c <= 'Z') val = c - 'A'; + else if (c >= 'a' && c <= 'z') val = c - 'a' + 26; + else if (c >= '0' && c <= '9') val = c - '0' + 26 + 26; + else if (c == '-') val = c - '-' + 26 + 26 + 10; + else if (c == '_') val = c - '_' + 26 + 26 + 10 + 1; + else error++; + rv1 = val + (rv1 << 6); + } + if(p) + *p = '.'; + return error ? 0L : rv1; + } + + unsigned long mdfntoul (char *name) + { + unsigned long t; + char *r, last; + + if((*name == '_') && ((r = strpbrk(name,".,%+")) != NULL)){ /* Grrr!!! */ + last = *r; + *r = '\0'; + t = antoul(r+1); + *r = last; + } + else + t = antoul(name); + return t; + } + + int comp_maildir_file(char *name1, char *name2) + { + int uset1 = 1, uset2 = 1, i, j, cmp; + unsigned long t1, t2; + char *s1, *s2; + + if (!(name1 && *name1)) + return (name2 && *name2) ? (*name2 == FLAGSEP ? 0 : -1) : 0; + + if (!(name2 && *name2)) + return (name1 && *name1) ? (*name1 == FLAGSEP ? 0 : 1) : 0; + + if((cmp = strcmp(name1,name2)) == 0) + return 0; + + t1 = strtoul(name1, &s1, 10); + t2 = strtoul(name2, &s2, 10); + + if(!s1 || *s1 != '.') + uset1 = 0; + + if(!s2 || *s2 != '.') + uset2 = 0; + + if(uset1 && uset2) /* normal sort order */ + return (t1 < t2) ? -1 : (t1 > t2 ? 1 : (cmp < 0 ? -1 : 1)); + + /* If we make it here we say Grrrr.... first, then we try to figure out + * how to sort this mess. + * These are the rules. + * If there is a number at the beginning it is bigger than anything else. + * If there are digits, then the number of digits decides which one is bigger. + */ + + for(i = 0; isdigit(name1[i]); i++); + for(j = 0; isdigit(name2[j]); j++); + + return(uset1 ? 1 + : (uset2 ? -1 + : (i < j ? -1 : (i > j ? 1 : (cmp < 0 ? -1 : 1))))); + } + + void + maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t) + { + char tmp[MAILTMPLEN], *b; + int offset = 0; + int tmpd, tmpf, tmpr, tmps, tmpt; + + if(d) *d = 0; + if(f) *f = 0; + if(r) *r = 0; + if(s) *s = 0; + if(t) *t = 0; + + tmpd = tmpf = tmpr = tmps = tmpt = NIL; /* no flags set by default */ + strcpy(tmp,name); + while ((b = strrchr(tmp+offset, FLAGSEP)) != NULL){ + char flag,last; + int k; + if (!++b) break; + switch (*b){ + case '1': + case '2': + case '3': flag = *b; b += 2; + for (k = 0; b[k] && b[k] != FLAGSEP && b[k] != ','; k++); + last = b[k]; + b[k] = '\0'; + if (flag == '2' || flag == '3'){ + tmpd = strchr (b, MDFLAGC(Draft)) ? T : NIL; + tmpf = strchr (b, MDFLAGC(Flagged)) ? T : NIL; + tmpr = strchr (b, MDFLAGC(Replied)) ? T : NIL; + tmps = strchr (b, MDFLAGC(Seen)) ? T : NIL; + tmpt = strchr (b, MDFLAGC(Trashed)) ? T : NIL; + } + b[k] = last; + b += k; + for (; tmp[offset] && tmp[offset] != FLAGSEP; offset++); + offset++; + break; + default: break; /* Should we crash?... Nahhh */ + } + } + if(d) *d = tmpd; + if(f) *f = tmpf; + if(r) *r = tmpr; + if(s) *s = tmps; + if(t) *t = tmpt; + } + + int + maildir_message_in_list(char *msgname, struct direct **names, + unsigned long bottom, unsigned long top, unsigned long *pos) + { + unsigned long middle = (bottom + top)/2; + int test; + + if (!msgname) + return NIL; + + if (pos) *pos = middle; + + if (same_maildir_file(msgname, names[middle]->d_name)) + return T; + + if (middle == bottom){ /* 0 <= 0 < 1 */ + int rv = NIL; + if (same_maildir_file(msgname, names[middle]->d_name)){ + rv = T; + if (pos) *pos = middle; + } + else + if (same_maildir_file(msgname, names[top]->d_name)){ + rv = T; + if (pos) *pos = top; + } + return rv; + } + + test = comp_maildir_file(msgname, names[middle]->d_name); + + if (top <= bottom) + return test ? NIL : T; + + if (test < 0 ) /* bottom < msgname < middle */ + return maildir_message_in_list(msgname, names, bottom, middle, pos); + else if (test > 0) /* middle < msgname < top */ + return maildir_message_in_list(msgname, names, middle, top, pos); + else return T; + } + + void + maildir_abort(MAILSTREAM *stream) + { + if (LOCAL){ + DirNamesType i; + + if(LOCAL->candouid) + maildir_read_uid(stream, NULL, &stream->uid_validity); + if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); + for (i = Cur; i < EndDir; i++) + if(LOCAL->path[i]) fs_give ((void **) &LOCAL->path[i]); + fs_give ((void **) &LOCAL->path); + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); + if(LOCAL->uidtempfile){ + unlink(LOCAL->uidtempfile); + fs_give ((void **) &LOCAL->uidtempfile); + } + fs_give ((void **) &stream->local); + } + if (mdfpath) fs_give((void **)&mdfpath); + stream->dtb = NIL; + } + + int + maildir_contains_folder(char *dirname, char *name) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN]; + int rv = 0; + DIR *dir; + struct direct *d; + + maildir_file_path(dirname, tmp2, sizeof(tmp2)); + if(name){ + strcat(tmp2,"/"); + strcat(tmp2, name); + } + + if (!(dir = opendir (tmp2))) + return NIL; + + while ((d = readdir(dir)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New))){ + + snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); + if(maildir_valid(tmp)){ + rv++; + break; + } + } + } + closedir(dir); + return rv; + } + + int + maildir_is_dir(char *dirname, char *name) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + maildir_file_path(dirname, tmp, sizeof(tmp)); + if(name){ + strcat(tmp, "/"); + strcat(tmp, name); + } + strcat(tmp, "/"); + strcat(tmp, MDDIR); + + return ((stat(tmp, &sbuf) == 0) && S_ISREG (sbuf.st_mode)) ? 1 : 0; + } + + int + maildir_dir_is_empty(char *mailbox) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], tmp3[MAILTMPLEN],*s; + int rv = 1, courier = IS_COURIER(mailbox); + DIR *dir; + struct direct *d; + struct stat sbuf; + + maildir_file_path(mailbox, tmp2, sizeof(tmp2)); + + if(courier){ + strcpy(tmp3, tmp2); + if(s = strrchr(tmp2, '/')) + *s = '\0'; + } + + if (!(dir = opendir (tmp2))) + return rv; + + if(courier){ + while((d = readdir(dir)) != NULL){ + snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); + if(!strncmp(tmp, tmp3, strlen(tmp3)) + && tmp[strlen(tmp3)] == '.'){ + rv = 0; + break; + } + } + } + else + while ((d = readdir(dir)) != NULL){ + snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); + if (strcmp(d->d_name, ".") + && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New)) + && strcmp(d->d_name, MDDIR) + && strcmp(d->d_name, MDUIDVALIDITY) + && !(d->d_name[0] == '.' + && stat (tmp,&sbuf) == 0 + && S_ISREG(sbuf.st_mode))){ + rv = 0; + break; + } + } + closedir(dir); + return rv; + } + + void + maildir_get_file (MAILDIRFILE **mdfile) + { + MAILDIRFILE *md; + + md = (MAILDIRFILE *) fs_get(sizeof(MAILDIRFILE)); + memset(md, 0, sizeof(MAILDIRFILE)); + *mdfile = md; + } + + void + maildir_free_file (void **mdfile) + { + MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; + + if (md){ + if (md->name) fs_give((void **)&md->name); + fs_give((void **)&md); + } + } + + void + maildir_free_file_only (void **mdfile) + { + MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; + + if (md && md->name) + fs_give((void **)&md->name); + } + + int + maildir_any_new_msgs(char *mailbox) + { + char tmp[MAILTMPLEN]; + int rv = NIL; + DIR *dir; + struct direct *d; + + MDFLD(tmp, mailbox, New); + + if (!(dir = opendir (tmp))) + return rv; + + while ((d = readdir(dir)) != NULL){ + if (d->d_name[0] == '.') + continue; + rv = T; + break; + } + closedir(dir); + return rv; + } + + + void + maildir_get_date(MAILSTREAM *stream, unsigned long msgno) + { + MESSAGECACHE *elt; + struct tm *t; + time_t ti; + int i,k; + + elt = mail_elt (stream,msgno); + if(elt && elt->year != 0) + return; + if ((ti = mdfntoul(MDFILE(elt))) > 0L && (t = gmtime(&ti))){ + i = t->tm_hour * 60 + t->tm_min; + k = t->tm_yday; + t = localtime(&ti); + i = t->tm_hour * 60 + t->tm_min - i; + if((k = t->tm_yday - k) != 0) + i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; + k = abs (i); + elt->hours = t->tm_hour; + elt->minutes = t->tm_min; + elt->seconds = t->tm_sec; + elt->day = t->tm_mday; elt->month = t->tm_mon + 1; + elt->year = t->tm_year - (BASEYEAR - 1900); + elt->zoccident = (k == i) ? 0 : 1; + elt->zhours = k/60; + elt->zminutes = k % 60; + } + } + + /* Support for Courier Style directories + When this code is complete there will be two types of support, which + will be configurable. The problem is the following: In Courier style + folder structure, a "folder" may have a subfolder called + "folder.subfolder", which is not natural in the file system in the + sense that I can not stat for "folder.subfolder" wihtout knowing what + "subfolder" is. It needs to be guessed. Because of this I need to look + in the list of folders if there is a folder with a name + "folder.subfolder", before I can say if the folder is dual or not. One + can avoid this annoyance if one ignores the problem by declaring that + every folder is dual. I will however code as the default the more + complicated idea of scaning the containing directory each time it is + modified and search for subfolders, and list the entries it found. + */ + + int courier_dir_select (const struct direct *name) + { + return name->d_name[0] == '.' && (strlen(name->d_name) > 2 + || (strlen(name->d_name) == 2 && name->d_name[1] != '.')); + } + + int courier_dir_sort (const struct direct **d1, const struct direct **d2) + { + const struct direct *e1 = *(const struct direct **) d1; + const struct direct *e2 = *(const struct direct **) d2; + + return strcmp((char *) e1->d_name, (char *) e2->d_name); + } + + void courier_free_cdir (COURIER_S **cdir) + { + int i; + + if (!*cdir) + return; + + if ((*cdir)->path) fs_give((void **)&((*cdir)->path)); + for (i = 0; i < (*cdir)->total; i++) + if((*cdir)->data[i]->name) fs_give((void **)&((*cdir)->data[i]->name)); + fs_give((void **)&((*cdir)->data)); + fs_give((void **)&(*cdir)); + } + + COURIER_S *courier_get_cdir (int total) + { + COURIER_S *cdir; + + cdir = (COURIER_S *)fs_get(sizeof(COURIER_S)); + memset(cdir, 0, sizeof(COURIER_S)); + cdir->data = (COURIERLOCAL **) fs_get(total*sizeof(COURIERLOCAL *)); + memset(cdir->data, 0, sizeof(COURIERLOCAL *)); + cdir->total = total; + return cdir; + } + + int courier_search_list(COURIERLOCAL **data, char *name, int first, int last) + { + int try = (first + last)/2; + + if(!strstr(data[try]->name, name)){ + if(first == try) /* first == last || first + 1 == last */ + return strstr(data[last]->name, name) ? 1 : 0; + if(strcmp(data[try]->name, name) < 0) /*data[try] < name < data[end] */ + return courier_search_list(data, name, try, last); + else /* data[begin] < name < data[try] */ + return courier_search_list(data, name, first, try); + } + return 1; + } + + /* Lists all directories that are subdirectories of a given directory */ + + COURIER_S *courier_list_dir(char *curdir) + { + struct direct **names = NIL; + struct stat sbuf; + unsigned long ndir; + COURIER_S *cdir = NULL; + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], pathname[MAILTMPLEN], + realname[MAILTMPLEN]; + int i, j, scand, td; + + /* There are two cases, either curdir is + #mc/INBOX. #mc/INBOX.foo + or + #mc/Maildir/. #mc/Maildir/.foo + */ + strcpy(tmp,curdir + 4); + if(!strncmp(ucase(tmp), "INBOX", 5)) + strcpy(tmp, "#mc/INBOX."); + else{ + strcpy(tmp, curdir); + for (i = strlen(tmp) - 1; tmp[i] && tmp[i] != '/'; i--); + tmp[i+2] = '\0'; /* keep the last "." intact */ + } + maildir_file_path(tmp, realname, sizeof(realname)); + maildir_scandir (realname, &names, &ndir, &scand, COURIER); + + if (scand > 0){ + cdir = courier_get_cdir(ndir); + cdir->path = cpystr(realname); + for(i = 0, j = 0; i < ndir; i++){ + td = realname[strlen(realname) - 1] == '.' + && *names[i]->d_name == '.'; + snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, names[i]->d_name+1); + snprintf(pathname, sizeof(pathname), "%s%s", realname, names[i]->d_name + td); + if(stat(pathname, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)){ + cdir->data[j] = (COURIERLOCAL *) fs_get(sizeof(COURIERLOCAL)); + cdir->data[j++]->name = cpystr(tmp2); + } + fs_give((void **)&names[i]); + } + cdir->total = j; + if(cdir->total == 0) + courier_free_cdir(&cdir); + } + if(names) + fs_give((void **) &names); + return cdir; + } + + void + courier_list_info(COURIER_S **cdirp, char *data, int i) + { + long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL); + COURIER_S *cdir = *cdirp; + + if(maildir_valid(cdir->data[i]->name)){ + if(courier_search_list(cdir->data, data, 0, cdir->total - 1)) + cdir->data[i]->attribute = LATT_HASCHILDREN; + else + cdir->data[i]->attribute = (style == COURIER) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; + } + else + cdir->data[i]->attribute = LATT_NOSELECT; + cdir->data[i]->attribute += maildir_any_new_msgs(cdir->data[i]->name) + ? LATT_MARKED : LATT_UNMARKED; + } + + /* UID Support */ + /* Yes, I know I procastinated a lot about this, but here it is finally */ + + /* return code: + bigger than zero: this session can assign uids + zero: this session will not assign uid + smaller than zero: this session temporarily suspends assigning uids + */ + int + maildir_can_assign_uid (MAILSTREAM *stream) + { + unsigned int rv = 0; + int ownuid, existuid; + unsigned long t; + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], *p, *s; + DIR *dir; + struct direct *d; + + if(!stream || stream->rdonly + || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) + return 0; + + if(mypid == (pid_t) 0) + mypid = getpid(); + + snprintf(tmp, sizeof(tmp), "%s.%d", MDUIDTEMP, mypid); + + ownuid = existuid = 0; + s = NULL; + while ((d = readdir(dir)) != NULL){ + if(strncmp(d->d_name, tmp, strlen(tmp)) == 0){ + existuid++; ownuid++; + if(ownuid > 1){ + snprintf(tmp2, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); + unlink(tmp2); + if(s){ + snprintf(tmp2, sizeof(tmp2), "%s/%s", LOCAL->dir, s); + unlink(tmp2); + fs_give((void **)&s); + } + } + else + s = cpystr(d->d_name); + } + else if(strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)) == 0) + existuid++; + } + + closedir(dir); + if(s) + fs_give((void **)&s); + + if(ownuid == 1 && existuid == 1) + rv = 1; + + if(ownuid == 0 && existuid == 0){ /* nobody owns the uid? */ + FILE *fp; + snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); + if(fp = fopen(tmp, "w")){ + fclose(fp); + if(LOCAL->uidtempfile) + fs_give((void **)&LOCAL->uidtempfile); + LOCAL->uidtempfile = cpystr(tmp); + } + rv = 1; + } + + if(ownuid == 0 && existuid > 0) /* someone else owns uid assignment */ + return 0; + + /* if we own the uid, check that we do not own it more than once + * or that we share ownership. If any of these situations happens, + * give up the ownership until we can recover it + */ + + if(ownuid > 0){ + if(ownuid > 1) /* impossible, two lock files for the same session */ + return (-1)*ownuid; + + if(ownuid != existuid){ /* lock files for different sessions */ + if(LOCAL->uidtempfile){ + unlink(LOCAL->uidtempfile); + fs_give((void **)&LOCAL->uidtempfile); + } + return (-1)*ownuid; + } + } + + return rv; + } + + void + maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, + unsigned long *uid_validity) + { + int createuid, deleteuid = 0; + char tmp[MAILTMPLEN], *s = NULL; + DIR *dir; + struct direct *d; + + if(uid_last) *uid_last = 0L; + if(uid_last && uid_validity) *uid_validity = time(0); + if(!stream || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) + return; + + while ((d = readdir(dir)) != NULL){ + if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))) + break; + } + createuid = d == NULL ? 1 : 0; + if(uid_last == NULL) + deleteuid++; + if(d){ + if(uid_last){ + s = d->d_name + strlen(MDUIDLAST) + 1; + *uid_last = strtoul(s, &s, 10); + if(!s || *s != '.'){ + deleteuid++; + createuid++; + *uid_last = 0L; + } + } + if(s && *s == '.'){ + if(uid_validity){ + s++; + *uid_validity = strtoul(s, &s, 10); + if(s && *s != '\0'){ + *uid_validity = time(0); + deleteuid++; + createuid++; + } + } + } + else{ + deleteuid++; + createuid++; + } + } + if(deleteuid){ + snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); + unlink(tmp); + } + if(createuid) + maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), + uid_validity ? *uid_validity : time(0)); + closedir(dir); + } + + void + maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, + unsigned long uid_validity) + { + char tmp[MAILTMPLEN]; + FILE *fp; + + if(!stream || stream->rdonly || !LOCAL || !LOCAL->dir) + return; + + snprintf(tmp, sizeof(tmp), "%s/%s.%010lu.%010lu", LOCAL->dir, MDUIDLAST, + uid_last, uid_validity); + if(fp = fopen(tmp, "w")) + fclose(fp); + } + + unsigned long + maildir_get_uid(char *name) + { + char *s; + unsigned long rv = 0L; + + if(!name || (s = strstr(name,MDUIDSEP)) == NULL) + return rv; + + s += strlen(MDUIDSEP); + rv = strtoul(s, NULL, 10); + return rv; + } + + + void + maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno) + { + char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; + MESSAGECACHE *elt; + + elt = mail_elt(stream, msgno); + if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) + return; + + snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); + t = MDFILE(elt); + if(s = strstr(MDFILE(elt), MDUIDSEP)){ + *s = '\0'; + s += strlen(MDUIDSEP); + strtoul(s, &s, 10); + snprintf(new, sizeof(new), "%s/%s/%s%s", LOCAL->dir, MDNAME(Cur), t, s); + if(rename(old, new) == 0){ + maildir_free_file_only ((void **)&elt->private.spare.ptr); + s = strrchr(new, '/'); + MDFILE(elt) = cpystr(s+1); + } + elt->private.uid = 0L; + } + } + + void + maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid) + { + int createuid, deleteuid = 0; + char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; + MESSAGECACHE *elt; + + elt = mail_elt(stream, msgno); + if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) + return; + + maildir_delete_uid(stream, msgno); + snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); + t = MDFILE(elt); + if((s = strrchr(MDFILE(elt),FLAGSEP)) != NULL){ + *s++ = '\0'; + snprintf(new, sizeof(new), "%s/%s/%s%s%lu%c%s", + LOCAL->dir, MDNAME(Cur), t, MDUIDSEP, uid, FLAGSEP, s); + if(rename(old, new) == 0){ + maildir_free_file_only ((void **)&elt->private.spare.ptr); + s = strrchr(new, '/'); + MDFILE(elt) = cpystr(s+1); + stream->uid_validity = time(0); + } + elt->private.uid = uid; + } + } + + void + maildir_uid_renew_tempfile(MAILSTREAM *stream) + { + char tmp[MAILTMPLEN]; + + if(!stream || stream->rdonly + || !LOCAL || !LOCAL->candouid || !LOCAL->dir || !LOCAL->uidtempfile) + return; + + if(mypid == (pid_t) 0) + mypid = getpid(); + + snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); + if(rename(LOCAL->uidtempfile, tmp) == 0){ + fs_give((void **)&LOCAL->uidtempfile); + LOCAL->uidtempfile = cpystr(tmp); + } + } diff -rc alpine-2.24/imap/src/osdep/unix/maildir.h alpine-2.24.maildir/imap/src/osdep/unix/maildir.h *** alpine-2.24/imap/src/osdep/unix/maildir.h 2020-10-10 00:26:50.012168504 -0600 --- alpine-2.24.maildir/imap/src/osdep/unix/maildir.h 2020-10-10 00:26:49.972167485 -0600 *************** *** 0 **** --- 1,226 ---- + /* + * A few definitions that try to make this module portable to other + * platforms (e.g. Cygwin). This module is based on the information from + * http://cr.yp.to/proto/maildir.html + */ + + /* First we deal with the separator character */ + #ifndef FLAGSEP + #define FLAGSEP ':' + #endif + #define SIZESEP ',' + + const char sep1[] = {FLAGSEP, '1', ',', '\0'}; /* experimental semantics*/ + const char sep2[] = {FLAGSEP, '2', ',', '\0'}; /* Flags Information */ + const char sep3[] = {FLAGSEP, '3', ',', '\0'}; /* Grrrr.... */ + + const char *sep[] = { sep1, sep2, sep3, NULL}; + + #define MDSEP(i) sep[((i) - 1)] + + /* Now we deal with flags. Woohoo! */ + typedef enum {Draft, Flagged, Passed, Replied, Seen, Trashed, + EmptyFlag, EndFlags} MdFlagNamesType; + const int mdimapflags[] = {Draft, Flagged, Replied, Seen, Trashed, EmptyFlag, EndFlags}; + const int mdkwdflags[] = {Passed, EmptyFlag, EndFlags}; + + /* this array lists the codes for mdflgnms (maildir flag names) above */ + const char *mdflags[] = { "D", "F", "P", "R", "S", "T", "", NULL}; + /* and as characters too */ + const char cmdflags[] = { 'D', 'F', 'P', 'R', 'S', 'T', '0', '\0'}; + + /* MDFLAG(Seen, elt->seen) */ + #define MDFLAG(i,j) mdflags[j ? (i) : EmptyFlag] + /* MDFLAGC(Seen) */ + #define MDFLAGC(i) cmdflags[(i)] + + /* Now we deal with the directory structure */ + typedef enum {Cur, Tmp, New, EndDir} DirNamesType; + char *mdstruct[] = {"cur", "tmp", "new", NULL}; + #define MDNAME(i) mdstruct[(i)] + #define MDFLD(tmp, dir, i) sprintf((tmp),"%s/%s", (dir), mdstruct[(i)]) + #define MSGPATH(tmp, dir, msg,i) sprintf((tmp),"%s/%s/%s", (dir), mdstruct[(i)],(msg)) + + /* Files associated to a maildir directory */ + + #define MDUIDVALIDITY ".uidvalidity" /* support for old maildirs */ + #define MDDIR ".mdir" /* this folder is a directory */ + #define MDUIDLAST ".uidlast" /* last assigned uid */ + #define MDUIDTEMP ".uidtemp" /* We assign uid's no one else */ + + + + /* Support of Courier Structure */ + #define CCLIENT 0 + #define COURIER 1 + #define IS_CCLIENT(t) \ + (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ + && ((t)[2] == 'd' || (t)[2] == 'D')\ + && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) + + #define IS_COURIER(t) \ + (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ + && ((t)[2] == 'c' || (t)[2] == 'C')\ + && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) + #define MDPREFIX(s) ((s) ? "#mc/" : "#md/") + #define MDSEPARATOR(s) ((s) ? '.' : '/') + + /* UID Support */ + + #define MAXTEMPUID (unsigned long) 180L + const char mduid[] = {',','u','=','\0'}; + #define MDUIDSEP mduid + + + /* Now we deal with messages filenames */ + char mdlocaldomain[MAILTMPLEN+1] = {'\0'}; + pid_t mypid = (pid_t) 0; + static char *mdfpath = NULL; + static char myMdInboxDir[50] = { '\0' };/* Location of the Maildir INBOX */ + static long CourierStyle = CCLIENT; + + #define CHUNK 16384 /* from unix.h */ + + typedef struct courier_local { + char *name; /* name of directory/folder */ + int attribute; /* attributes (children/marked/etc) */ + } COURIERLOCAL; + + typedef struct courier { + char *path; /* Path to collection */ + time_t scantime; /* time at which information was generated */ + int total; /* total number of elements in data */ + COURIERLOCAL **data; + } COURIER_S; + + /* In gdb this is the *(struct maildir_local *)stream->local structure */ + typedef struct maildir_local { + unsigned int dirty : 1; /* diskcopy needs updating */ + unsigned int courier : 1; /* It is Courier style file system */ + unsigned int link : 1; /* There is a symbolic link */ + int candouid; /* we can assign uids and no one else */ + char *uidtempfile; /* path to uid temp file */ + int fd; /* fd of open message */ + char *dir; /* mail directory name */ + char **path; /* path to directories cur, new and tmp */ + unsigned char *buf; /* temporary buffer */ + unsigned long buflen; /* current size of temporary buffer */ + time_t scantime; /* last time directory scanned */ + } MAILDIRLOCAL; + + /* Convenient access to local data */ + #define LOCAL ((MAILDIRLOCAL *) stream->local) + + typedef struct maildir_file_info { + char *name; /* name of the file */ + DirNamesType loc; /* location of this file */ + unsigned long pos; /* place in list where this file is listed */ + off_t size; /* size in bytes, on disk */ + time_t atime; /* last access time */ + time_t mtime; /* last modified time */ + time_t ctime; /* last changed time */ + } MAILDIRFILE; + + #define MDFILE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->name) + #define MDLOC(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->loc) + #define MDPOS(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->pos) + #define MDSIZE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->size) + #define MDATIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->atime) + #define MDMTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->mtime) + #define MDCTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->ctime) + + /* Function prototypes */ + + DRIVER *maildir_valid (char *name); + MAILSTREAM *maildir_open (MAILSTREAM *stream); + void maildir_close (MAILSTREAM *stream, long options); + long maildir_ping (MAILSTREAM *stream); + void maildir_check (MAILSTREAM *stream); + long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); + char *maildir_header (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags); + void maildir_list (MAILSTREAM *stream,char *ref,char *pat); + void *maildir_parameters (long function,void *value); + int maildir_create_folder (char *mailbox); + long maildir_create (MAILSTREAM *stream,char *mailbox); + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); /*check */ + long maildir_expunge (MAILSTREAM *stream, char *sequence, long options); + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); + long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data); + long maildir_delete (MAILSTREAM *stream,char *mailbox); + long maildir_rename (MAILSTREAM *stream,char *old,char *new); + long maildir_sub (MAILSTREAM *stream,char *mailbox); + long maildir_unsub (MAILSTREAM *stream,char *mailbox); + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); + void courier_list (MAILSTREAM *stream,char *ref, char *pat); + + /* utility functions */ + void courier_realname (char *name, char *realname); + long maildir_dirfmttest (char *name); + char *maildir_file (char *dst,char *name); + int maildir_select (const struct direct *name); + int maildir_namesort (const struct direct **d1, const struct direct **d2); + unsigned long antoul (char *seed); + unsigned long mdfntoul (char *name); + int courier_dir_select (const struct direct *name); + int courier_dir_sort (const struct direct **d1, const struct direct **d2); + long maildir_canonicalize (char *pattern,char *ref,char *pat); + void maildir_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); + void courier_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); + int maildir_file_path(char *name, char *tmp, size_t sizeoftmp); + int maildir_valid_name (char *name); + int maildir_valid_dir (char *name); + int is_valid_maildir (char **name); + int maildir_message_exists(MAILSTREAM *stream,char *name, char *tmp); + char *maildir_remove_root(char *name); + char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); + unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, + DirNamesType dirtype); + int maildir_eliminate_duplicate (char *name, struct direct ***flist, + unsigned long *nfiles); + int maildir_doscandir (char *name, struct direct ***flist, int flag); + unsigned long maildir_scandir (char *name, struct direct ***flist, + unsigned long *nfiles, int *scand, int flag); + void maildir_parse_folder (MAILSTREAM *stream, int full); + void md_domain_name (void); + char *myrootdir (char *name); + char *mdirpath (void); + int maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype); + unsigned long maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, + DirNamesType dirtype, struct direct **names, unsigned long nfiles, int full); + int same_maildir_file(char *name1, char *name2); + int comp_maildir_file(char *name1, char *name2); + int maildir_message_in_list(char *msgname, struct direct **names, + unsigned long bottom, unsigned long top, unsigned long *pos); + void maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t); + int maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno); + void maildir_abort (MAILSTREAM *stream); + int maildir_contains_folder(char *dirname, char *name); + int maildir_is_dir(char *dirname, char *name); + int maildir_dir_is_empty(char *mailbox); + int maildir_create_work (char *mailbox, int loop); + void maildir_get_file (MAILDIRFILE **mdfile); + void maildir_free_file (void **mdfile); + void maildir_free_file_only (void **mdfile); + int maildir_any_new_msgs(char *mailbox); + void maildir_get_date(MAILSTREAM *stream, unsigned long msgno); + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags); + + /* Courier server support */ + void courier_free_cdir (COURIER_S **cdir); + COURIER_S *courier_get_cdir (int total); + int courier_search_list(COURIERLOCAL **data, char *name, int first, int last); + COURIER_S *courier_list_dir(char *curdir); + void courier_list_info(COURIER_S **cdirp, char *data, int i); + + /* UID Support */ + int maildir_can_assign_uid (MAILSTREAM *stream); + void maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, + unsigned long *uid_validity); + void maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, + unsigned long uid_validity); + unsigned long maildir_get_uid(char *name); + void maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno); + void maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid); + void maildir_uid_renew_tempfile(MAILSTREAM *stream); + diff -rc alpine-2.24/imap/src/osdep/unix/Makefile alpine-2.24.maildir/imap/src/osdep/unix/Makefile *** alpine-2.24/imap/src/osdep/unix/Makefile 2020-10-10 00:24:28.196554399 -0600 --- alpine-2.24.maildir/imap/src/osdep/unix/Makefile 2020-10-10 00:26:49.976167588 -0600 *************** *** 146,152 **** # However, mh needs to be before any sysinbox formats (such as mmdf or unix) # since otherwise INBOX won't work correctly when mh_allow_inbox is set. # ! DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile CHUNKSIZE=65536 # Normally no need to change any of these --- 146,152 ---- # However, mh needs to be before any sysinbox formats (such as mmdf or unix) # since otherwise INBOX won't work correctly when mh_allow_inbox is set. # ! DEFAULTDRIVERS=maildir courier imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile CHUNKSIZE=65536 # Normally no need to change any of these *************** *** 155,161 **** BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o http.o json.o pop3.o \ ! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o CFLAGS=-g CAT=cat --- 155,161 ---- BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o http.o json.o pop3.o \ ! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o maildir.o CFLAGS=-g CAT=cat *************** *** 292,298 **** cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ ! DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ --- 292,298 ---- cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ ! DEFAULTDRIVERS="imap nntp pop3 mbx unix maildir phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ *************** *** 921,926 **** --- 921,927 ---- utf8aux.o: mail.h misc.h osdep.h utf8.h json.o: mail.h misc.h osdep.h utf8.h json.h http.o: mail.h misc.h osdep.h utf8.h http.h json.h + maildir.o: mail.h misc.h osdep.h maildir.h dummy.h # OS-dependent diff -rc alpine-2.24/imap/src/osdep/unix/os_cyg.h alpine-2.24.maildir/imap/src/osdep/unix/os_cyg.h *** alpine-2.24/imap/src/osdep/unix/os_cyg.h 2020-10-10 00:24:28.192554297 -0600 --- alpine-2.24.maildir/imap/src/osdep/unix/os_cyg.h 2020-10-10 00:26:49.976167588 -0600 *************** *** 47,52 **** --- 47,53 ---- #define setpgrp setpgid #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ + #define FLAGSEP ';' #define geteuid Geteuid uid_t Geteuid (void); diff -rc alpine-2.24/pith/conf.c alpine-2.24.maildir/pith/conf.c *** alpine-2.24/pith/conf.c 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/conf.c 2020-10-10 00:26:49.976167588 -0600 *************** *** 453,458 **** --- 453,461 ---- CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; + #ifndef _WINDOWS + CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; + #endif /*---------------------------------------------------------------------- These are the variables that control a number of pine functions. They *************** *** 657,662 **** --- 660,669 ---- NULL, cf_text_news_active}, {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_news_spooldir}, + #ifndef _WINDOWS + {"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + "Maildir Location", cf_text_maildir_location}, + #endif {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_upload_cmd}, {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, *************** *** 2392,2397 **** --- 2399,2410 ---- mail_parameters(NULL, SET_NEWSSPOOL, (void *)VAR_NEWS_SPOOL_DIR); + #ifndef _WINDOWS + set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); + if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) + mail_parameters(NULL, SET_MDINBOXPATH, (void *)VAR_MAILDIR_LOCATION); + #endif + /* guarantee a save default */ set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) *************** *** 3030,3035 **** --- 3043,3052 ---- F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0}, {"vertical-folder-list", "Use Vertical Folder List", F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0}, + #ifndef _WINDOWS + {"use-courier-folder-list", "Courier Style Folder List", + F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR, 0}, + #endif /* Addr book */ {"combined-addrbook-display", "Combined Address Book Display", *************** *** 7209,7215 **** int just_flip_value, EditWhich ew) { char **vp, *p, **lval, ***alval; ! int og, on_before, was_set; char *err; long l; --- 7226,7232 ---- int just_flip_value, EditWhich ew) { char **vp, *p, **lval, ***alval; ! int og, on_before, was_set, i; char *err; long l; *************** *** 7262,7267 **** --- 7279,7291 ---- break; + #ifndef _WINDOWS + case F_COURIER_FOLDER_LIST: + i = F_ON(f->id ,ps) ? 1 : 0; + mail_parameters(NULL,SET_COURIERSTYLE, (void *) &i); + break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ + #endif + case F_COLOR_LINE_IMPORTANT : case F_DATES_TO_LOCAL : clear_index_cache(ps->mail_stream, 0); *************** *** 8062,8067 **** --- 8086,8095 ---- return(h_config_newmailwidth); case V_NEWSRC_PATH : return(h_config_newsrc_path); + #ifndef _WINDOWS + case V_MAILDIR_LOCATION : + return(h_config_maildir_location); + #endif case V_BROWSER : return(h_config_browser); case V_HISTORY : diff -rc alpine-2.24/pith/conf.h alpine-2.24.maildir/pith/conf.h *** alpine-2.24/pith/conf.h 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/conf.h 2020-10-10 00:26:49.976167588 -0600 *************** *** 264,269 **** --- 264,273 ---- #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p + #ifndef _WINDOWS + #define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p + #define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p + #endif #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p diff -rc alpine-2.24/pith/conftype.h alpine-2.24.maildir/pith/conftype.h *** alpine-2.24/pith/conftype.h 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/conftype.h 2020-10-10 00:26:49.980167690 -0600 *************** *** 118,123 **** --- 118,126 ---- , V_NEWSRC_PATH , V_NEWS_ACTIVE_PATH , V_NEWS_SPOOL_DIR + #ifndef _WINDOWS + , V_MAILDIR_LOCATION + #endif , V_UPLOAD_CMD , V_UPLOAD_CMD_PREFIX , V_DOWNLOAD_CMD *************** *** 406,411 **** --- 409,417 ---- F_PASS_C1_CONTROL_CHARS, F_SINGLE_FOLDER_LIST, F_VERTICAL_FOLDER_LIST, + #ifndef _WINDOWS + F_COURIER_FOLDER_LIST, + #endif F_TAB_CHK_RECENT, F_AUTO_REPLY_TO, F_VERBOSE_POST, diff -rc alpine-2.24/pith/init.c alpine-2.24.maildir/pith/init.c *** alpine-2.24/pith/init.c 2020-10-10 00:24:28.208554703 -0600 --- alpine-2.24.maildir/pith/init.c 2020-10-10 00:26:49.980167690 -0600 *************** *** 408,413 **** --- 408,416 ---- && stricmp(filename, folder_base)){ #else if(strncmp(filename, folder_base, folder_base_len) == 0 + #ifndef _WINDOWS + && filename[folder_base_len] != list_cntxt->dir->delim + #endif && strcmp(filename, folder_base)){ #endif #endif diff -rc alpine-2.24/pith/pattern.c alpine-2.24.maildir/pith/pattern.c *** alpine-2.24/pith/pattern.c 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/pattern.c 2020-10-10 00:26:49.984167792 -0600 *************** *** 49,55 **** #include "../pith/icache.h" #include "../pith/ablookup.h" #include "../pith/keyword.h" ! /* * Internal prototypes --- 49,57 ---- #include "../pith/icache.h" #include "../pith/ablookup.h" #include "../pith/keyword.h" ! #ifndef _WINDOWS ! int maildir_file_path(char *name, char *tmp, size_t sizeoftmp); ! #endif /* _WINDOWS */ /* * Internal prototypes *************** *** 5485,5490 **** --- 5487,5501 ---- break; case '#': + #ifndef _WINDOWS + if(!struncmp(patfolder, "#md/", 4) + || !struncmp(patfolder, "#mc/", 4)){ + maildir_file_path(patfolder, tmp1, sizeof(tmp1)); + if(!strcmp(tmp1, stream->mailbox)) + match++; + break; + } + #endif if(!strcmp(patfolder, stream->mailbox)) match++; *************** *** 7905,7911 **** int we_cancel = 0, width; CONTEXT_S *save_context = NULL; char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; ! char *save_ref = NULL; #define FILTMSG_MAX 30 if(!stream) --- 7916,7922 ---- int we_cancel = 0, width; CONTEXT_S *save_context = NULL; char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; ! char *save_ref = NULL, *save_dstfldr = NULL, *save_dstfldr2 = NULL; #define FILTMSG_MAX 30 if(!stream) *************** *** 7939,7944 **** --- 7950,7965 ---- if(F_OFF(F_QUELL_FILTER_MSGS, ps_global)) we_cancel = busy_cue(buf, NULL, 0); + #ifndef _WINDOWS + if(!struncmp(dstfldr, "#md/", 4) || !struncmp(dstfldr, "#mc/", 4)){ + char tmp1[MAILTMPLEN]; + maildir_file_path(dstfldr, tmp1, sizeof(tmp1)); + save_dstfldr2 = dstfldr; + save_dstfldr = cpystr(tmp1); + dstfldr = save_dstfldr; + } + #endif + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; *************** *** 8002,8007 **** --- 8023,8033 ---- if(we_cancel) cancel_busy_cue(buf[0] ? 0 : -1); + if(save_dstfldr){ + fs_give((void **)&save_dstfldr); + dstfldr = save_dstfldr2; + } + return(buf[0] != '\0'); } diff -rc alpine-2.24/pith/pine.hlp alpine-2.24.maildir/pith/pine.hlp *** alpine-2.24/pith/pine.hlp 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/pine.hlp 2020-10-10 00:26:49.996168098 -0600 *************** *** 23452,23457 **** --- 23452,23553 ---- <End of help on this topic> + ====== h_config_maildir_location ====== + + + OPTION: <!--#echo var="VAR_maildir-location"--> + + +

OPTION:

+ +

+ This option should be used only if you have a Maildir folder which you + want to use as your INBOX. If this is not your case (or don't know what + this is), you can safely ignore this option. + +

+ This option overrides the default directory Pine uses to find the location of + your INBOX, in case this is in Maildir format. The default value of this + option is "Maildir", but in some systems, this directory could have been + renamed (e.g. to ".maildir"). If this is your case use this option to change + the default. + +

+ The value of this option is prefixed with the "~/" string to determine the + full path to your INBOX. + +

+ You should probably read a few tips that + teach you how to configure your maildir for optimal performance. This + version also has support for the + Courier style file system when a maildir collection is accessed locally. + +

+

+ <End of help on this topic> + + + ====== h_config_maildir ===== + + + Maildir Support + + +

Maildir Support

+ + This version of Alpine has been enhanced with Maildir support. This text is + intended to be a reference on its support. +

+ + A Maildir folder is a directory that contains three directories called + cur, tmp and new. A program that delivers mail (e.g. postfix) will put new + mail in the new directory. A program that reads mail will look for for old + messages in the cur directory, while it will look for new mail in the new + directory. +

+ + In order to use maildir support it is better to set your inbox-path to the + value "#md/inbox" (without quotes). This assumes that your mail + delivery agent is delivering new mail to ~/Maildir/new. If the directory + where new mail is being delivered is not called "Maildir", you can set the + name of the subdirectory of home where it is being delivered in the configuration + variable. Most of the time you will not have to worry about the + variable, because it will probably be set by your + administrator in the pine.conf configuration file. +

+ + One of the advantages of the Maildir support of this version of Alpine is + that you do not have to stop using folders in another styles (mbox, mbx, + etc.). This is desirable since the usage of a specific mail storage system + is a personal decision. Folders in the maildir format that are part of the + Mail collection will be recognized without any extra configuration of your + part. If your mail/ collection is located under the mail/ directory, then + creating a new maildir folder in this collection is done by pressing "A" + and entering the string "#driver.md/mail/newfolder". Observe that adding a + new folder as "newfolder" may not create such folder in maildir format. + +

+ If you would like to have all folders created in the maildir format by + default, you do so by adding a Maildir Collection. In order to convert + your current mail/ collection into a maildir collection, edit the + collection and change the path variable from "mail/" to + "#md/mail". In a maildir collection folders of any other format + are ignored. + +

Finally, This version also has + support for the Courier style file system + when a maildir collection is accessed locally. + +

+

+ <End of help on this topic> + + ====== h_config_literal_sig ===== *************** *** 31683,31688 **** --- 31779,31827 ----

<End of help on this topic> + + ====== h_config_courier_list ===== + + + FEATURE: <!--#echo var="FEAT_courier-folder-list"--> + + +

FEATURE:

+ + In a maildir collection, a folder could be used as a directory to store + folders. In the Courier server if you create a folder, then a directory + with the same name is created. If you use this patch to access a + collection created by the Courier server, then the display of such + collection will look confusing. The best way to access a maildir + collection created by the Courier server is by using the "#mc/" + prefix instead of the "#md/" prefix. If you use this alternate + prefix, then this feature applies to you, otherwise you can safely ignore + the text that follows. +

+ Depending on if you have enabled the option + + a folder may be listed as "folder[.]", or as two entries in the + list by "folder" and "folder.". +

+ If this option is disabled, Pine will list local folders that are in Courier + style format, as "folder", and those that are also directories as + "folder[.]". This makes the default display cleaner. +

+ If this feature is enabled then creating folders in a maildir collection + will create a directory with the same name. If this feature is disabled, then + a folder is considered a directory only if it contains subfolders, so you can + not create a directory with the same name as an exisiting folder unless + you create a subfolder of that folder first (e.g. if you have a folder + called "foo" simply add "foo.bar" directly. This will + create the directory "foo" and the subfolder "bar" of it). +

+ Observe that this feature works only for maildir collections that are accessed + locally. If a collection is accessed remotely then this feature has no value, + as the report is created in a server, and Pine only reports what received + from the server in this case. +

+ <End of help on this topic> + ====== h_config_verbose_post ===== diff -rc alpine-2.24/pith/send.c alpine-2.24.maildir/pith/send.c *** alpine-2.24/pith/send.c 2020-10-10 00:24:28.204554602 -0600 --- alpine-2.24.maildir/pith/send.c 2020-10-10 00:26:49.996168098 -0600 *************** *** 47,52 **** --- 47,55 ---- #include "../c-client/smtp.h" #include "../c-client/nntp.h" + #ifndef _WINDOWS + int maildir_file_path(char *name, char *tmp, size_t sizeoftmp); + #endif /* _WINDOWS */ /* this is used in pine_send and pine_simple_send */ *************** *** 250,255 **** --- 253,265 ---- if(exists & FEX_ISFILE){ context_apply(tmp, p_cntxt, mbox, sizeof(tmp)); + #ifndef _WINDOWS + if (!struncmp(tmp, "#md/",4) || !struncmp(tmp, "#mc/", 4)){ + char tmp2[MAILTMPLEN]; + maildir_file_path(tmp, tmp2, sizeof(tmp2)); + strcpy(tmp, tmp2); + } + #endif if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){ /* * The mbox is relative to the home directory. diff -rc alpine-2.24/README.maildir alpine-2.24.maildir/README.maildir *** alpine-2.24/README.maildir 2020-10-10 00:26:50.012168504 -0600 --- alpine-2.24.maildir/README.maildir 2020-10-10 00:26:49.996168098 -0600 *************** *** 0 **** --- 1,149 ---- + --------------------------------------- + + Maildir Driver for Alpine 2.0 + By Eduardo Chappa + + + --------------------------------------- + 1. General Information About This Patch + --------------------------------------- + + This patch adds support for the maildir format to Alpine. We take the + approach that this patch is one more driver among the number of formats + supported by Alpine (more generally c-client). This approach differs from + older versions of similar patches, in that once a maildir patch was + applied, it was assumed that all your folders would be created in the + maildir format. + + This patch does not assume that maildir is a preferred format, instead + puts maildir in equal footing with other formats (mbox, mbx, mix, etc), + and so a maildir folder in the mail/ collection is treated in the same way + as any other folder in any other format. In other words, just by reading + the name of a folder, or opening it, or doing any operation with it, you + can not know in which format the folder is. + + This implies that if you want to add a folder in the maildir format to the + mail/ collection, then you must add by pressing "A" in the folder list + collection and enter "#driver.md/mail/name_maildir_folder". + + If you only want to use maildir, however, you can do so too. In this case, + you must create a maildir collection. In that collection, only maildir + folders will be listed. If there is any folder in any other format, that + folder will be ignored. In another words, any folder listed there is in + maildir format and can be accessed through that collection, conversely, + any folder not listed there is not in maildir format and there is no way + to access it using this collection. + + In order to create a maildir collection, you could press M S L, and "A" to + add a collection. Fill in the required fields as follows: + + Nickname : Anything + Server : + Path : #md/relative/path/to/maildir/collection/ + View : + + For example, if "path" is set to "#md/mail/", then Alpine will look for your + maildir folders that are in ~/mail/. + + The code in this patch is mostly based in code for the unix driver plus + some combinations of the mh, mbx and nntp drivers for the c-client + library. Those drivers were designed by Mark Crispin, and bugs in this + code are not his bugs, but my own. + + I got all the specification for this patch from + http://cr.yp.to/proto/maildir.html. If you know of a place with a better + specification for maildir format please let me know. The method this patch + uses to create a unique filename for a message is one of the "old + fashioned" methods. I realize that this is old fashioned, but it is + portable, and portability is the main reason why I decided to use an old + fashioned method (most methods are not portable. See the word + "Unfortunately" in that document). + + -------------- + 2. Other Goals + -------------- + + It is intended that this code will work well with any application + written using the c-client library. Of paramount importance is to make the + associated imap server work well when the server accesses a folder in + Maildir format. The program mailutil should also work flawlessly with this + implemetation of the driver. + + It is intended that this driver be fast and stable. We intend not to + patch Alpine to make this driver do its work, unless such patching is for + fixing bugs in Alpine or to pass parameters to the driver. + + ------------------------------------------------------------------------ + 3. What are the known bugs of this implementation of the Maildir driver? + ------------------------------------------------------------------------ + + I don't know any at this time. There have been bugs before, though, but + I try to fix bugs as soon as they are reported. + + ---------- + 4. On UIDs + ---------- + + This patch keeps uids in the name of the file that contains the message, + by adding a ",u=" string to the file name to save the uid of a message. A + file is kept between sessions to save information on the last uid assigned + and its time of validity. Only one session with writing access can write + uids, all others must wait for the other session to assign them. The + session assigning uids creates a ".uidtemp" file which other sessions must + not disturb. + + Uid support appeared in Alpine 1.00 (snapshot 925), and is experimental, + please report any problems. + + ---------------------------------------------- + 5. Configuring Alpine and Setting up a Maildir + ---------------------------------------------- + + Once this approach was chosen, it implied the following: + + * This patch assumes that your INBOX is located at "$HOME/Maildir". + This is a directory which should have three subdirectories "cur", + "tmp" and "new". Mail is delivered to 'new' and read from 'cur'. I + have added a configuration option "maildir-location" which can be + used to tell Alpine where your Maildir inbox is, in case your system + does not use the above directory (e.g. your system may use + "~/.maildir"). In this case define that variable to be the name of + the directory where your e-mail is being delivered (e.g. + ".maildir"). + + * If you want to use the above configuration as your inbox, you must + define your inbox-path as "#md/inbox" (no quotes). You can define + the inbox-path like above even if you have changed the + maildir-location variable. That's the whole point of that variable. + + ------------------------------------------- + 6. What about Courier/Dovecot file systems? + ------------------------------------------- + + In a courier file system all folders are subfolders of a root folder + called INBOX. Normally INBOX is located at ~/Maildir and subfolders are + "dot" directories in ~/Maildir. For example ~/Maildir/.Trash is a + subfolder of INBOX and is accessed with the nickname "INBOX.Trash". + + You can not access folders in this way unless you preceed them with the + string "#mc/". The purpose of the string "#mc/" is to warn Alpine that a + collection in the Courier format is going to be accessed. Therefore, you + can SELECT a folder like "#mc/INBOX.Trash", but not "INBOX.Trash" + + You can access a collection through a server, but if you want to access a + collection of folders created using the Courier server, you MUST edit your + ".pinerc" file and enter the definition of the collection as follows: + + folder-collections="Anything you want" #mc/INBOX.[] + + You can replace the string "#mc/INBOX." by something different, for example + "#mc/Courier/." will make Alpine search for your collection in ~/Courier. + + You can not add this setting directly into Alpine because Alpine fails to + accept this value from its input, but it takes it correctly when it is + added through the ".pinerc" file. + + You can access your inbox as "#mc/INBOX" or "#md/INBOX". Both definitions + point to the same place. + + Last Updated May 28, 2011