From 6d74aafd90d49d20415dbc10b9f300eb4139fee0 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Tue, 5 Mar 2013 14:30:51 -0500 Subject: [PATCH 01/16] Update with more information. --- man/mh-folders.man | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/man/mh-folders.man b/man/mh-folders.man index 6397dffa..06296bcf 100644 --- a/man/mh-folders.man +++ b/man/mh-folders.man @@ -20,11 +20,35 @@ names beyond those of the host filesystem. .PP .B one message per file .RS 5 -The file name is a positive integer. The filename for a new +The file name is a positive integer. Other files containing metadata or +arbitrary names can exist in a folder; while the preference is that non\-message +files begin with \*(lq.\*(rq, all files that are not positive integers +.B must +be ignored by a +.BR MH \-compatible +implementation. However, implementations are free to indicate to the user +the existence of non\-message files that are not prefixed with a \*(lq.\*(rq. +.PP +The filename for a new message is one greater than the highest numbered message in -the folder; its full path can be shown by -.B mh-path -.IR new . +the folder; its full path can be accessed by the pseudo\-sequence +.I new +(e.g., +.B mhpath +.IR new ). +New messages are +.B only +permitted to be added to a folder at the end of the message number range. +.PP +To add a new message to a folder, the recommended sequence is: +.PP +.IP \(bu 4 +Create a temporary file in the desired folder. +.IP \(bu 4 +Attempt to link the temporary file to the new message number. +.IP \(bu 4 +If successful, remove the temporary file. If the link fails increment the +message number and try again. .PP .RE .B context @@ -74,6 +98,18 @@ showing the (possibly empty) message numbers and/or ranges of message numbers in each sequence. The .B cur sequence has at most just a single message number, not a range. +.PP +Sequence names have a maximum size of 998 characters. Each line is also +limited to a maximum of 998 characters, but RFC\-822 continuation rules +apply; sequences can be continued across multiple lines by prefixing +continuation lines with a whitespace character. +.PP +If an implementation finds messages in a sequence that do not exist, +the sequence file should be updated to remove the missing messages +from the sequence. If a sequence contains no messages, it should be +removed from the sequence file. The exception to this is the +.B cur +sequence, which can refer to a nonexistant message. .RE .PP .SS Locking -- 2.48.1 From 518dfa384e0c188a798134f3370c573ca307c51c Mon Sep 17 00:00:00 2001 From: David Levine Date: Tue, 5 Mar 2013 20:17:08 -0600 Subject: [PATCH 02/16] Added lockmethod pseudocomponent to mhparam so that users can easily find out what locking method was configured in. --- man/mh-folders.man | 15 ++++++++------- man/mhparam.man | 9 +++++++-- uip/mhparam.c | 25 ++++++++++++++++++++----- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/man/mh-folders.man b/man/mh-folders.man index 06296bcf..59de18da 100644 --- a/man/mh-folders.man +++ b/man/mh-folders.man @@ -1,4 +1,4 @@ -.TH MH-MAIL %manext5% "March 3, 2013" "%nmhversion%" +.TH MH-MAIL %manext5% "March 5, 2013" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -15,7 +15,6 @@ A .B nmh folder corresponds to directory. There are no limits on folder names beyond those of the host filesystem. -.I Is that right? .RE .PP .B one message per file @@ -31,7 +30,7 @@ the existence of non\-message files that are not prefixed with a \*(lq.\*(rq. .PP The filename for a new message is one greater than the highest numbered message in -the folder; its full path can be accessed by the pseudo\-sequence +the folder; its full path can be accessed by the pseudo\-sequence .I new (e.g., .B mhpath @@ -120,12 +119,13 @@ these files when accessing them. Any program outside of that accesses these files must be sure to lock them using the same locking method. The locking method is selected when .B nmh -is configured. By default, kernel-level locking is used if -appropriate for the platform, and it is for popular platforms. That -default should also be the same as used by the +is configured and can be accessed as a string using +.BR "mhparam lockmethod" . +By default, kernel-level locking is used if appropriate for the +platform, and it is for popular platforms. That default should also +be the same as used by the .B mail program, if provided on the platform. -.I Should we add a lockmethod component to mhparam so users can easily detect it? .SH FILES .fc ^ ~ .nf @@ -137,6 +137,7 @@ program, if provided on the platform. .SH "SEE ALSO" .I .IR mail (1), +.IR mh\-param (1), .IR mh\-path (1), .IR mh\-profile (5), .IR mh\-sequence (5) diff --git a/man/mhparam.man b/man/mhparam.man index cc614ef0..6f51ffd9 100644 --- a/man/mhparam.man +++ b/man/mhparam.man @@ -50,9 +50,11 @@ the locations of the nmh .I etcdir and .I libdir -install directories and all +install directories, all .I proc -settings. The +settings, and the locking method with which the +.B nmh +installation was configured. The .B \-debug switch displays all such other information available from .BR mhparam . @@ -88,6 +90,9 @@ context: context % mhparam libdir %libdir% + +% mhparam lockmethod +fcntl .fi .RE .PP diff --git a/uip/mhparam.c b/uip/mhparam.c index c4b0af2e..414255ba 100644 --- a/uip/mhparam.c +++ b/uip/mhparam.c @@ -12,11 +12,6 @@ #include -extern char *mhlibdir; -extern char *mhetcdir; - -char *sbackup = BACKUP_PREFIX; - #define MHPARAM_SWITCHES \ X("components", 0, COMPSW) \ X("nocomponents", 0, NCOMPSW) \ @@ -33,6 +28,25 @@ DEFINE_SWITCH_ENUM(MHPARAM); DEFINE_SWITCH_ARRAY(MHPARAM, switches); #undef X +extern char *mhlibdir; +extern char *mhetcdir; + +char *sbackup = BACKUP_PREFIX; + +char *lockmethod = +#if defined FCNTL_LOCKING + "fcntl" +#elif defined FLOCK_LOCKING + "flock" +#elif defined LOCKF_LOCKING + "lockf" +#elif defined DOT_LOCKING + "dot" +#else + "none" +#endif + ; + struct proc { char *p_name; char **p_field; @@ -65,6 +79,7 @@ static struct proc procs [] = { { "etcdir", &mhetcdir }, { "libdir", &mhlibdir }, { "sbackup", &sbackup }, + { "lockmethod", &lockmethod }, { NULL, NULL }, }; -- 2.48.1 From f22012d7e3f223403faf35e9e39c4b60114fc8be Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 7 Mar 2013 20:11:22 -0600 Subject: [PATCH 03/16] In the locking discussion of mh-folders(5), discourage direct access to the nmh state files. --- man/mh-folders.man | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/man/mh-folders.man b/man/mh-folders.man index 59de18da..e6b1d442 100644 --- a/man/mh-folders.man +++ b/man/mh-folders.man @@ -1,4 +1,4 @@ -.TH MH-MAIL %manext5% "March 5, 2013" "%nmhversion%" +.TH MH-MAIL %manext5% "March 7, 2013" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -46,7 +46,7 @@ Create a temporary file in the desired folder. .IP \(bu 4 Attempt to link the temporary file to the new message number. .IP \(bu 4 -If successful, remove the temporary file. If the link fails increment the +If successful, remove the temporary file. If the link fails, increment the message number and try again. .PP .RE @@ -82,7 +82,7 @@ is a message number or range of message numbers in the sequence. .B sequences .RS 5 There is one sequences file in each -.B .nmh +.B nmh folder. Its default name is .IR \&.mh\(rusequences , but that can be overridden with the \*(lqmh\-sequences\*(rq profile entry. @@ -114,10 +114,21 @@ sequence, which can refer to a nonexistant message. .SS Locking .B nmh programs read and write the context and sequences files, and lock -these files when accessing them. Any program outside of +these files when accessing them. There should not be a need to +access these files directly; instead, programs such as +.BR flist , +.BR folder , +.BR mark , +.BR pick , +and +.B rcvstore +should be used to query and update their contents. Any program +outside of .B nmh that accesses these files must be sure to lock them using the same -locking method. The locking method is selected when +locking method as +.BR nmh . +The locking method is selected when .B nmh is configured and can be accessed as a string using .BR "mhparam lockmethod" . @@ -129,15 +140,20 @@ program, if provided on the platform. .SH FILES .fc ^ ~ .nf -.ta \w'%etcdir%/ExtraBigFileName 'u +.ta \w'^/\&.mh\(rusequences~'u ^/context~^The user context ^or $MHCONTEXT~^Rather than the standard context ^/\&.mh\(rusequences~^Public sequences for .fi .SH "SEE ALSO" .I +.IR flist (1), +.IR folder (1), .IR mail (1), +.IR mark (1), .IR mh\-param (1), .IR mh\-path (1), .IR mh\-profile (5), -.IR mh\-sequence (5) +.IR mh\-sequence (5), +.IR pick (1), +.IR rcvstore (1) -- 2.48.1 From 0f1f3f43ff17248c7de3da2494614bc7a880f478 Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 8 Mar 2013 06:40:07 -0600 Subject: [PATCH 04/16] Fixed spelling of "preserve" in refile man page. --- man/refile.man | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/refile.man b/man/refile.man index 55b78eb9..a111046d 100644 --- a/man/refile.man +++ b/man/refile.man @@ -7,7 +7,7 @@ refile \- file message in other folders .SH SYNOPSIS .HP 5 .na -.B refile +.B refile .RI [ msgs ] .RB [ \-draft ] .RB [ \-link " | " \-nolink ] @@ -103,7 +103,7 @@ whereas, Normally when a message is refiled, for each destination folder it is assigned the number which is one above the current highest message number in that folder. Use of the -.B \-preserv +.B \-preserve switch will override this message renaming, and try to preserve the number of the message. If a conflict for a particular folder occurs when using the -- 2.48.1 From 76ddfab4d76264b1cb30d7df642965c141e61545 Mon Sep 17 00:00:00 2001 From: David Levine Date: Mon, 11 Mar 2013 18:42:16 -0500 Subject: [PATCH 05/16] Ignore the sign mismatch warning when sbr/dtimep.c is built with flex 2.5.36 or 2.5.37. --- docs/contrib/build_nmh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contrib/build_nmh b/docs/contrib/build_nmh index f17b240f..0b9e582e 100755 --- a/docs/contrib/build_nmh +++ b/docs/contrib/build_nmh @@ -308,7 +308,9 @@ if [ $status -eq 0 ]; then fi grep 'Error' "$logfile" -grep 'warn' "$logfile" +#### Ignore the warning when sbr/dtimep.c is built with flex 2.5.36 +#### or 2.5.37. +grep 'warn' "$logfile" | grep -v 'sbr/dtimep.c:.*-Wsign-compare' if [ $status -ne 0 ]; then echo build failed! echo build log is in "$logfile" -- 2.48.1 From e3c346c0fd80d1c88bc9e6902dcc4674c82d41b9 Mon Sep 17 00:00:00 2001 From: David Levine Date: Mon, 11 Mar 2013 18:57:01 -0500 Subject: [PATCH 06/16] Added check that writesomecmd() isn't at the end of argp before dereferencing ++argp. I'm surprised that no one, including me, tripped over this before: whatnow(1) would seg fault when handling, e.g., detach -n 4. But until I upgraded to Fedora 18, I never noticed it. (valgrind did.) --- uip/whatnowsbr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uip/whatnowsbr.c b/uip/whatnowsbr.c index 0beba2b1..a084bafc 100644 --- a/uip/whatnowsbr.c +++ b/uip/whatnowsbr.c @@ -552,10 +552,10 @@ writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp) int trailln = strlen(trailcmd) + 4; if (ln < 0 || ln + trailln > bufsz) adios((char *)0, "arguments too long"); - + cp = buf + ln; - - while (*++argp != (char *)0) { + + while (*argp && *++argp) { ln = strlen(*argp); /* +1 for leading space */ if (ln + trailln + 1 > bufsz - (cp-buf)) @@ -618,7 +618,7 @@ popen_in_dir(const char *dir, const char *cmd, const char *type) /* ensure that $SHELL exists, as the cmd was written relying on a non-blank $SHELL... */ setenv("SHELL","/bin/sh",0); /* don't overwrite */ - + if (getcwd(olddir, sizeof(olddir)) == 0) adios("getcwd", "could not get working directory"); if (chdir(dir) != 0) -- 2.48.1 From 70fb5e6d9b84d77bbd816a23f7e5b1ad7b313f7f Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Tue, 12 Mar 2013 12:57:41 -0400 Subject: [PATCH 07/16] Change sample disposition from "application" to "attachment" --- man/mhbuild.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/mhbuild.man b/man/mhbuild.man index cbe2f4f5..a2ce2054 100644 --- a/man/mhbuild.man +++ b/man/mhbuild.man @@ -234,7 +234,7 @@ separated accordingly. For example, type=tar; \\ conversions=compress \\ [this is the nmh distribution] \\ - {application; filename="nmh.tar.gz"} \\ + {attachment; filename="nmh.tar.gz"} \\ name="nmh.tar.gz"; \\ directory="/pub/nmh"; \\ site="ftp.math.gatech.edu"; \\ -- 2.48.1 From 9d211736c4ff802eef68d2264feacbe001f83e61 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Tue, 12 Mar 2013 14:22:49 -0400 Subject: [PATCH 08/16] Add support for RFC-2017, message/external-body content which contains URLs. --- config/config.c | 3 + docs/pending-release-notes | 2 + h/mh.h | 1 + h/mhparse.h | 1 + man/mhbuild.man | 19 ++++- man/mhshow.man | 11 +++ man/mhstore.man | 16 ++++ uip/mhbuildsbr.c | 49 +++++++++++- uip/mhfree.c | 2 + uip/mhparse.c | 157 +++++++++++++++++++++++++++++++++++++ 10 files changed, 256 insertions(+), 5 deletions(-) diff --git a/config/config.c b/config/config.c index d29f8a2e..4024d305 100644 --- a/config/config.c +++ b/config/config.c @@ -138,6 +138,9 @@ char *nmhprivcache = "nmh-private-cache"; /* profile entry for external ftp access command */ char *nmhaccessftp = "nmh-access-ftp"; +/* profile entry for external url access command */ +char *nmhaccessurl = "nmh-access-url"; + char *mhlibdir = NMHLIBDIR; char *mhetcdir = NMHETCDIR; diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 97bdf02d..94a62af3 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -42,6 +42,8 @@ NEW FEATURES contain spaces and shell metacharacters. If found, such entries will either be space-splitted or processed by /bin/sh. - A new program, fmttest(1) is included to help debug format files +- mhshow/mhstore now have support for RFC-2017 (access-type=url) for + external message bodies. ---------------------------- OBSOLETE/DEPRECATED FEATURES diff --git a/h/mh.h b/h/mh.h index b8e56442..daf47c02 100644 --- a/h/mh.h +++ b/h/mh.h @@ -387,6 +387,7 @@ extern char *moreproc; extern char *msgprot; extern char *mshproc; extern char *nmhaccessftp; +extern char *nmhaccessurl; extern char *nmhstorage; extern char *nmhcache; extern char *nmhprivcache; diff --git a/h/mhparse.h b/h/mhparse.h index d7bdb344..57bdabd3 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -231,6 +231,7 @@ struct exbody { char *eb_server; char *eb_subject; char *eb_body; + char *eb_url; }; /* diff --git a/man/mhbuild.man b/man/mhbuild.man index a2ce2054..05ed1d32 100644 --- a/man/mhbuild.man +++ b/man/mhbuild.man @@ -255,7 +255,7 @@ These parameters are of the form: .RS 5 .nf .ta \w'access-type= 'u -access-type= usually \fIanon-ftp\fR or \fImail-server\fR +access-type= usually \fIanon-ftp\fR, \fImail-server\fR, or \fIurl\fR name= filename permission= read-only or read-write site= hostname @@ -265,9 +265,24 @@ size= number of octets server= mailbox subject= subject to send body= command to send for retrieval +url= URL of content .fi .RE .PP +A mimimum \*(lqexternal\-type\*(rq directive for the +.B url +.I access\-type +would be as follows: +.PP +.RS 3 +.nf +#@application/octet-stream [] access-type=url; \\ + url="http://download.savannah.gnu.org/releases/nmh/nmh-1.5.tar.gz" +.fi +.RE +.PP +Any long URLs will be wrapped according to RFC\-2017 rules. +.PP The \*(lqmessage\*(rq directive (#forw) is used to specify a message or group of messages to include. You may optionally specify the name of the folder and which messages are to be forwarded. If a folder is not @@ -680,6 +695,8 @@ line ::= "##" text EOL .PP .I "Multipurpose Internet Mail Extensions (MIME) Part Five: Conformance Criteria and Examples" (RFC\-2049) +.I "Definition of the URL MIME External-Body Access-Type" +(RRC\-2017) .SH DEFAULTS .nf .RB ` \-headers ' diff --git a/man/mhshow.man b/man/mhshow.man index b2bc6f66..352bcfca 100644 --- a/man/mhshow.man +++ b/man/mhshow.man @@ -436,6 +436,8 @@ ftp local-file .IP \(bu 4 mail-server +.IP \(bu 4 +url .PP For the \*(lqanon-ftp\*(rq and \*(lqftp\*(rq access types, .B mhshow @@ -464,6 +466,14 @@ local filename .PP The program should terminate with an exit status of zero if the retrieval is successful, and a non-zero exit status otherwise. +.PP +For the \*(lqurl\*(rq access\-type, +.B mhshow +will look for the \*(lqnmh-access-url\*(rq +profile entry. See +.IR mhstore (1) +for more details. +.PP .SS "The Content Cache" When .B mhshow @@ -569,6 +579,7 @@ installation. ^Unseen\-Sequence:~^To name sequences denoting unseen messages ^mhlproc:~^Default program to display message headers ^nmh-access-ftp:~^Program to retrieve contents via FTP +^nmh-access-url:~^Program to retrieve contents via HTTP ^nmh-cache~^Public directory to store cached external contents ^nmh-private-cache~^Personal directory to store cached external contents ^mhshow-charset-~^Template for environment to render character sets diff --git a/man/mhstore.man b/man/mhstore.man index 35696494..ccf1ee9c 100644 --- a/man/mhstore.man +++ b/man/mhstore.man @@ -395,6 +395,8 @@ ftp local-file .IP \(bu 4 mail-server +.IP \(bu 4 +url .PP For the \*(lqanon-ftp\*(rq and \*(lqftp\*(rq access types, .B mhstore @@ -422,6 +424,19 @@ local filename .PP The program should terminate with an exit status of zero if the retrieval is successful, and a non-zero exit status otherwise. +.PP +For the \*(lqurl\*(rq access types, +.B mhstore +will look for the \*(lqnmh-access-url\*(rq profile entry, e.g., +.PP +.RS 5 +nmh-access-url: curl -l +.RE +.PP +to determine the program to use to perform the HTTP retrieval. This program +is invoked with one argument: the URL of the content to retrieve. The program +should write the content to standard out, and should terminate with a status of zero if the retrieval is successful and a non\-zero exit status otherwise. +.PP .SS "The Content Cache" When .B mhstore @@ -514,6 +529,7 @@ installation. ^Path:~^To determine the user's nmh directory ^Current\-Folder:~^To find the default current folder ^nmh-access-ftp:~^Program to retrieve contents via FTP +^nmh-access-url:~^Program to retrieve contents via HTTP ^nmh-cache~^Public directory to store cached external contents ^nmh-private-cache~^Personal directory to store cached external contents ^nmh-storage~^Directory to store contents diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index d2d033f1..8bac6d37 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -54,6 +54,12 @@ pid_t xpid = 0; static char prefix[] = "----- =_aaaaaaaaaa"; +/* + * Maximum size of URL token in message/external-body + */ + +#define MAXURLTOKEN 40 + /* mhmisc.c */ void content_error (char *, CT, char *, ...); @@ -1360,7 +1366,7 @@ scan_content (CT ct) static int build_headers (CT ct) { - int cc, mailbody, len; + int cc, mailbody, extbody, len; char **ap, **ep; char *np, *vp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; @@ -1402,9 +1408,8 @@ build_headers (CT ct) len = strlen (TYPE_FIELD) + strlen (ci->ci_type) + strlen (ci->ci_subtype) + 3; - mailbody = ct->c_type == CT_MESSAGE - && ct->c_subtype == MESSAGE_EXTERNAL - && ((struct exbody *) ct->c_ctparams)->eb_body; + extbody = ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_EXTERNAL; + mailbody = extbody && ((struct exbody *) ct->c_ctparams)->eb_body; /* * Append the attribute/value pairs to @@ -1417,6 +1422,42 @@ build_headers (CT ct) vp = add (";", vp); len++; + /* + * According to RFC 2017, if we have a URL longer than 40 characters + * we have to break it across multiple lines + */ + + if (extbody && mh_strcasecmp (*ap, "url") == 0) { + char *value = *ep; + + /* 7 here refers to " url=\"\"" */ + if (len + 1 + (cc = (min(MAXURLTOKEN, strlen(value)) + 7)) >= + CPERLIN) { + vp = add ("\n\t", vp); + len = 8; + } else { + vp = add (" ", vp); + len++; + } + + vp = add ("url=\"", vp); + len += 5; + + while (strlen(value) > MAXURLTOKEN) { + strncpy(buffer, value, MAXURLTOKEN); + buffer[MAXURLTOKEN] = '\0'; + vp = add (buffer, vp); + vp = add ("\n\t", vp); + value += MAXURLTOKEN; + len = 8; + } + + vp = add (value, vp); + vp = add ("\"", vp); + len += strlen(value) + 1; + continue; + } + snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { vp = add ("\n\t", vp); diff --git a/uip/mhfree.c b/uip/mhfree.c index 83ed5366..cf9e548e 100644 --- a/uip/mhfree.c +++ b/uip/mhfree.c @@ -246,6 +246,8 @@ free_external (CT ct) free_content (e->eb_content); if (e->eb_body) free (e->eb_body); + if (e->eb_url) + free (e->eb_url); free ((char *) e); ct->c_ctparams = NULL; diff --git a/uip/mhparse.c b/uip/mhparse.c index 62034555..4a70610e 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -128,6 +128,8 @@ static int InitMail (CT); static int openMail (CT, char **); static int readDigest (CT, char *); static int get_leftover_mp_content (CT, int); +static int InitURL (CT); +static int openURL (CT, char **); struct str2init str2cts[] = { { "application", CT_APPLICATION, InitApplication }, @@ -162,6 +164,7 @@ struct str2init str2methods[] = { { "ftp", 0, InitFTP }, { "local-file", 0, InitFile }, { "mail-server", 0, InitMail }, + { "url", 0, InitURL }, { NULL, 0, NULL } }; @@ -1438,6 +1441,7 @@ invalid_param: e->eb_parent = ct; e->eb_content = p; p->c_ctexbody = e; + p->c_ceopenfnx = NULL; if ((exresult = params_external (ct, 0)) != NOTOK && p->c_ceopenfnx == openMail) { int cc, size; @@ -1510,6 +1514,7 @@ params_external (CT ct, int composing) struct exbody *e = (struct exbody *) ct->c_ctparams; CI ci = &ct->c_ctinfo; + ct->c_ceopenfnx = NULL; for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { if (!mh_strcasecmp (*ap, "access-type")) { struct str2init *s2i; @@ -1565,6 +1570,23 @@ params_external (CT ct, int composing) e->eb_subject = *ep; continue; } + if (!mh_strcasecmp (*ap, "url")) { + /* + * According to RFC 2017, we have to remove all whitespace from + * the URL + */ + + char *u, *p = *ep; + e->eb_url = u = mh_xmalloc(strlen(*ep) + 1); + + for (; *p != '\0'; p++) { + if (! isspace((unsigned char) *p)) + *u++ = *p; + } + + *u = '\0'; + continue; + } if (composing && !mh_strcasecmp (*ap, "body")) { e->eb_body = getcpy (*ep); continue; @@ -2799,6 +2821,141 @@ openMail (CT ct, char **file) } +/* + * URL + */ + +static int +InitURL (CT ct) +{ + return init_encoding (ct, openURL); +} + + +static int +openURL (CT ct, char **file) +{ + struct exbody *e = ct->c_ctexbody; + CE ce = ct->c_cefile; + char *urlprog, *program; + char buffer[BUFSIZ], cachefile[BUFSIZ]; + int fd, caching, cachetype; + struct msgs_array args = { 0, 0, NULL}; + pid_t child_id; + + if ((urlprog = context_find(nmhaccessurl)) && *urlprog == '\0') + urlprog = NULL; + + if (! urlprog) { + content_error(NULL, ct, "No entry for nmh-access-url in profile"); + return NOTOK; + } + + switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) { + case NOTOK: + return NOTOK; + + case OK: + break; + + case DONE: + return fd; + } + + if (!e->eb_url) { + content_error(NULL, ct, "missing url parameter"); + return NOTOK; + } + + if (xpid) { + if (xpid < 0) + xpid = -xpid; + pidcheck (pidwait (xpid, NOTOK)); + xpid = 0; + } + + ce->ce_unlink = (*file == NULL); + caching = 0; + cachefile[0] = '\0'; + + if (find_cache(NULL, wcachesw, &cachetype, e->eb_content->c_id, + cachefile, sizeof(cachefile)) != NOTOK) { + if (*file == NULL) { + ce->ce_unlink = 0; + caching = 1; + } + } + + if (*file) + ce->ce_file = add(*file, NULL); + else if (caching) + ce->ce_file = add(cachefile, NULL); + else + ce->ce_file = add(m_mktemp(tmp, NULL, NULL), NULL); + + if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) { + content_error(ce->ce_file, ct, "unable to fopen for read/writing"); + return NOTOK; + } + + switch (child_id = fork()) { + case NOTOK: + adios ("fork", "unable to"); + /* NOTREACHED */ + + case OK: + argsplit_msgarg(&args, urlprog, &program); + app_msgarg(&args, e->eb_url); + app_msgarg(&args, NULL); + dup2(fileno(ce->ce_fp), 1); + close(fileno(ce->ce_fp)); + execvp(program, args.msgs); + fprintf(stderr, "Unable to exec "); + perror(program); + _exit(-1); + /* NOTREACHED */ + + default: + if (pidXwait(child_id, NULL)) { + ce->ce_unlink = 1; + return NOTOK; + } + } + + if (cachefile[0]) { + if (caching) + chmod(cachefile, cachetype ? m_gmprot() : 0444); + else { + int mask; + FILE *fp; + + mask = umask (cachetype ? ~m_gmprot() : 0222); + if ((fp = fopen(cachefile, "w"))) { + int cc; + FILE *gp = ce->ce_fp; + + fseeko(gp, 0, SEEK_SET); + + while ((cc = fread(buffer, sizeof(*buffer), + sizeof(buffer), gp)) > 0) + fwrite(buffer, sizeof(*buffer), cc, fp); + + fflush(fp); + + if (ferror(gp)) { + admonish(ce->ce_file, "error reading"); + unlink(cachefile); + } + } + umask(mask); + } + } + + fseeko(ce->ce_fp, 0, SEEK_SET); + *file = ce->ce_file; + return fd; +} + static int readDigest (CT ct, char *cp) { -- 2.48.1 From 4823929d7c53e0cb167884ef35c34db34fef2729 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Wed, 13 Mar 2013 21:02:48 -0400 Subject: [PATCH 09/16] Start of the changes for the new lock code. Does not compile yet. --- configure.ac | 40 +++------------------------------ h/mh.h | 6 +++++ h/prototypes.h | 16 ++++++++++++-- m4/locking.m4 | 48 ++++++++++++++++++++++++++++++++++++++++ sbr/lock_file.c | 59 +++++++++++++++++++++++++++++++------------------ 5 files changed, 109 insertions(+), 60 deletions(-) create mode 100644 m4/locking.m4 diff --git a/configure.ac b/configure.ac index 238e17a4..96a99022 100644 --- a/configure.ac +++ b/configure.ac @@ -62,43 +62,6 @@ AS_IF([test x"$with_hash_backup" != x -a x"$with_hash_backup" != x"no"], AC_DEFINE_UNQUOTED([BACKUP_PREFIX], "$backup_prefix", [The prefix that is prepended to the name of message files when they are "removed" by rmm. This should typically be `,' or `#'.])dnl -dnl What method of locking to use? -AS_CASE(["$host_os"], - [aix*|cygwin*|linux*], - [default_locktype="fcntl"; default_locking=FCNTL_LOCKING], - [freebsd*|openbsd*|darwin*], [default_locktype="flock"; default_locking=FLOCK_LOCKING], - [default_locktype="dot"; default_locking=DOT_LOCKING]) - -AC_ARG_WITH([locking], - AS_HELP_STRING([--with-locking=@<:@dot|fcntl|flock|lockf@:>@], - [specify the file locking method])) - -AS_IF([test x"$with_locking" = x"dot"], - [LOCKTYPE="dot" - AC_DEFINE([DOT_LOCKING], [1], [Define to use dot based file locking.])], - [test x"$with_locking" = x"flock"], - [LOCKTYPE="flock" - AC_DEFINE([FLOCK_LOCKING], [1], [Define to use flock() based locking.])], - [test x"$with_locking" = x"lockf"], - [LOCKTYPE="lockf" - AC_DEFINE([LOCKF_LOCKING], [1], [Define to use lockf() based locking.])], - [test x"$with_locking" = x"fcntl"], - [LOCKTYPE="fcntl" - AC_DEFINE([FCNTL_LOCKING], [1], [Define to use fnctl() based locking.])], - [LOCKTYPE="$default_locktype" - AC_DEFINE_UNQUOTED([$default_locking], [1])]) - -dnl Should we use a locking directory? -AC_ARG_ENABLE([lockdir], - [AS_HELP_STRING([--enable-lockdir=dir], [Store dot-lock files in "dir"])], [ - AS_IF([test "x$enableval" = xyes],[ - AC_MSG_ERROR([--enable-lockdir requires an argument])]) - AS_IF([test "x$LOCKTYPE" != xdot],[ - AC_MSG_ERROR([Can only use --enable-lockdir with dot locking])]) - AC_DEFINE_UNQUOTED([LOCKDIR], ["$enableval"], - [Directory to store dot-locking lock files]) -]) - dnl What method of posting should post use? AC_ARG_WITH([mts], AS_HELP_STRING([--with-mts=@<:@smtp|sendmail/smtp|sendmail/pipe@:>@], @@ -409,6 +372,9 @@ NMH_CHECK_NETLIBS dnl Check for readline support NMH_READLINE +dnl Check the locking functions supported and what we should use by default +NMH_LOCKING + dnl Check for iconv NMH_CHECK_ICONV diff --git a/h/mh.h b/h/mh.h index daf47c02..2d977446 100644 --- a/h/mh.h +++ b/h/mh.h @@ -316,6 +316,12 @@ typedef struct m_getfld_state *m_getfld_state_t; #define NMH_ATTACH_HEADER "Nmh-Attachment" /* Default header for -attach */ +/* + * The type of locking we support + */ + +enum locktype { FCNTL_LOCKING, FLOCK_LOCKING, LOCKF_LOCKING, DOT_LOCKING }; + /* * miscellaneous macros */ diff --git a/h/prototypes.h b/h/prototypes.h index ae6a115e..fff761d4 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -1,6 +1,9 @@ /* * prototypes.h -- various prototypes + * + * If you modify functions here, please document their current behavior + * as much as practical. */ /* @@ -76,10 +79,19 @@ char *get_charset(void); char *getcpy (char *); char *get_default_editor(void); char *getfolder(int); +/* + * Parse a lock type and return the type of lock it is + */ +enum locktype init_locktype(const char *); +/* + * Lock open/close routines. The 3rd argument to lkopen/lfkopen is an + * integer which indicates the type of lock. A "0" means a shared (read) + * lock, and a "1" indicates an exclusive (write) lock. + */ int lkclose(int, char*); int lkfclose(FILE *, char *); -FILE *lkfopen(char *, char *); -int lkopen(char *, int, mode_t); +FILE *lkfopen(char *, char *, int); +int lkopen(char *, int, mode_t, int); int m_atoi (char *); char *m_backup (char *); int m_convert (struct msgs *, char *); diff --git a/m4/locking.m4 b/m4/locking.m4 new file mode 100644 index 00000000..e013790a --- /dev/null +++ b/m4/locking.m4 @@ -0,0 +1,48 @@ +dnl +dnl Our functions to check the locking functions available and select a +dnl default locking function for the spool file +dnl +dnl Since we're assuming POSIX as a minimum, we always assume we have fcntl +dnl locking available. +dnl + +AC_DEFUN([NMH_LOCKING], +[AC_CHECK_FUNCS([flock lockf]) +AS_CASE(["$host_os"], + [aix*|cygwin*|linux*], + [default_locktype="fcntl"], + [freebsd*|openbsd*|darwin*], [default_locktype="flock"], + [default_locktype="dot"]) + +AC_MSG_CHECKING([default locking method for the mail spool]) + +AC_ARG_WITH([locking], + AS_HELP_STRING([--with-locking=@<:@dot|fcntl|flock|lockf@:>@], + [The default locking method for the mail spool file]), , + [with_locking="$default_locktype"]) + +AS_CASE([$with_locking], + [fcntl|dot], , + [flock], + [AS_IF([test x"$ac_cv_func_flock" != x"yes"], + [AC_MSG_ERROR([flock locks not supported on this system])])], + [lockf], + [AS_IF([test x"$ac_cv_func_lockf" != x"yes"], + [AC_MSG_ERROR([lockf locks not supported on this system])])], + [no], + [AC_MSG_ERROR([--without-locking not supported])], + [AC_MSG_ERROR([Unknown locking type $with_locking])]) + +AC_DEFINE_UNQUOTED([DEFAULT_LOCKING], [$with_locking], + [The default lock type for the mail spool file]) + +AC_MSG_RESULT([$with_locking]) + +dnl Should we use a locking directory? +AC_ARG_ENABLE([lockdir], + [AS_HELP_STRING([--enable-lockdir=dir], [Store dot-lock files in "dir"])], [ + AS_IF([test "x$enableval" = xyes],[ + AC_MSG_ERROR([--enable-lockdir requires an argument])]) + AC_DEFINE_UNQUOTED([LOCKDIR], ["$enableval"], + [Directory to store dot-locking lock files]) +])]) diff --git a/sbr/lock_file.c b/sbr/lock_file.c index bbfc8fc7..6653a28b 100644 --- a/sbr/lock_file.c +++ b/sbr/lock_file.c @@ -24,15 +24,12 @@ #endif #include #include - -#ifdef HAVE_FCNTL_H -# include -#else +#include +#ifdef HAVE_FLOCK # include #endif - -#if defined(LOCKF_LOCKING) || defined(FLOCK_LOCKING) -# include +#ifdef HAVE_LOCKF +# include #endif #include @@ -45,13 +42,6 @@ char *lockdir = LOCKDIR; #endif -/* Are we using any kernel locking? */ -#if defined (FLOCK_LOCKING) || defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING) -# define KERNEL_LOCKING -#endif - -#ifdef DOT_LOCKING - /* struct for getting name of lock file to create */ struct lockinfo { char curlock[BUFSIZ]; @@ -83,14 +73,11 @@ struct lock { /* top of list containing all open locks */ static struct lock *l_top = NULL; -#endif /* DOT_LOCKING */ -/* - * static prototypes - */ -#ifdef KERNEL_LOCKING -static int lkopen_kernel (char *, int, mode_t); -#endif +static int lkopen_fcntl (char *, int, mode_t, int); +#ifdef HAVE_LOCKF +static int lkopen_lockf (char *, int, mode_t, int); +#endif /* HAVE_LOCKF */ #ifdef DOT_LOCKING static int lkopen_dot (char *, int, mode_t); @@ -609,3 +596,33 @@ alrmser (int sig) } #endif /* DOT_LOCKING */ + +/* + * Return a locking algorithm based on the string name + */ + +enum locktype +init_locktype(const char *lockname) +{ + if (mh_strcasecmp(lockname, "fcntl") == 0) { + return FCNTL_LOCKING; + } else if (mh_strcasecmp(lockname, "lockf") == 0) { +#ifdef HAVE_LOCKF + return LOCKF_LOCKING; +#else /* ! HAVE_LOCKF */ + adios(NULL, "lockf not supported on this system"); +#endif /* HAVE_LOCKF */ + } else if (mh_strcasecmp(lockname, "flock") == 0) { +#ifdef HAVE_FLOCK + return FLOCK_LOCKING; +#else /* ! HAVE_FLOCK */ + adios(NULL, "flock not supported on this system"); +#endif /* HAVE_FLOCK */ + } else if (mh_strcasecmp(lockname, "dot") == 0) { + return DOT_LOCKING; + } else { + adios(NULL, "Unknown lock type: \"%s\"", lockname) + /* NOTREACHED */ + return 0; + } +} -- 2.48.1 From 182f6368f75799e25563cbf355923a72c995078b Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Thu, 14 Mar 2013 15:57:07 -0400 Subject: [PATCH 10/16] More locking updates; still doesn't compile yet. --- configure.ac | 3 +- h/mh.h | 6 ---- h/prototypes.h | 30 +++++++++++------ sbr/context_read.c | 4 +-- sbr/context_save.c | 4 +-- sbr/lock_file.c | 84 +++++++++++++++++++++++++++++++++++++--------- 6 files changed, 93 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index 96a99022..bfa3c518 100644 --- a/configure.ac +++ b/configure.ac @@ -646,9 +646,8 @@ man page install path : ${nmhman} RPM build root : ${nmhrpm} backup prefix : ${backup_prefix} transport system : ${MTS} -file locking type : ${LOCKTYPE} +spool default locking type : ${with_locking} default smtp servers : ${smtpservers} -default editor : ${editorpath} SASL support : ${sasl_support} TLS support : ${tls_support} ])])dnl diff --git a/h/mh.h b/h/mh.h index 2d977446..daf47c02 100644 --- a/h/mh.h +++ b/h/mh.h @@ -316,12 +316,6 @@ typedef struct m_getfld_state *m_getfld_state_t; #define NMH_ATTACH_HEADER "Nmh-Attachment" /* Default header for -attach */ -/* - * The type of locking we support - */ - -enum locktype { FCNTL_LOCKING, FLOCK_LOCKING, LOCKF_LOCKING, DOT_LOCKING }; - /* * miscellaneous macros */ diff --git a/h/prototypes.h b/h/prototypes.h index fff761d4..921021d7 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -80,18 +80,26 @@ char *getcpy (char *); char *get_default_editor(void); char *getfolder(int); /* - * Parse a lock type and return the type of lock it is - */ -enum locktype init_locktype(const char *); -/* - * Lock open/close routines. The 3rd argument to lkopen/lfkopen is an - * integer which indicates the type of lock. A "0" means a shared (read) - * lock, and a "1" indicates an exclusive (write) lock. + * Lock open/close routines. + * + * The lk[f]opendata() functions are designed to open "data" files (anything + * not a mail spool file) using the locking mechanism configured for data + * files. The lk[f]openspool() functions are for opening the mail spool + * file, which will use the locking algorithm configured for the mail + * spool. + * + * All of these functions have a third argument, an integer which + * indicates the type of lock. A "0" means a shared (read) lock, and a + * "1" indicates an exclusive (write) lock. */ -int lkclose(int, char*); -int lkfclose(FILE *, char *); -FILE *lkfopen(char *, char *, int); -int lkopen(char *, int, mode_t, int); +int lkclosedata(int, char*); +int lkclosespool(int, char*); +int lkfclosedata(FILE *, char *); +int lkfclosespool(FILE *, char *); +FILE *lkfopendata(char *, char *, int); +int lkopendata(char *, int, mode_t, int); +FILE *lkfopenspool(char *, char *, int); +int lkopenspool(char *, int, mode_t, int); int m_atoi (char *); char *m_backup (char *); int m_convert (struct msgs *, char *); diff --git a/sbr/context_read.c b/sbr/context_read.c index a655effc..2008c26b 100644 --- a/sbr/context_read.c +++ b/sbr/context_read.c @@ -136,9 +136,9 @@ context_read (void) ctxpath = getcpy (m_maildir (cp)); - if ((ib = lkfopen (ctxpath, "r"))) { + if ((ib = lkfopendata (ctxpath, "r", 0))) { readconfig ((struct node **) 0, ib, cp, 1); - lkfclose (ib, ctxpath); + lkfclosedata (ib, ctxpath); } return; diff --git a/sbr/context_save.c b/sbr/context_save.c index 33cd23dd..86776c7d 100644 --- a/sbr/context_save.c +++ b/sbr/context_save.c @@ -43,12 +43,12 @@ context_save (void) sigaddset (&set, SIGTERM); sigprocmask (SIG_BLOCK, &set, &oset); - if (!(out = lkfopen (ctxpath, "w"))) + if (!(out = lkfopendata (ctxpath, "w", 1))) adios (ctxpath, "unable to write"); for (np = m_defs; np; np = np->n_next) if (np->n_context) fprintf (out, "%s: %s\n", np->n_name, np->n_field); - lkfclose (out, ctxpath); + lkfclosedata (out, ctxpath); sigprocmask (SIG_SETMASK, &oset, &set); /* reset the signal mask */ diff --git a/sbr/lock_file.c b/sbr/lock_file.c index 6653a28b..1e49a2a9 100644 --- a/sbr/lock_file.c +++ b/sbr/lock_file.c @@ -71,15 +71,30 @@ struct lock { struct lock *l_next; }; +enum locktype { FCNTL_LOCKING, FLOCK_LOCKING, LOCKF_LOCKING, DOT_LOCKING }; + +/* + * Flags to indicate whether we've initialized the lock types, and + * our saved lock types + */ +static int datalockinit = 0; +static int spoollockinit = 0; +enum locktype datalocking, spoollocking; + + /* top of list containing all open locks */ static struct lock *l_top = NULL; +static int lkopen(char *, int, mode_t, enum locktype, int); +static FILE *lkfopen(char *, const char *, mode_t, int); + static int lkopen_fcntl (char *, int, mode_t, int); #ifdef HAVE_LOCKF static int lkopen_lockf (char *, int, mode_t, int); #endif /* HAVE_LOCKF */ -#ifdef DOT_LOCKING +static enum locktype init_locktype(const char *); + static int lkopen_dot (char *, int, mode_t); static void lockname (char *, struct lockinfo *, int); static void timerON (char *, int); @@ -89,23 +104,62 @@ static void alrmser (int); #if !defined(HAVE_LIBLOCKFILE) static int lockit (struct lockinfo *); #endif -#endif /* - * Base routine to open and lock a file, - * and return a file descriptor. + * Base function: determine the data type used to lock files and + * call the underlying function. */ int -lkopen (char *file, int access, mode_t mode) +lkopendata(char *file, int access, mode_t mode, int exclusive) { -#ifdef KERNEL_LOCKING - return lkopen_kernel(file, access, mode); -#endif + if (! datalockinit) { + char *cp = context_find("datalocking"); + + if (cp) { + datalocking = init_locktype(cp); + } else { + /* We default to fcntl locking for data files */ + datalocking = FCNTL_LOCKING; + } -#ifdef DOT_LOCKING - return lkopen_dot(file, access, mode); -#endif + datalockinit = 1; + } + + return lkopen(file, access, mode, datalocking, exclusive); +} + +/* + * Internal routine to switch between different locking types. + */ + +static int +lkopen (char *file, int access, mode_t mode, enum locktype ltype, + int exclusive) +{ + switch (ltype) { + + case FCNTL_LOCKING: + return lkopen_fcntl(file, access, mode, exclusive); + + case DOT_LOCKING: + return lkopen_dot(file, access, mode, exclusive); + +#ifdef HAVE_FLOCK + case FLOCK_LOCKING: + return lkopen_flock(file, access, mode, exclusive); +#endif /* HAVE_FLOCK */ + +#ifdef HAVE_LOCKF + case LOCKF_LOCKING: + return lkopen_lockf(file, access, mode, exclusive); +#endif /* HAVE_FLOCK */ + + default: + adios(NULL, "Internal locking error: unsupported lock type used!"); + } + + return -1; } @@ -168,7 +222,7 @@ lkclose (int fd, char *file) */ FILE * -lkfopen (char *file, char *mode) +lkfopen (char *file, char *mode, int exclusive) { int fd, access; FILE *fp; @@ -190,7 +244,7 @@ lkfopen (char *file, char *mode) return NULL; } - if ((fd = lkopen (file, access, 0666)) == -1) + if ((fd = lkopen (file, access, 0666, exclusive)) == -1) return NULL; if ((fp = fdopen (fd, mode)) == NULL) { @@ -601,7 +655,7 @@ alrmser (int sig) * Return a locking algorithm based on the string name */ -enum locktype +static enum locktype init_locktype(const char *lockname) { if (mh_strcasecmp(lockname, "fcntl") == 0) { @@ -621,7 +675,7 @@ init_locktype(const char *lockname) } else if (mh_strcasecmp(lockname, "dot") == 0) { return DOT_LOCKING; } else { - adios(NULL, "Unknown lock type: \"%s\"", lockname) + adios(NULL, "Unknown lock type: \"%s\"", lockname); /* NOTREACHED */ return 0; } -- 2.48.1 From 2e08fdfc0ef872c968c2e42b7ee0ede42aee14aa Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Fri, 15 Mar 2013 15:57:56 -0400 Subject: [PATCH 11/16] Mostly complete now, but not yet functional. --- h/mts.h | 1 + h/prototypes.h | 22 +-- m4/locking.m4 | 2 +- sbr/context_read.c | 2 +- sbr/context_save.c | 2 +- sbr/lock_file.c | 441 ++++++++++++++++++++++++++------------------- sbr/mts.c | 3 + sbr/seq_read.c | 4 +- sbr/seq_save.c | 6 +- uip/annosbr.c | 4 +- uip/dropsbr.c | 5 +- uip/inc.c | 8 +- uip/mhcachesbr.c | 12 +- uip/msh.c | 4 +- uip/slocal.c | 4 +- 15 files changed, 301 insertions(+), 219 deletions(-) diff --git a/h/mts.h b/h/mts.h index 1b8f71b5..b425e242 100644 --- a/h/mts.h +++ b/h/mts.h @@ -16,6 +16,7 @@ extern char *mmdfldir; extern char *mmdflfil; extern char *uucpldir; extern char *uucplfil; +extern char *spoollocking; #define MAILDIR (mmdfldir && *mmdfldir ? mmdfldir : getenv ("HOME")) #define MAILFIL (mmdflfil && *mmdflfil ? mmdflfil : getusername ()) diff --git a/h/prototypes.h b/h/prototypes.h index 921021d7..14b12528 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -88,18 +88,18 @@ char *getfolder(int); * file, which will use the locking algorithm configured for the mail * spool. * - * All of these functions have a third argument, an integer which - * indicates the type of lock. A "0" means a shared (read) lock, and a - * "1" indicates an exclusive (write) lock. + * Files opened for reading are locked with a read lock (if possible by + * the underlying lock mechanism), files opened for writing are locked + * using an exclusive lock. */ -int lkclosedata(int, char*); -int lkclosespool(int, char*); -int lkfclosedata(FILE *, char *); -int lkfclosespool(FILE *, char *); -FILE *lkfopendata(char *, char *, int); -int lkopendata(char *, int, mode_t, int); -FILE *lkfopenspool(char *, char *, int); -int lkopenspool(char *, int, mode_t, int); +int lkclosedata(int, const char *); +int lkclosespool(int, const char *); +int lkfclosedata(FILE *, const char *); +int lkfclosespool(FILE *, const char *); +FILE *lkfopendata(const char *, const char *); +int lkopendata(const char *, int, mode_t); +FILE *lkfopenspool(const char *, const char *); +int lkopenspool(const char *, int, mode_t); int m_atoi (char *); char *m_backup (char *); int m_convert (struct msgs *, char *); diff --git a/m4/locking.m4 b/m4/locking.m4 index e013790a..b70f225f 100644 --- a/m4/locking.m4 +++ b/m4/locking.m4 @@ -33,7 +33,7 @@ AS_CASE([$with_locking], [AC_MSG_ERROR([--without-locking not supported])], [AC_MSG_ERROR([Unknown locking type $with_locking])]) -AC_DEFINE_UNQUOTED([DEFAULT_LOCKING], [$with_locking], +AC_DEFINE_UNQUOTED([DEFAULT_LOCKING], ["$with_locking"], [The default lock type for the mail spool file]) AC_MSG_RESULT([$with_locking]) diff --git a/sbr/context_read.c b/sbr/context_read.c index 2008c26b..bee5566b 100644 --- a/sbr/context_read.c +++ b/sbr/context_read.c @@ -136,7 +136,7 @@ context_read (void) ctxpath = getcpy (m_maildir (cp)); - if ((ib = lkfopendata (ctxpath, "r", 0))) { + if ((ib = lkfopendata (ctxpath, "r"))) { readconfig ((struct node **) 0, ib, cp, 1); lkfclosedata (ib, ctxpath); } diff --git a/sbr/context_save.c b/sbr/context_save.c index 86776c7d..5224af4c 100644 --- a/sbr/context_save.c +++ b/sbr/context_save.c @@ -43,7 +43,7 @@ context_save (void) sigaddset (&set, SIGTERM); sigprocmask (SIG_BLOCK, &set, &oset); - if (!(out = lkfopendata (ctxpath, "w", 1))) + if (!(out = lkfopendata (ctxpath, "w"))) adios (ctxpath, "unable to write"); for (np = m_defs; np; np = np->n_next) if (np->n_context) diff --git a/sbr/lock_file.c b/sbr/lock_file.c index 1e49a2a9..8506faf8 100644 --- a/sbr/lock_file.c +++ b/sbr/lock_file.c @@ -18,6 +18,7 @@ #include #include #include +#include #ifdef HAVE_SYS_TIME_H # include @@ -50,6 +51,11 @@ struct lockinfo { #endif }; +/* + * Number of tries to retry locking + */ +#define LOCK_RETRIES 5 + /* * Amount of time to wait before * updating ctime of lock file. @@ -79,24 +85,28 @@ enum locktype { FCNTL_LOCKING, FLOCK_LOCKING, LOCKF_LOCKING, DOT_LOCKING }; */ static int datalockinit = 0; static int spoollockinit = 0; -enum locktype datalocking, spoollocking; +static enum locktype datalocktype, spoollocktype; /* top of list containing all open locks */ static struct lock *l_top = NULL; -static int lkopen(char *, int, mode_t, enum locktype, int); -static FILE *lkfopen(char *, const char *, mode_t, int); +static int lkopen(const char *, int, mode_t, enum locktype); +static int str2accbits(const char *); -static int lkopen_fcntl (char *, int, mode_t, int); +static int lkopen_fcntl (const char *, int, mode_t); #ifdef HAVE_LOCKF -static int lkopen_lockf (char *, int, mode_t, int); +static int lkopen_lockf (const char *, int, mode_t); #endif /* HAVE_LOCKF */ +#ifdef HAVE_FLOCK +static int lkopen_flock (const char *, int, mode_t); +#endif /* HAVE_FLOCK */ static enum locktype init_locktype(const char *); -static int lkopen_dot (char *, int, mode_t); -static void lockname (char *, struct lockinfo *, int); +static int lkopen_dot (const char *, int, mode_t); +static void lkclose_dot (int, const char *); +static void lockname (const char *, struct lockinfo *, int); static void timerON (char *, int); static void timerOFF (int); static void alrmser (int); @@ -106,27 +116,175 @@ static int lockit (struct lockinfo *); #endif /* - * Base function: determine the data type used to lock files and + * Base functions: determine the data type used to lock files and * call the underlying function. */ int -lkopendata(char *file, int access, mode_t mode, int exclusive) +lkopendata(const char *file, int access, mode_t mode) { if (! datalockinit) { char *cp = context_find("datalocking"); if (cp) { - datalocking = init_locktype(cp); + datalocktype = init_locktype(cp); } else { /* We default to fcntl locking for data files */ - datalocking = FCNTL_LOCKING; + datalocktype = FCNTL_LOCKING; } datalockinit = 1; } - return lkopen(file, access, mode, datalocking, exclusive); + return lkopen(file, access, mode, datalocktype); +} + + +/* + * Locking using the spool locking algorithm + */ + +int lkopenspool(const char *file, int access, mode_t mode) +{ + if (! spoollockinit) { + spoollocktype = init_locktype(spoollocking); + + spoollockinit = 1; + } + + return lkopen(file, access, mode, spoollocktype); +} + + +/* + * Versions of lkopen that return a FILE * + */ + +FILE * +lkfopendata(const char *file, const char *mode) +{ + FILE *fp; + int oflags = str2accbits(mode); + int fd; + + if (oflags == -1) { + errno = EINVAL; + return NULL; + } + + if ((fd = lkopendata(file, oflags, 0666)) == -1) + return NULL; + + if ((fp = fdopen (fd, mode)) == NULL) { + close (fd); + return NULL; + } + + return fp; +} + +FILE * +lkfopenspool(const char *file, const char *mode) +{ + FILE *fp; + int oflags = str2accbits(mode); + int fd; + + if (oflags == -1) { + errno = EINVAL; + return NULL; + } + + if ((fd = lkopenspool(file, oflags, 0666)) == -1) + return NULL; + + if ((fp = fdopen (fd, mode)) == NULL) { + close (fd); + return NULL; + } + + return fp; +} + + +/* + * Corresponding close functions. + * + * A note here: All of the kernel locking functions terminate the lock + * when the descriptor is closed, so why write the code to explicitly + * unlock the file? We only need to do this in the dot-locking case. + */ + +int +lkclosedata(int fd, const char *name) +{ + int rc = close(fd); + + if (datalocktype == DOT_LOCKING) + lkclose_dot(fd, name); + + return rc; +} + +int +lkfclosedata(FILE *f, const char *name) +{ + int fd = fileno(f); + int rc = fclose(f); + + if (datalocktype == DOT_LOCKING) + lkclose_dot(fd, name); + + return rc; +} + +int +lkclosespool(int fd, const char *name) +{ + int rc = close(fd); + + if (spoollocktype == DOT_LOCKING) + lkclose_dot(fd, name); + + return rc; +} + +int +lkfclosespool(FILE *f, const char *name) +{ + int fd = fileno(f); + int rc = fclose(f); + + if (spoollocktype == DOT_LOCKING) + lkclose_dot(fd, name); + + return rc; +} + + +/* + * Convert fopen() mode argument to open() bits + */ + +static int +str2accbits(const char *mode) +{ + if (strcmp (mode, "r") == 0) + return O_RDONLY; + else if (strcmp (mode, "r+") == 0) + return O_RDWR; + else if (strcmp (mode, "w") == 0) + return O_WRONLY | O_CREAT | O_TRUNC; + else if (strcmp (mode, "w+") == 0) + return O_RDWR | O_CREAT | O_TRUNC; + else if (strcmp (mode, "a") == 0) + return O_WRONLY | O_CREAT | O_APPEND; + else if (strcmp (mode, "a+") == 0) + return O_RDWR | O_CREAT | O_APPEND; + else { + errno = EINVAL; + return -1; + } } /* @@ -134,25 +292,24 @@ lkopendata(char *file, int access, mode_t mode, int exclusive) */ static int -lkopen (char *file, int access, mode_t mode, enum locktype ltype, - int exclusive) +lkopen (const char *file, int access, mode_t mode, enum locktype ltype) { switch (ltype) { case FCNTL_LOCKING: - return lkopen_fcntl(file, access, mode, exclusive); + return lkopen_fcntl(file, access, mode); case DOT_LOCKING: - return lkopen_dot(file, access, mode, exclusive); + return lkopen_dot(file, access, mode); #ifdef HAVE_FLOCK case FLOCK_LOCKING: - return lkopen_flock(file, access, mode, exclusive); + return lkopen_flock(file, access, mode); #endif /* HAVE_FLOCK */ #ifdef HAVE_LOCKF case LOCKF_LOCKING: - return lkopen_lockf(file, access, mode, exclusive); + return lkopen_lockf(file, access, mode); #endif /* HAVE_FLOCK */ default: @@ -164,43 +321,14 @@ lkopen (char *file, int access, mode_t mode, enum locktype ltype, /* - * Base routine to close and unlock a file, - * given a file descriptor. + * Routine to clean up the dot locking file */ -int -lkclose (int fd, char *file) +static void +lkclose_dot (int fd, const char *file) { -#ifdef FCNTL_LOCKING - struct flock buf; -#endif - -#ifdef DOT_LOCKING struct lockinfo lkinfo; -#endif - - if (fd == -1) - return 0; - -#ifdef FCNTL_LOCKING - buf.l_type = F_UNLCK; - buf.l_whence = SEEK_SET; - buf.l_start = 0; - buf.l_len = 0; - fcntl(fd, F_SETLK, &buf); -#endif -#ifdef FLOCK_LOCKING - flock (fd, LOCK_UN); -#endif - -#ifdef LOCKF_LOCKING - /* make sure we unlock the whole thing */ - lseek (fd, (off_t) 0, SEEK_SET); - lockf (fd, F_ULOCK, 0L); -#endif - -#ifdef DOT_LOCKING lockname (file, &lkinfo, 0); /* get name of lock file */ #if !defined(HAVE_LIBLOCKFILE) unlink (lkinfo.curlock); /* remove lock file */ @@ -208,189 +336,138 @@ lkclose (int fd, char *file) lockfile_remove(lkinfo.curlock); #endif /* HAVE_LIBLOCKFILE */ timerOFF (fd); /* turn off lock timer */ -#else /* DOT_LOCKING */ - NMH_UNUSED (file); -#endif /* DOT_LOCKING */ - - return (close (fd)); } /* - * Base routine to open and lock a file, - * and return a FILE pointer + * Open and lock a file, using fcntl locking */ -FILE * -lkfopen (char *file, char *mode, int exclusive) +static int +lkopen_fcntl(const char *file, int access, mode_t mode) { - int fd, access; - FILE *fp; + int fd, i, saved_errno; + struct flock flk; - if (strcmp (mode, "r") == 0) - access = O_RDONLY; - else if (strcmp (mode, "r+") == 0) - access = O_RDWR; - else if (strcmp (mode, "w") == 0) - access = O_WRONLY | O_CREAT | O_TRUNC; - else if (strcmp (mode, "w+") == 0) - access = O_RDWR | O_CREAT | O_TRUNC; - else if (strcmp (mode, "a") == 0) - access = O_WRONLY | O_CREAT | O_APPEND; - else if (strcmp (mode, "a+") == 0) - access = O_RDWR | O_CREAT | O_APPEND; - else { - errno = EINVAL; - return NULL; - } + /* + * The assumption here is that if you open the file for writing, you + * need an exclusive lock. + */ - if ((fd = lkopen (file, access, 0666, exclusive)) == -1) - return NULL; + for (i = 0; i < LOCK_RETRIES; i++) { + if ((fd = open(file, access, mode)) == -1) + return -1; - if ((fp = fdopen (fd, mode)) == NULL) { - close (fd); - return NULL; + flk.l_start = 0; + flk.l_len = 0; + flk.l_type = (access & O_ACCMODE) == O_RDONLY ? F_RDLCK : F_WRLCK; + flk.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLK, &flk) != -1) + return fd; + + saved_errno = errno; + close(fd); + sleep(1); } - return fp; + errno = saved_errno; + return -1; } /* - * Base routine to close and unlock a file, - * given a FILE pointer + * Open and lock a file, using flock locking */ -int -lkfclose (FILE *fp, char *file) +static int +lkopen_flock(const char *file, int access, mode_t mode) { -#ifdef FCNTL_LOCKING - struct flock buf; -#endif - -#ifdef DOT_LOCKING - struct lockinfo lkinfo; -#endif + int fd, i, saved_errno, locktype; - if (fp == NULL) - return 0; + /* + * The assumption here is that if you open the file for writing, you + * need an exclusive lock. + */ -#ifdef FCNTL_LOCKING - buf.l_type = F_UNLCK; - buf.l_whence = SEEK_SET; - buf.l_start = 0; - buf.l_len = 0; - fcntl(fileno(fp), F_SETLK, &buf); -#endif + locktype = (((access & O_ACCMODE) == O_RDONLY) ? LOCK_SH : LOCK_EX) | + LOCK_NB; -#ifdef FLOCK_LOCKING - flock (fileno(fp), LOCK_UN); -#endif + for (i = 0; i < LOCK_RETRIES; i++) { + if ((fd = open(file, access, mode)) == -1) + return -1; -#ifdef LOCKF_LOCKING - /* make sure we unlock the whole thing */ - fseek (fp, 0L, SEEK_SET); - lockf (fileno(fp), F_ULOCK, 0L); -#endif + if (flock(fd, locktype) != -1) + return fd; -#ifdef DOT_LOCKING - lockname (file, &lkinfo, 0); /* get name of lock file */ -#if !defined(HAVE_LIBLOCKFILE) - unlink (lkinfo.curlock); /* remove lock file */ -#else - lockfile_remove(lkinfo.curlock); -#endif /* HAVE_LIBLOCKFILE */ - timerOFF (fileno(fp)); /* turn off lock timer */ -#else /* DOT_LOCKING */ - NMH_UNUSED (file); -#endif /* DOT_LOCKING */ + saved_errno = errno; + close(fd); + sleep(1); + } - return (fclose (fp)); + errno = saved_errno; + return -1; } -#ifdef KERNEL_LOCKING - /* - * open and lock a file, using kernel locking + * Open and lock a file, using lockf locking */ static int -lkopen_kernel (char *file, int access, mode_t mode) +lkopen_lockf(const char *file, int access, mode_t mode) { - int fd, i, j; - -# ifdef FCNTL_LOCKING - struct flock buf; -# endif /* FCNTL_LOCKING */ + int fd, i, saved_errno, saved_access; - for (i = 0; i < 5; i++) { + /* + * Two notes: + * + * Because lockf locks start from the current offset, mask off O_APPEND + * and seek to the end of the file later if it was requested. + * + * lockf locks require write access to the file, so always add it + * even if it wasn't requested. + */ -# if defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING) - /* remember the original mode */ - j = access; + saved_access = access; - /* make sure we open at the beginning */ - access &= ~O_APPEND; + access &= ~O_APPEND; - /* - * We MUST have write permission or - * lockf/fcntl() won't work - */ - if ((access & 03) == O_RDONLY) { - access &= ~O_RDONLY; - access |= O_RDWR; - } -# endif /* LOCKF_LOCKING || FCNTL_LOCKING */ + if ((access & O_ACCMODE) == O_RDONLY) { + access &= ~O_RDONLY; + access |= O_RDWR; + } - if ((fd = open (file, access | O_NDELAY, mode)) == -1) + for (i = 0; i < LOCK_RETRIES; i++) { + if ((fd = open(file, access, mode)) == -1) return -1; -# ifdef FCNTL_LOCKING - buf.l_type = F_WRLCK; - buf.l_whence = SEEK_SET; - buf.l_start = 0; - buf.l_len = 0; - if (fcntl (fd, F_SETLK, &buf) != -1) - return fd; -# endif - -# ifdef FLOCK_LOCKING - if (flock (fd, (((access & 03) == O_RDONLY) ? LOCK_SH : LOCK_EX) - | LOCK_NB) != -1) - return fd; -# endif - -# ifdef LOCKF_LOCKING - if (lockf (fd, F_TLOCK, 0L) != -1) { - /* see if we should be at the end */ - if (j & O_APPEND) - lseek (fd, (off_t) 0, SEEK_END); + if (lockf(fd, F_TLOCK, 0) != -1) { + /* + * Seek to end if requested + */ + if (saved_access & O_APPEND) { + lseek(fd, 0, SEEK_END); + } return fd; } -# endif - j = errno; - close (fd); - sleep (5); + saved_errno = errno; + close(fd); + sleep(1); } - close (fd); - errno = j; + errno = saved_errno; return -1; } -#endif /* KERNEL_LOCKING */ - - -#ifdef DOT_LOCKING /* * open and lock a file, using dot locking */ static int -lkopen_dot (char *file, int access, mode_t mode) +lkopen_dot (const char *file, int access, mode_t mode) { int fd; struct lockinfo lkinfo; @@ -497,10 +574,11 @@ lockit (struct lockinfo *li) */ static void -lockname (char *file, struct lockinfo *li, int isnewlock) +lockname (const char *file, struct lockinfo *li, int isnewlock) { int bplen, tmplen; - char *bp, *cp; + char *bp; + const char *cp; #if 0 struct stat st; @@ -649,7 +727,6 @@ alrmser (int sig) alarm (NSECS); } -#endif /* DOT_LOCKING */ /* * Return a locking algorithm based on the string name diff --git a/sbr/mts.c b/sbr/mts.c index 03578288..70dae024 100644 --- a/sbr/mts.c +++ b/sbr/mts.c @@ -54,6 +54,8 @@ char *uucplfil = ""; char *mmdlm1 = "\001\001\001\001\n"; char *mmdlm2 = "\001\001\001\001\n"; +char *spoollocking = DEFAULT_LOCKING; + /* Cache the username, fullname, and mailbox of the user */ static char username[BUFSIZ]; static char fullname[BUFSIZ]; @@ -102,6 +104,7 @@ static struct bind binds[] = { { "systemname", &systemname }, { "mmdfldir", &mmdfldir }, { "mmdflfil", &mmdflfil }, + { "spoollocking", &spoollocking }, { "uucpldir", &uucpldir }, { "uucplfil", &uucplfil }, { "mmdelim1", &mmdlm1 }, diff --git a/sbr/seq_read.c b/sbr/seq_read.c index a807ef7d..2bea5367 100644 --- a/sbr/seq_read.c +++ b/sbr/seq_read.c @@ -73,7 +73,7 @@ seq_public (struct msgs *mp) /* get filename of sequence file */ snprintf (seqfile, sizeof(seqfile), "%s/%s", mp->foldpath, mh_seq); - if ((fp = lkfopen (seqfile, "r")) == NULL) + if ((fp = lkfopendata (seqfile, "r")) == NULL) return; /* Use m_getfld to scan sequence file */ @@ -110,7 +110,7 @@ seq_public (struct msgs *mp) } m_getfld_state_destroy (&gstate); - lkfclose (fp, seqfile); + lkfclosedata (fp, seqfile); } diff --git a/sbr/seq_save.c b/sbr/seq_save.c index 4055f9d5..837b4bab 100644 --- a/sbr/seq_save.c +++ b/sbr/seq_save.c @@ -74,9 +74,9 @@ priv: * If that fails (probably because folder is * readonly), then make sequence private. */ - if ((fp = lkfopen (seqfile, "w")) == NULL + if ((fp = lkfopendata (seqfile, "w")) == NULL && (unlink (seqfile) == -1 || - (fp = lkfopen (seqfile, "w")) == NULL)) { + (fp = lkfopendata (seqfile, "w")) == NULL)) { admonish (attr, "unable to write"); goto priv; } @@ -94,7 +94,7 @@ priv: } if (fp) { - lkfclose (fp, seqfile); + lkfclosedata (fp, seqfile); sigprocmask (SIG_SETMASK, &oset, &set); /* reset signal mask */ } else { /* diff --git a/uip/annosbr.c b/uip/annosbr.c index 5b9fff42..27533f77 100644 --- a/uip/annosbr.c +++ b/uip/annosbr.c @@ -35,7 +35,7 @@ annotate (char *file, char *comp, char *text, int inplace, int datesw, int delet struct stat s; /* open and lock the file to be annotated */ - if ((fd = lkopen (file, O_RDWR, 0)) == NOTOK) { + if ((fd = lkopendata (file, O_RDWR, 0)) == NOTOK) { switch (errno) { case ENOENT: break; @@ -60,7 +60,7 @@ annotate (char *file, char *comp, char *text, int inplace, int datesw, int delet if (preserve_actime_and_modtime && utime(file, &b) == -1) advise("can't set access and modification times for %s", file); - lkclose (fd, file); + lkclosedata (fd, file); return i; } diff --git a/uip/dropsbr.c b/uip/dropsbr.c index 7771b6b8..09ee2c1f 100644 --- a/uip/dropsbr.c +++ b/uip/dropsbr.c @@ -48,7 +48,8 @@ mbx_open (char *file, int mbx_style, uid_t uid, gid_t gid, mode_t mode) /* attempt to open and lock file */ for (count = 4; count > 0; count--) { - if ((fd = lkopen (file, O_RDWR | O_CREAT | O_NONBLOCK, mode)) == NOTOK) { + if ((fd = lkopenspool (file, O_RDWR | O_CREAT | + O_NONBLOCK, mode)) == NOTOK) { switch (errno) { #if defined(FCNTL_LOCKING) || defined(LOCKF_LOCKING) case EACCES: @@ -463,7 +464,7 @@ mbx_size (int md, off_t start, off_t stop) int mbx_close (char *mailbox, int md) { - if (lkclose (md, mailbox) == 0) + if (lkclosespool (md, mailbox) == 0) return OK; return NOTOK; } diff --git a/uip/inc.c b/uip/inc.c index 2d791f68..6f7f5946 100644 --- a/uip/inc.c +++ b/uip/inc.c @@ -531,7 +531,7 @@ go_to_it: } GETGROUPPRIVS(); /* Reset gid to lock mail file */ - in = lkfopen (newmail, "r"); + in = lkfopenspool (newmail, "r"); DROPGROUPPRIVS(); if (in == NULL) adios (NULL, "unable to lock and fopen %s", newmail); @@ -875,7 +875,7 @@ go_to_it: if (incerr < 0) { /* error */ if (locked) { GETGROUPPRIVS(); /* Be sure we can unlock mail file */ - (void) lkfclose (in, newmail); in = NULL; + (void) lkfclosespool (in, newmail); in = NULL; DROPGROUPPRIVS(); /* And then return us to normal privileges */ } else { fclose (in); in = NULL; @@ -932,7 +932,7 @@ go_to_it: if (inc_type == INC_FILE && Maildir == NULL) { if (locked) { GETGROUPPRIVS(); /* Be sure we can unlock mail file */ - (void) lkfclose (in, newmail); in = NULL; + (void) lkfclosespool (in, newmail); in = NULL; DROPGROUPPRIVS(); /* And then return us to normal privileges */ } else { fclose (in); in = NULL; @@ -955,7 +955,7 @@ inc_done (int status) if (locked) { GETGROUPPRIVS(); - lkfclose(in, newmail); + lkfclosespool(in, newmail); DROPGROUPPRIVS(); } exit (status); diff --git a/uip/mhcachesbr.c b/uip/mhcachesbr.c index 25ac9ab0..5f7e1479 100644 --- a/uip/mhcachesbr.c +++ b/uip/mhcachesbr.c @@ -344,19 +344,19 @@ use_raw: make_intermediates (mapfile); mask = umask (writing == 2 ? 0077 : 0); - if (!(fp = lkfopen (mapfile, "a")) && errno == ENOENT) { + if (!(fp = lkfopendata (mapfile, "a")) && errno == ENOENT) { int fd; if ((fd = creat (mapfile, 0666)) != NOTOK) { close (fd); - fp = lkfopen (mapfile, "a"); + fp = lkfopendata (mapfile, "a"); } } umask (mask); if (!fp) return NOTOK; fprintf (fp, "%s: %s\n", mapname, id); - lkfclose (fp, mapfile); + lkfclosedata (fp, mapfile); done_map: if (*mapname == '/') @@ -378,7 +378,7 @@ find_cache_aux2 (char *mapfile, char *id, char *mapname, int namelen) FILE *fp; m_getfld_state_t gstate = 0; - if (!(fp = lkfopen (mapfile, "r"))) + if (!(fp = lkfopendata (mapfile, "r"))) return NOTOK; for (;;) { @@ -409,7 +409,7 @@ find_cache_aux2 (char *mapfile, char *id, char *mapname, int namelen) result = strcmp (id, dp); free (dp); if (result == 0) { - lkfclose (fp, mapfile); + lkfclosedata (fp, mapfile); return OK; } continue; @@ -423,6 +423,6 @@ find_cache_aux2 (char *mapfile, char *id, char *mapname, int namelen) } m_getfld_state_destroy (&gstate); - lkfclose (fp, mapfile); + lkfclosedata (fp, mapfile); return NOTOK; } diff --git a/uip/msh.c b/uip/msh.c index 4ab73286..2a562f00 100644 --- a/uip/msh.c +++ b/uip/msh.c @@ -1097,7 +1097,7 @@ quit (void) if (vmh) ttyNaux (NULLCMD, "FAST"); cp = NULL; - if ((dp = lkfopen (mp->foldpath, "r")) == NULL) { + if ((dp = lkfopendata (mp->foldpath, "r")) == NULL) { advise (mp->foldpath, "unable to lock"); if (vmh) { ttyR (NULLCMD); @@ -1161,7 +1161,7 @@ quit (void) release: ; if (cp) free (cp); - lkfclose (dp, mp->foldpath); + lkfclosedata (dp, mp->foldpath); if (vmh) { ttyR (NULLCMD); pFIN (); diff --git a/uip/slocal.c b/uip/slocal.c index f570aaaf..ebc2c26b 100644 --- a/uip/slocal.c +++ b/uip/slocal.c @@ -1453,7 +1453,7 @@ suppress_duplicates (int fd, char *file) * This will fail if your Maildelivery file doesn't * exist. */ - if ((lockfd = lkopen(file, O_RDWR, 0)) == -1) { + if ((lockfd = lkopendata(file, O_RDWR, 0)) == -1) { advise (file, "unable to perform file locking on"); free (cp); fclose (in); @@ -1474,7 +1474,7 @@ suppress_duplicates (int fd, char *file) } dbm_close (db); - lkclose(lockfd, file); + lkclosedata(lockfd, file); free (cp); fclose (in); return result; -- 2.48.1 From f5d307951949b2787173fd2dc0aa413b59a9486e Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Fri, 15 Mar 2013 19:42:43 -0400 Subject: [PATCH 12/16] Turns out the close function can be called with FILE * == NULL, so handle that case. --- sbr/lock_file.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sbr/lock_file.c b/sbr/lock_file.c index 8506faf8..987b922c 100644 --- a/sbr/lock_file.c +++ b/sbr/lock_file.c @@ -229,8 +229,13 @@ lkclosedata(int fd, const char *name) int lkfclosedata(FILE *f, const char *name) { - int fd = fileno(f); - int rc = fclose(f); + int fd, rc; + + if (f == NULL) + return 0; + + fd = fileno(f); + rc = fclose(f); if (datalocktype == DOT_LOCKING) lkclose_dot(fd, name); @@ -252,8 +257,13 @@ lkclosespool(int fd, const char *name) int lkfclosespool(FILE *f, const char *name) { - int fd = fileno(f); - int rc = fclose(f); + int fd, rc; + + if (f == NULL) + return 0; + + fd = fileno(f); + rc = fclose(f); if (spoollocktype == DOT_LOCKING) lkclose_dot(fd, name); -- 2.48.1 From 1c8cf81caa1f8d56f8812b73d37cfc62a9815877 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Fri, 15 Mar 2013 23:45:54 -0400 Subject: [PATCH 13/16] Support for locking tests in the test suite. --- Makefile.am | 7 ++++- etc/mts.conf.in | 16 ++++++++++ m4/locking.m4 | 8 +++++ test/common.sh.in | 1 + test/locking/test-datalocking | 47 +++++++++++++++++++++++++++++ test/locking/test-spoollocking | 55 ++++++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100755 test/locking/test-datalocking create mode 100755 test/locking/test-spoollocking diff --git a/Makefile.am b/Makefile.am index 0e64a27b..e8f64505 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,6 +37,7 @@ TESTS_ENVIRONMENT = MH_OBJ_DIR="@abs_builddir@" \ MH_TEST_DIR="@abs_builddir@/test/testdir" \ auxexecdir="$(auxexecdir)" bindir="$(bindir)" \ mandir="$(mandir)" sysconfdir="$(sysconfdir)" \ + supported_locks="$(supported_locks)" \ MULTIBYTE_ENABLED=$(MULTIBYTE_ENABLED) \ ICONV_ENABLED=$(ICONV_ENABLED) \ $(TESTS_SHELL) ## Keep at end of TESTS_ENVIRONMENT. @@ -58,7 +59,9 @@ TESTS = test/ali/test-ali test/anno/test-anno \ test/inc/test-deb359167 test/inc/test-eom-align \ test/inc/test-inc-scanout test/inc/test-msgchk \ test/inc/test-pop \ - test/install-mh/test-install-mh test/manpages/test-manpages \ + test/install-mh/test-install-mh \ + test/locking/test-datalocking test/locking/test-spoollocking \ + test/manpages/test-manpages \ test/mhbuild/test-forw test/mhbuild/test-utf8-body \ test/mhlist/test-mhlist test/mhmail/test-mhmail \ test/mhparam/test-mhparam test/mhpath/test-mhpath \ @@ -448,6 +451,8 @@ etc/mts.conf: $(srcdir)/etc/mts.conf.in Makefile $(SED) -e 's,%mts%,$(MTS),' \ -e 's,%mailspool%,$(mailspool),' \ -e 's,%smtpservers%,$(smtpservers),' \ + -e 's,%default_locking%,$(default_locking),' \ + -e 's,%supported_locks%,$(supported_locks),' \ < $(srcdir)/etc/mts.conf.in > $@ etc/mhn.defaults: $(srcdir)/etc/mhn.defaults.sh $(MHNSEARCHPROG) diff --git a/etc/mts.conf.in b/etc/mts.conf.in index 4e3deda6..bcf7b2f9 100644 --- a/etc/mts.conf.in +++ b/etc/mts.conf.in @@ -27,6 +27,22 @@ mmdfldir: %mailspool% # are kept. If this is empty, the user's login name is used. mmdflfil: +# +# The locking algorithm to use on the spool file. Valid settings are: +# +# fcntl Locking using the fcntl() function +# dot "Dot" locking using an external lock file +# flock Locking using the flock() function (if supported by OS) +# lockf Locking using the lockf() function (if supported by OS) +# +# Locking algorithms supported on this installation are: +# +# %supported_locks% +# +# The default spool locking configured on this system is %default_locking%; +# change the line below to get a different value +#spoollocking: %default_locking% + # Hardcoded POP server name (prevents inc'ing from local mail spool). #pophost: localhost diff --git a/m4/locking.m4 b/m4/locking.m4 index b70f225f..2895d882 100644 --- a/m4/locking.m4 +++ b/m4/locking.m4 @@ -35,9 +35,17 @@ AS_CASE([$with_locking], AC_DEFINE_UNQUOTED([DEFAULT_LOCKING], ["$with_locking"], [The default lock type for the mail spool file]) +AC_SUBST([default_locking], [$with_locking]) AC_MSG_RESULT([$with_locking]) +supported_locks="fcntl dot" +AS_IF([test x"$ac_cv_func_flock" = x"yes"], + [supported_locks="$supported_locks flock"]) +AS_IF([test x"$ac_cv_func_lockf" = x"yes"], + [supported_locks="$supported_locks lockf"]) +AC_SUBST([supported_locks]) + dnl Should we use a locking directory? AC_ARG_ENABLE([lockdir], [AS_HELP_STRING([--enable-lockdir=dir], [Store dot-lock files in "dir"])], [ diff --git a/test/common.sh.in b/test/common.sh.in index 2264d469..1f52004a 100644 --- a/test/common.sh.in +++ b/test/common.sh.in @@ -13,6 +13,7 @@ test -z "$auxexecdir" && auxexecdir="@libdir@" test -z "$bindir" && bindir="@bindir@" test -z "$mandir" && mandir="@mandir@" test -z "$sysconfdir" && sysconfdir="@sysconfdir@" +test -z "$supported_locks" && supported_locks="@supported_locks@" test -z "$MULTIBYTE_ENABLED" && MULTIBYTE_ENABLED="@MULTIBYTE_ENABLED@" test -z "$ICONV_ENABLED" && ICONV_ENABLED="@ICONV_ENABLED@" export MH_TEST_DIR auxexecdir bindir mandir sysconfdir diff --git a/test/locking/test-datalocking b/test/locking/test-datalocking new file mode 100755 index 00000000..4c2d1935 --- /dev/null +++ b/test/locking/test-datalocking @@ -0,0 +1,47 @@ +#!/bin/sh +###################################################### +# +# Test the locking of nmh metadata +# +###################################################### + +set -e + +if test -z "${MH_OBJ_DIR}"; then + srcdir=`dirname $0`/../.. + MH_OBJ_DIR=`cd $srcdir && pwd`; export MH_OBJ_DIR +fi + +. "$MH_OBJ_DIR/test/common.sh" + +setup_test + +# +# Set things up so we have a "cur" sequence +# + +show +inbox 1 -nocheckmime -showproc cat > /dev/null + +# +# mark read & writes sequences files, so use it to exercise the locking code +# for each locking algorithm +# + +for locktype in $supported_locks +do + mv -f ${MH} ${MH}.old + sed -e '/^datalocking:/d' < ${MH}.old > ${MH} + rm -f ${MH}.old + + echo "datalocking: $locktype" >> ${MH} + + mark 2 4 6 -sequence test -add + + run_test 'mark -list' 'cur: 1 +test: 2 4 6' + + mark all -sequence test -delete + +done + +exit ${failed:-0} diff --git a/test/locking/test-spoollocking b/test/locking/test-spoollocking new file mode 100755 index 00000000..c795ce3d --- /dev/null +++ b/test/locking/test-spoollocking @@ -0,0 +1,55 @@ +#!/bin/sh +###################################################### +# +# Test the locking of nmh spool file +# +###################################################### + +set -e + +if test -z "${MH_OBJ_DIR}"; then + srcdir=`dirname $0`/../.. + MH_OBJ_DIR=`cd $srcdir && pwd`; export MH_OBJ_DIR +fi + +. "$MH_OBJ_DIR/test/common.sh" + +setup_test + +testmessage="$MH_TEST_DIR/testmessage" + +cat > "$testmessage" < +To: Some Other User +Subject: Hello, how are you? +Date: Sun, 17 Dec 2006 12:13:14 -0500 + +This is a test; will it work? +EOM + +# +# invoke "inc" for each locking algorithm +# + +for locktype in $supported_locks +do + mv -f ${MHMTSCONF} ${MHMTSCONF}.backup + sed -e '/^datalocking:/d' < ${MHMTSCONF}.backup > ${MHMTSCONF} + rm -f ${MHMTSCONF}.backup + + echo "spoollocking: $locktype" >> ${MHMTSCONF} + + run_test "inc -notruncate -width 80 -file $testmessage" \ +"Incorporating new mail into inbox... + + 11+ 12/17 No Such User Hello, how are you?< Date: Sat, 16 Mar 2013 00:04:38 -0400 Subject: [PATCH 14/16] Add information about locking support entries to the man pages. --- Makefile.am | 2 ++ man/mh-profile.man | 19 +++++++++++++++++++ man/mh-tailor.man | 13 +++++++++++++ 3 files changed, 34 insertions(+) diff --git a/Makefile.am b/Makefile.am index e8f64505..074adb26 100644 --- a/Makefile.am +++ b/Makefile.am @@ -570,6 +570,8 @@ man/man.sed: Makefile @echo 's,%mandir%,$(mandir),g' >> $@ @echo 's,%mailspool%,$(mailspool),g' >> $@ @echo 's,%sendmailpath%,$(sendmailpath),g' >> $@ + @echo 's,%default_locking%,$(default_locking),g' >> $@ + @echo 's,%supported_locks%,$(supported_locks),g' >> $@ @echo 's,%manext1%,$(manext1),g' >> $@ @echo 's,%manext5%,$(manext5),g' >> $@ @echo 's,%manext7%,$(manext7),g' >> $@ diff --git a/man/mh-profile.man b/man/mh-profile.man index d7afe5be..38dafb80 100644 --- a/man/mh-profile.man +++ b/man/mh-profile.man @@ -189,6 +189,25 @@ for an explanation of the octal number. (profile, default: 700) .RE .PP +.BR datalocking : +fcntl +.RS 5 +The locking algorithm used to lock changes to any +.B nmh +data files, such as sequences or the context. The locking algorithm is +any one of the following entries: +.PP +.RS 5 +.nf +%supported_locks% +.fi +.RE +.PP +Available locking algorithms can vary depending on what is supported by +the operating system. +(profile, default: fcntl) +.RE +.PP .IR program : .I default switches .RS 5 diff --git a/man/mh-tailor.man b/man/mh-tailor.man index 8c76e4b5..3ac3d21a 100644 --- a/man/mh-tailor.man +++ b/man/mh-tailor.man @@ -184,6 +184,19 @@ The beginning-of-message delimiter for maildrops. The end-of-message delimiter for maildrops. .RE .PP +.BR spoollocking : +%default_locking% +.RS 5 +The locking algorithm to use when opening the maildrop. Can be any one of +the following: +.PP +.RS 5 +.nf +%supported_locks% +.fi +.RE +.RE +.PP .BR maildelivery : %libdir%/maildelivery .RS 5 -- 2.48.1 From 6b4bc4a25eaac2eddd1bef51e3f00c21f98ed32d Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Mon, 18 Mar 2013 15:29:35 -0400 Subject: [PATCH 15/16] Add code for (and convert world to) the new world lock order. Still need to add some code to make inc and pick behave better (not hold locks for as long). --- h/mh.h | 12 ++++++++++++ h/prototypes.h | 30 ++++++++++++++++++++++++++++-- sbr/folder_free.c | 8 ++++++++ sbr/folder_read.c | 6 ++++-- sbr/m_draft.c | 2 +- sbr/seq_read.c | 21 ++++++++++++++------- sbr/seq_save.c | 15 +++++++++++++-- uip/anno.c | 2 +- uip/burst.c | 2 +- uip/comp.c | 3 ++- uip/dist.c | 2 +- uip/flist.c | 2 +- uip/fmttest.c | 2 +- uip/folder.c | 2 +- uip/forw.c | 2 +- uip/inc.c | 2 +- uip/mark.c | 2 +- uip/mhbuildsbr.c | 2 +- uip/mhlist.c | 2 +- uip/mhn.c | 2 +- uip/mhpath.c | 2 +- uip/mhshow.c | 2 +- uip/mhstore.c | 2 +- uip/mhstoresbr.c | 2 +- uip/mhtest.c | 2 +- uip/msh.c | 4 ++-- uip/packf.c | 2 +- uip/pick.c | 2 +- uip/rcvstore.c | 2 +- uip/refile.c | 4 ++-- uip/repl.c | 2 +- uip/rmm.c | 2 +- uip/scan.c | 2 +- uip/send.c | 2 +- uip/sendsbr.c | 2 +- uip/show.c | 2 +- uip/sortm.c | 2 +- 37 files changed, 113 insertions(+), 46 deletions(-) diff --git a/h/mh.h b/h/mh.h index daf47c02..c5246f29 100644 --- a/h/mh.h +++ b/h/mh.h @@ -205,6 +205,18 @@ struct msgs { * in a particular sequence. */ seqset_t *msgstats; /* msg status */ + + /* + * A FILE handle containing an open filehandle for the sequence file + * for this folder. If non-NULL, use it when the sequence file is + * written. + */ + FILE *seqhandle; + + /* + * The name of the public sequence file; required by lkfclose() + */ + char *seqname; }; /* diff --git a/h/prototypes.h b/h/prototypes.h index 14b12528..484bef28 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -66,7 +66,19 @@ int folder_addmsg (struct msgs **, char *, int, int, int, int, char *); int folder_delmsgs (struct msgs *, int, int); void folder_free (struct msgs *); int folder_pack (struct msgs **, int); -struct msgs *folder_read (char *); + +/* + * Read a MH folder structure and return an allocated "struct msgs" + * corresponding to the contents of the folder. + * + * Arguments include: + * + * name - Name of folder + * lockflag - If true, write-lock (and keep open) metadata files. + * See comments for seq_read() for more information. + */ +struct msgs *folder_read (char *name, int lockflag); + struct msgs *folder_realloc (struct msgs *, int, int); int gans (char *, struct swit *); char **getans (char *, struct swit *); @@ -152,7 +164,21 @@ char *seq_list (struct msgs *, char *); int seq_nameok (char *); void seq_print (struct msgs *, char *); void seq_printall (struct msgs *); -void seq_read (struct msgs *); + +/* + * Read the sequence files for the folder referenced in the given + * struct msgs and populate the sequence entries in the struct msgs. + * + * Arguments: + * + * mp - Folder structure to add sequence entries to + * lockflag - If true, obtain a write lock on the sequence file. + * Additionally, the sequence file will remain open + * and a pointer to the filehandle will be stored in + * folder structure, where it will later be used by + * seq_save(). + */ +void seq_read (struct msgs * mp, int lockflag); void seq_save (struct msgs *); void seq_setcur (struct msgs *, int); void seq_setprev (struct msgs *); diff --git a/sbr/folder_free.c b/sbr/folder_free.c index ebb58ab4..7f87ccd4 100644 --- a/sbr/folder_free.c +++ b/sbr/folder_free.c @@ -25,6 +25,14 @@ folder_free (struct msgs *mp) for (i = 0; mp->msgattrs[i]; i++) free (mp->msgattrs[i]); + /* Close/free the sequence file if it is open */ + + if (mp->seqhandle) + lkfclosedata (mp->seqhandle, mp->seqname); + + if (mp->seqname) + free (mp->seqname); + free (mp->msgstats); /* free message status area */ free (mp); /* free main folder structure */ } diff --git a/sbr/folder_read.c b/sbr/folder_read.c index 29204484..02271bc6 100644 --- a/sbr/folder_read.c +++ b/sbr/folder_read.c @@ -23,7 +23,7 @@ */ struct msgs * -folder_read (char *name) +folder_read (char *name, int lockflag) { int msgnum, prefix_len, len, *mi; struct msgs *mp; @@ -54,6 +54,8 @@ folder_read (char *name) mp->hghsel = 0; mp->numsel = 0; mp->nummsg = 0; + mp->seqhandle = NULL; + mp->seqname = NULL; if (access (name, W_OK) == -1) set_readonly (mp); @@ -151,7 +153,7 @@ folder_read (char *name) /* * Read and initialize the sequence information. */ - seq_read (mp); + seq_read (mp, lockflag); return mp; } diff --git a/sbr/m_draft.c b/sbr/m_draft.c index e4791869..a8c973d2 100644 --- a/sbr/m_draft.c +++ b/sbr/m_draft.c @@ -38,7 +38,7 @@ m_draft (char *folder, char *msg, int use, int *isdf) if (chdir (buffer) == -1) adios (buffer, "unable to change directory to"); - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* diff --git a/sbr/seq_read.c b/sbr/seq_read.c index 2bea5367..eea27512 100644 --- a/sbr/seq_read.c +++ b/sbr/seq_read.c @@ -15,7 +15,7 @@ * static prototypes */ static int seq_init (struct msgs *, char *, char *); -static void seq_public (struct msgs *); +static void seq_public (struct msgs *, int); static void seq_private (struct msgs *); @@ -26,7 +26,7 @@ static void seq_private (struct msgs *); */ void -seq_read (struct msgs *mp) +seq_read (struct msgs *mp, int lockflag) { /* * Initialize the list of sequence names. Go ahead and @@ -41,7 +41,7 @@ seq_read (struct msgs *mp) return; /* Initialize the public sequences */ - seq_public (mp); + seq_public (mp, lockflag); /* Initialize the private sequences */ seq_private (mp); @@ -53,7 +53,7 @@ seq_read (struct msgs *mp) */ static void -seq_public (struct msgs *mp) +seq_public (struct msgs *mp, int lockflag) { int state; char *cp, seqfile[PATH_MAX]; @@ -73,7 +73,7 @@ seq_public (struct msgs *mp) /* get filename of sequence file */ snprintf (seqfile, sizeof(seqfile), "%s/%s", mp->foldpath, mh_seq); - if ((fp = lkfopendata (seqfile, "r")) == NULL) + if ((fp = lkfopendata (seqfile, lockflag ? "r+" : "r")) == NULL) return; /* Use m_getfld to scan sequence file */ @@ -96,7 +96,8 @@ seq_public (struct msgs *mp) } continue; - case BODY: + case BODY: + lkfclosedata (fp, seqfile); adios (NULL, "no blank lines are permitted in %s", seqfile); /* fall */ @@ -104,13 +105,19 @@ seq_public (struct msgs *mp) break; default: + lkfclosedata (fp, seqfile); adios (NULL, "%s is poorly formatted", seqfile); } break; /* break from for loop */ } m_getfld_state_destroy (&gstate); - lkfclosedata (fp, seqfile); + if (lockflag) { + mp->seqhandle = fp; + mp->seqname = getcpy(seqfile); + } else { + lkfclosedata (fp, seqfile); + } } diff --git a/sbr/seq_save.c b/sbr/seq_save.c index 837b4bab..3c53e4bc 100644 --- a/sbr/seq_save.c +++ b/sbr/seq_save.c @@ -30,8 +30,11 @@ seq_save (struct msgs *mp) sigset_t set, oset; /* check if sequence information has changed */ - if (!(mp->msgflags & SEQMOD)) + if (!(mp->msgflags & SEQMOD)) { + if (mp->seqhandle) + lkfclosedata (mp->seqhandle, mp->seqname); return; + } mp->msgflags &= ~SEQMOD; fp = NULL; @@ -74,7 +77,15 @@ priv: * If that fails (probably because folder is * readonly), then make sequence private. */ - if ((fp = lkfopendata (seqfile, "w")) == NULL + + if (mp->seqhandle) { + fp = mp->seqhandle; + mp->seqhandle = NULL; + free(mp->seqname); + mp->seqname = NULL; + rewind(fp); + ftruncate(fileno(fp), 0); + } else if ((fp = lkfopendata (seqfile, "w")) == NULL && (unlink (seqfile) == -1 || (fp = lkfopendata (seqfile, "w")) == NULL)) { admonish (attr, "unable to write"); diff --git a/uip/anno.c b/uip/anno.c index cf52021c..d18713ae 100644 --- a/uip/anno.c +++ b/uip/anno.c @@ -240,7 +240,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/burst.c b/uip/burst.c index e4792335..34ef10b0 100644 --- a/uip/burst.c +++ b/uip/burst.c @@ -158,7 +158,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/comp.c b/uip/comp.c index 416a8854..be7968d1 100644 --- a/uip/comp.c +++ b/uip/comp.c @@ -256,7 +256,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ @@ -267,6 +267,7 @@ main (int argc, char **argv) if (!m_convert (mp, msg)) done (1); seq_setprev (mp); /* set the previous-sequence */ + seq_save (mp); if (mp->numsel > 1) adios (NULL, "only one message at a time!"); diff --git a/uip/dist.c b/uip/dist.c index 3d247181..d3ceaec2 100644 --- a/uip/dist.c +++ b/uip/dist.c @@ -281,7 +281,7 @@ try_it_again: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/flist.c b/uip/flist.c index 1444acaf..d0945fce 100644 --- a/uip/flist.c +++ b/uip/flist.c @@ -468,7 +468,7 @@ AddFolder(char *name, int force) struct msgs *mp; /* Read folder and create message structure */ - if (!(mp = folder_read (name))) { + if (!(mp = folder_read (name, 0))) { /* Oops, error occurred. Record it and continue. */ AllocFolders(&folders, &nFoldersAlloced, nFolders + 1); f = &folders[nFolders++]; diff --git a/uip/fmttest.c b/uip/fmttest.c index dafdbdb8..8b81924b 100644 --- a/uip/fmttest.c +++ b/uip/fmttest.c @@ -507,7 +507,7 @@ process_messages(struct format *fmt, struct msgs_array *comps, if (chdir(maildir) < 0) adios(maildir, "unable to change directory to"); - if (!(mp = folder_read(folder))) + if (!(mp = folder_read(folder, 1))) adios(NULL, "unable to read folder %s", folder); if (mp->nummsg == 0) diff --git a/uip/folder.c b/uip/folder.c index e4defc70..29ef616a 100644 --- a/uip/folder.c +++ b/uip/folder.c @@ -403,7 +403,7 @@ get_folder_info_body (char *fold, char *msg, boolean *crawl_children) /* * create message structure and get folder info */ - if (!(mp = folder_read (fold))) { + if (!(mp = folder_read (fold, 1))) { admonish (NULL, "unable to read folder %s", fold); return 0; } diff --git a/uip/forw.c b/uip/forw.c index 18a4f03b..45fb740f 100644 --- a/uip/forw.c +++ b/uip/forw.c @@ -363,7 +363,7 @@ try_it_again: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/inc.c b/uip/inc.c index 6f7f5946..2b4c7ce9 100644 --- a/uip/inc.c +++ b/uip/inc.c @@ -515,7 +515,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 0))) adios (NULL, "unable to read folder %s", folder); go_to_it: diff --git a/uip/mark.c b/uip/mark.c index f99832a2..cc5a7133 100644 --- a/uip/mark.c +++ b/uip/mark.c @@ -159,7 +159,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* print some general debugging info */ diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 8bac6d37..459a2559 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -753,7 +753,7 @@ use_forw: if (!folder) folder = add (getfolder (1), NULL); - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 0))) adios (NULL, "unable to read folder %s", folder); for (ap = arguments; *ap; ap++) { cp = *ap; diff --git a/uip/mhlist.c b/uip/mhlist.c index 6ab50e39..38060739 100644 --- a/uip/mhlist.c +++ b/uip/mhlist.c @@ -278,7 +278,7 @@ do_cache: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 0))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/mhn.c b/uip/mhn.c index a5a7b05e..ced10f2e 100644 --- a/uip/mhn.c +++ b/uip/mhn.c @@ -512,7 +512,7 @@ do_cache: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/mhpath.c b/uip/mhpath.c index ef16bce2..3a0a628b 100644 --- a/uip/mhpath.c +++ b/uip/mhpath.c @@ -91,7 +91,7 @@ main(int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* diff --git a/uip/mhshow.c b/uip/mhshow.c index f5666b07..76a0d022 100644 --- a/uip/mhshow.c +++ b/uip/mhshow.c @@ -336,7 +336,7 @@ do_cache: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/mhstore.c b/uip/mhstore.c index 1865f14c..cd84aeef 100644 --- a/uip/mhstore.c +++ b/uip/mhstore.c @@ -304,7 +304,7 @@ do_cache: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/mhstoresbr.c b/uip/mhstoresbr.c index b978dbfb..6badafeb 100644 --- a/uip/mhstoresbr.c +++ b/uip/mhstoresbr.c @@ -872,7 +872,7 @@ output_content_folder (char *folder, char *filename) struct msgs *mp; /* Read the folder. */ - if ((mp = folder_read (folder))) { + if ((mp = folder_read (folder, 0))) { /* Link file into folder */ msgnum = folder_addmsg (&mp, filename, 0, 0, 0, 0, (char *)0); } else { diff --git a/uip/mhtest.c b/uip/mhtest.c index cf7f8642..aa6d8e43 100644 --- a/uip/mhtest.c +++ b/uip/mhtest.c @@ -270,7 +270,7 @@ do_cache: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/msh.c b/uip/msh.c index 2a562f00..c1f0fc3a 100644 --- a/uip/msh.c +++ b/uip/msh.c @@ -604,7 +604,7 @@ fsetup (char *folder) padios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 0))) padios (NULL, "unable to read folder %s", folder); /* check for empty folder */ @@ -831,7 +831,7 @@ check_folder (int scansw) low = mp->hghmsg + 1; folder_free (mp); /* free folder/message structure */ - if (!(mp = folder_read (fmsh))) + if (!(mp = folder_read (fmsh, 0))) padios (NULL, "unable to re-read folder %s", fmsh); hgh = mp->hghmsg; diff --git a/uip/packf.c b/uip/packf.c index 53e696af..d82ef782 100644 --- a/uip/packf.c +++ b/uip/packf.c @@ -138,7 +138,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to "); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/pick.c b/uip/pick.c index 5549ea64..281a1ac7 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -193,7 +193,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 0))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/rcvstore.c b/uip/rcvstore.c index 3afe12e3..9a89230e 100644 --- a/uip/rcvstore.c +++ b/uip/rcvstore.c @@ -193,7 +193,7 @@ main (int argc, char **argv) /* * read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* diff --git a/uip/refile.c b/uip/refile.c index 54b6e5f8..8dd04fb9 100644 --- a/uip/refile.c +++ b/uip/refile.c @@ -195,7 +195,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read source folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ @@ -282,7 +282,7 @@ opnfolds (struct st_fold *folders, int nfolders) if (chdir (nmaildir) == NOTOK) adios (nmaildir, "unable to change directory to"); - if (!(mp = folder_read (fp->f_name))) + if (!(mp = folder_read (fp->f_name, 1))) adios (NULL, "unable to read folder %s", fp->f_name); mp->curmsg = 0; diff --git a/uip/repl.c b/uip/repl.c index e8d64907..840b7620 100644 --- a/uip/repl.c +++ b/uip/repl.c @@ -386,7 +386,7 @@ try_it_again: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/rmm.c b/uip/rmm.c index d96622ed..02a55910 100644 --- a/uip/rmm.c +++ b/uip/rmm.c @@ -94,7 +94,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/scan.c b/uip/scan.c index 459fcb07..2996a2b5 100644 --- a/uip/scan.c +++ b/uip/scan.c @@ -201,7 +201,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/send.c b/uip/send.c index 510047a0..8a659222 100644 --- a/uip/send.c +++ b/uip/send.c @@ -350,7 +350,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (dfolder))) + if (!(mp = folder_read (dfolder, 1))) adios (NULL, "unable to read folder %s", dfolder); /* check for empty folder */ diff --git a/uip/sendsbr.c b/uip/sendsbr.c index e3a8195f..2a5ad829 100644 --- a/uip/sendsbr.c +++ b/uip/sendsbr.c @@ -1064,7 +1064,7 @@ annoaux (int fd) admonish (maildir, "unable to change directory to"); return; } - if (!(mp = folder_read (folder))) { + if (!(mp = folder_read (folder, 0))) { if (debugsw) admonish (NULL, "unable to read folder %s", folder); return; diff --git a/uip/show.c b/uip/show.c index dc426130..b73e56a8 100644 --- a/uip/show.c +++ b/uip/show.c @@ -221,7 +221,7 @@ usage: adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ diff --git a/uip/sortm.c b/uip/sortm.c index 6e7a7491..ea6b805e 100644 --- a/uip/sortm.c +++ b/uip/sortm.c @@ -199,7 +199,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ -- 2.48.1 From 64cc8937ff11ffd37736f27ceed5f4ff93161ca7 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Thu, 21 Mar 2013 13:47:04 -0400 Subject: [PATCH 16/16] Don't lock and write the sequence files until after the searching is complete. --- h/utils.h | 21 ++++++++++++++++++ sbr/utils.c | 16 ++++++++++++++ uip/pick.c | 61 ++++++++++++++++++++++++++++++++++------------------- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/h/utils.h b/h/utils.h index 188d262f..d07e46c1 100644 --- a/h/utils.h +++ b/h/utils.h @@ -12,12 +12,33 @@ int folder_exists(const char *); void create_folder(char *, int, void (*)(int)); int num_digits(int); +/* + * A vector of char array, used to hold a list of string message numbers + * or command arguments. + */ + struct msgs_array { int max, size; char **msgs; }; +/* + * Same as msgs_array, but for a vector of ints + */ + +struct msgnum_array { + int max, size; + int *msgnums; +}; + +/* + * Add a argument to the given msgs_array or msgnum_array structure; extend + * the array size if necessary + */ + void app_msgarg(struct msgs_array *, char *); +void app_msgnum(struct msgnum_array *, int); + int open_form(char **, char *); char *find_str (const char [], size_t, const char *); char *rfind_str (const char [], size_t, const char *); diff --git a/sbr/utils.c b/sbr/utils.c index 262e5767..81620567 100644 --- a/sbr/utils.c +++ b/sbr/utils.c @@ -226,6 +226,22 @@ app_msgarg(struct msgs_array *msgs, char *cp) msgs->msgs[msgs->size++] = cp; } +/* + * Append a message number to an array of them, resizing it if necessary. + * Like app_msgarg, but with a vector-of-ints instead. + */ + +void +app_msgnum(struct msgnum_array *msgs, int msgnum) +{ + if (msgs->size >= msgs->max) { + msgs->max += MAXMSGS; + msgs->msgnums = mh_xrealloc(msgs->msgnums, + msgs->max * sizeof(*msgs->msgnums)); + } + msgs->msgnums[msgs->size++] = msgnum; +} + /* Open a form or components file */ int open_form(char **form, char *def) diff --git a/uip/pick.c b/uip/pick.c index 281a1ac7..66198ff2 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -56,12 +56,13 @@ main (int argc, char **argv) { int publicsw = -1, zerosw = 1, vecp = 0; size_t seqp = 0; - int lo, hi, msgnum; + int msgnum; char *maildir, *folder = NULL, buf[100]; char *cp, **argp, **arguments; char *seqs[NUMATTRS + 1], *vec[MAXARGS]; struct msgs_array msgs = { 0, 0, NULL }; - struct msgs *mp; + struct msgnum_array nums = { 0, 0, NULL }; + struct msgs *mp, *mp2; register FILE *fp; done=putzero_done; @@ -204,7 +205,6 @@ main (int argc, char **argv) for (msgnum = 0; msgnum < msgs.size; msgnum++) if (!m_convert (mp, msgs.msgs[msgnum])) done (1); - seq_setprev (mp); /* set the previous-sequence */ /* * If we aren't saving the results to a sequence, @@ -219,9 +219,6 @@ main (int argc, char **argv) if (!pcompile (vec, NULL)) done (1); - lo = mp->lowsel; - hi = mp->hghsel; - /* If printing message numbers to standard out, force line buffering on. */ if (listsw) @@ -229,56 +226,76 @@ main (int argc, char **argv) /* * Scan through all the SELECTED messages and check for a - * match. If the message does not match, then unselect it. + * match. If there is NOT a match, then add it to a list to + * remove from the final sequence (it will make sense later) */ for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) { if (is_selected (mp, msgnum)) { if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL) admonish (cp, "unable to read message"); if (fp && pmatches (fp, msgnum, 0L, 0L)) { - if (msgnum < lo) - lo = msgnum; - if (msgnum > hi) - hi = msgnum; - if (listsw) printf ("%s\n", m_name (msgnum)); } else { - /* if it doesn't match, then unselect it */ - unset_selected (mp, msgnum); - mp->numsel--; + app_msgnum(&nums, msgnum); } if (fp) fclose (fp); } } - mp->lowsel = lo; - mp->hghsel = hi; - - if (mp->numsel <= 0) + if (nums.size >= mp->numsel) adios (NULL, "no messages match specification"); seqs[seqp] = NULL; + /* + * So, what's happening here? + * + * - Re-read the folder and sequences, but with locking. + * - Recreate the original set of selected messages from the command line + * - Set the previous sequence + * - Remove any messages that did NOT result in hits from the selection + * - Add to any new sequences + */ + + if (!(mp2 = folder_read (folder, 1))) + adios (NULL, "unable to reread folder %s", folder); + + for (msgnum = 0; msgnum < msgs.size; msgnum++) + if (!m_convert (mp2, msgs.msgs[msgnum])) + done (1); + seq_setprev (mp2); /* set the previous-sequence */ + + /* + * Nums contains a list of messages that we did NOT match. Remove + * that from the selection. + */ + + for (msgnum = 0; msgnum < nums.size; msgnum++) { + unset_selected(mp2, nums.msgnums[msgnum]); + mp2->numsel--; + } + /* * Add the matching messages to sequences */ for (seqp = 0; seqs[seqp]; seqp++) - if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw)) + if (!seq_addsel (mp2, seqs[seqp], publicsw, zerosw)) done (1); /* * Print total matched if not printing each matched message number. */ if (!listsw) { - printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s"); + printf ("%d hit%s\n", mp2->numsel, mp2->numsel == 1 ? "" : "s"); } context_replace (pfolder, folder); /* update current folder */ - seq_save (mp); /* synchronize message sequences */ + seq_save (mp2); /* synchronize message sequences */ context_save (); /* save the context file */ folder_free (mp); /* free folder/message structure */ + folder_free (mp2); done (0); return 1; } -- 2.48.1