/* profile entry for external ftp access command */
char *nmhaccessftp = "nmh-access-ftp";
+/* Default attachment header field name */
+char *attach_hdr = "Attach";
+
+
/*
extern struct swit anoyes[]; /* standard yes/no switches */
-#define ATTACHFORMATS 3 /* Number of send attach formats. */
-
/*
** general folder attributes
*/
** their values and reloading the various modules, nmh will run
** on any system.
*/
+extern char *attach_hdr;
extern char *buildmimeproc;
extern char *backup_prefix;
extern char *altmsglink;
int distout(char *, char *, char *);
void replout(FILE *, char *, char *, struct msgs *, int, int,
char *, char *, char *);
-int sendsbr(char **, int, char *, struct stat *, int, char *, int);
+int sendsbr(char **, int, char *, struct stat *, int);
int what_now(char *, int, int, char *, char *, int, struct msgs *,
char *, int, char *);
(profile, default: `@')
.RE
.PP
+.BR Attachment-Header :
+Attach
+.RS 5
+The (pseudo) header in draft messages, that contains files to be attached
+to the message on sending.
+If you like to type a lot, name it `X-MH-Attachment'.
+(profile, default: `Attach')
+.RE
+.PP
.BR automimeproc :
.RS 5
If defined and set to 1, then the
\&...]
.RB [ \-version ]
.RB [ \-help ]
-.RB [ \-attach
-.IR header-field-name ]
-.RB [ \-attachformat
-.IR 0 " | " 1 " | " 2 ]
.ad
.SH DESCRIPTION
.B Send
.B send
are actually performed by
.BR post .
-
.PP
-If a
-.I header-field-name
-is supplied using the
-.B -attach
-option, the draft is scanned for a header whose field name matches the
-supplied
-.IR header-field-name .
-The draft is converted to a MIME message if one or more matches are found.
+The draft is scanned for attachment header fields.
+Their name defaults to ``Attach'', but may be changed by the value of the
+.I Attachment-Header
+profile entry.
+If such header fields are found, or the body contains non-ASCII characters,
+the message is converted to a MIME message.
This conversion occurs before all other processing.
.PP
-The first part of the MIME message is the draft body if that body contains
-any non-blank characters.
-The body of each header field whose name matches the
-.I header-field-name
-is interpreted as a file name, and each file named is included as a separate
-part in the MIME message.
+The first part of the MIME message is the draft body if the body is non-empty.
+The body of each attachment header field is interpreted as a file name,
+and each file named is included as a separate part in the MIME message.
.PP
For file names with dot suffixes, the context is scanned for a
.I mhshow-suffix-
The content-type for the part is taken from that context entry if a match is
found.
If no match is found or the file does not have a dot suffix, the content-type
-is text/plain if the file contains only ASCII characters or application/octet-stream
-if it contains characters outside of the ASCII range.
-.PP
-Each part contains a name attribute that is the last component of the path name.
-A
-.I x-unix-mode
-attribute containing the file mode accompanies each part.
-Finally, a description attribute is generated by running the
-.I file
-command on the file.
-.PP
-The
-.B -attachformat
-option specifies the MIME header field formats: a value of
-.B 0,
-the default,
-includes the
-.I x-unix-mode
-attribute as noted above. A value of
-.B 1
-suppresses both that and the \*(lqContent-Description\*(rq header, and
-adds a \*(lqContent-Disposition\*(rq header. A value of
-.B 2
-adds the file
-.I modification-date
-parameter to the \*(lqContent-Disposition\*(rq header. You can
-specify one value in your profile, and override it for individual
-messages at the
-.I whatnow
-prompt.
+is text/plain if the file contains only ASCII characters or
+application/octet-stream otherwise.
.PP
-Here are example message part headers, for an attachment, for each of the
-.B -attachformat
-values:
+The last component of the path name is taken as the name of the MIME parts.
+A message part header for an attachment might be:
.PP
.nf
--attachformat 0:
-Content-Type: text/plain; name="VERSION"; x-unix-mode="0644";
- charset="us-ascii"
-Content-Description: ASCII text
-
--attachformat 1:
Content-Type: text/plain; name="VERSION"; charset="us-ascii"
Content-Disposition: attachment; filename="VERSION"
-
--attachformat 2:
-Content-Type: text/plain; name="VERSION"; charset="us-ascii"
-Content-Disposition: attachment; filename="VERSION"; modification-date="Mon, 19 Dec 2005 22:39:51 -0600"
.fi
.PP
If
^Draft\-Folder:~^To set the default draft\-folder
^Aliasfile:~^For a default alias file
^Signature:~^To determine the user's mail signature
+^Attachment\-Header:~^To set the name of the attachment header field
^mailproc:~^Program to post failure notices
^postproc:~^Program to post the message
.fi
.RB ` \-noverbose '
.RB ` \-nowatch '
.RB ` "\-width\ 72" '
-.RB ` "\-attachformat\ 0" '
.fi
.SH CONTEXT
.RI [ file ]
.RB [ \-version ]
.RB [ \-help ]
-.RB [ \-attach
-.IR header-field-name ]
.ad
.SH DESCRIPTION
.B Whatnow
};
static struct procstr procs[] = {
+ { "attachment-header", &attach_hdr },
{ "context", &context },
{ "mh-sequences", &mh_seq },
{ "backup-prefix", &backup_prefix },
};
static struct proc procs [] = {
- { "context", &context },
- { "mh-sequences", &mh_seq },
- { "buildmimeproc", &buildmimeproc },
- { "editor", &defaulteditor },
- { "fileproc", &fileproc },
- { "foldprot", &foldprot },
- { "incproc", &incproc },
- { "lproc", &lproc },
- { "mailproc", &mailproc },
- { "mhlproc", &mhlproc },
- { "moreproc", &moreproc },
- { "msgprot", &msgprot },
- { "postproc", &postproc },
- { "rmmproc", &rmmproc },
- { "sendmail", &sendmail },
- { "sendproc", &sendproc },
- { "showmimeproc", &showmimeproc },
- { "showproc", &showproc },
- { "version", &version_num },
- { "whatnowproc", &whatnowproc },
- { "etcdir", &mhetcdir },
- { "libdir", &mhlibdir },
- { "backup-prefix", &backup_prefix },
- { "altmsg-link", &altmsglink },
- { "draft-folder", &draftfolder },
- { NULL, NULL },
+ { "attachment-header", &attach_hdr },
+ { "context", &context },
+ { "mh-sequences", &mh_seq },
+ { "buildmimeproc", &buildmimeproc },
+ { "editor", &defaulteditor },
+ { "fileproc", &fileproc },
+ { "foldprot", &foldprot },
+ { "incproc", &incproc },
+ { "lproc", &lproc },
+ { "mailproc", &mailproc },
+ { "mhlproc", &mhlproc },
+ { "moreproc", &moreproc },
+ { "msgprot", &msgprot },
+ { "postproc", &postproc },
+ { "rmmproc", &rmmproc },
+ { "sendmail", &sendmail },
+ { "sendproc", &sendproc },
+ { "showmimeproc", &showmimeproc },
+ { "showproc", &showproc },
+ { "version", &version_num },
+ { "whatnowproc", &whatnowproc },
+ { "etcdir", &mhetcdir },
+ { "libdir", &mhlibdir },
+ { "backup-prefix", &backup_prefix },
+ { "altmsg-link", &altmsglink },
+ { "draft-folder", &draftfolder },
+ { NULL, NULL },
};
{ "server host", 6 },
#define SNOOPSW 31
{ "snoop", 5 },
-#define ATTACHSW 32
- { "attach", 6 },
-#define ATTACHFORMATSW 33
- { "attachformat", 7 },
-#define PORTSW 34
+#define PORTSW 32
{ "port server-port-name/number" , 4 },
{ NULL, 0 }
};
char *msgs[MAXARGS], *vec[MAXARGS];
struct msgs *mp;
struct stat st;
- char *attach = NULL; /* header field name for attachments */
- int attachformat = 0; /* mhbuild format specifier for attachments */
#ifdef UCI
FILE *fp;
#endif /* UCI */
vec[vecp++] = cp;
continue;
- case ATTACHSW:
- if (!(attach = *argp++) || *attach == '-')
- adios(NULL, "missing argument to %s",
- argp[-2]);
- continue;
-
- case ATTACHFORMATSW:
- if (! *argp || **argp == '-')
- adios(NULL, "missing argument to %s",
- argp[-1]);
- else {
- attachformat = atoi(*argp);
- if (attachformat < 0 || attachformat > ATTACHFORMATS - 1) {
- advise(NULL, "unsupported attachformat %d", attachformat);
- continue;
- }
- }
- ++argp;
- continue;
}
} else {
msgs[msgp++] = cp;
closefds(3);
for (msgnum = 0; msgnum < msgp; msgnum++) {
- switch (sendsbr(vec, vecp, msgs[msgnum], &st, 1, attach,
- attachformat)) {
+ switch (sendsbr(vec, vecp, msgs[msgnum], &st, 1)) {
case DONE:
done(++status);
case NOTOK:
/*
** external prototypes
*/
-int sendsbr(char **, int, char *, struct stat *, int, char *, int);
+int sendsbr(char **, int, char *, struct stat *, int);
char *getusername(void);
/*
static void annoaux(int);
static int sendaux(char **, int, char *, struct stat *);
-static int attach(char *, char *, int);
+static int attach(char *);
static void clean_up_temporary_files(void);
static int get_line(void);
-static void make_mime_composition_file_entry(char *, int);
+static void make_mime_composition_file_entry(char *);
/*
*/
int
-sendsbr(char **vec, int vecp, char *drft, struct stat *st,
- int rename_drft, char *attachment_header_field_name, int attachformat)
+sendsbr(char **vec, int vecp, char *drft, struct stat *st, int rename_drft)
{
int status;
char buffer[BUFSIZ], file[BUFSIZ];
original_draft = drft;
/*
- ** There might be attachments if a header field name for
- ** attachments is supplied. Convert the draft to a MIME message.
+ ** Convert the draft to a MIME message.
** Use the mhbuild composition file for the draft if there was
** a successful conversion because that now contains the MIME
** message. A nice side effect of this is that it leaves the
** original draft file untouched so that it can be retrieved
** and modified if desired.
*/
+ switch (attach(drft)) {
+ case OK:
+ drft = composition_file_name;
+ break;
- if (attachment_header_field_name != NULL) {
- switch (attach(attachment_header_field_name, drft,
- attachformat)) {
- case OK:
- drft = composition_file_name;
- break;
-
- case NOTOK:
- return (NOTOK);
+ case NOTOK:
+ return (NOTOK);
- case DONE:
- break;
- }
+ case DONE:
+ break;
}
done=armed_done;
}
static int
-attach(char *attachment_header_field_name, char *draft_file_name,
- int attachformat)
+attach(char *draft_file_name)
{
char buf[MAXPATHLEN + 6]; /* miscellaneous buffer */
int c; /* current character for body copy */
int has_attachment; /* draft has at least one attachment */
int has_body; /* draft has a message body */
+ int non_ascii; /* msg body contains non-ASCII chars */
int length; /* length of attachment header field name */
char *p; /* miscellaneous string pointer */
field = (char *)mh_xmalloc(field_size = 256);
/*
- ** Scan the draft file for a header field name that matches the
- ** -attach argument. The existence of one indicates that the
+ ** Scan the draft file for an attachment header field name.
+ ** The existence of one indicates that the
** draft has attachments. Bail out if there are no attachments
** because we're done. Read to the end of the headers even if
** we have no attachments.
*/
- length = strlen(attachment_header_field_name);
+ length = strlen(attach_hdr);
has_attachment = 0;
while (get_line() != EOF && *field != '\0' && *field != '-')
- if (strncasecmp(field, attachment_header_field_name, length)
- == 0 && field[length] == ':')
+ if (strncasecmp(field, attach_hdr, length) == 0 &&
+ field[length] == ':')
has_attachment = 1;
- if (has_attachment == 0)
- return (DONE);
-
/*
** We have at least one attachment. Look for at least one
** non-blank line in the body of the message which indicates
** content in the body.
+ ** Check if body contains at least one non-blank (= not empty)
+ ** and if it contains any non-ASCII chars (= need MIME).
*/
has_body = 0;
+ non_ascii = 0;
while (get_line() != EOF) {
for (p = field; *p != '\0'; p++) {
if (*p != ' ' && *p != '\t') {
has_body = 1;
- break;
+ }
+ if (*p > 127 || *p < 0) {
+ non_ascii = 1;
}
}
-
- if (has_body)
+ if (has_body && non_ascii)
break;
}
/*
- ** Make names for the temporary files.
+ ** Bail out if there are no attachments and only ASCII text.
+ ** This means we don't need to convert it to MIME.
*/
+ if (!has_attachment && non_ascii==0) {
+ return DONE;
+ }
+ /*
+ ** Else: mimify
+ */
+
+ /* Make names for the temporary files. */
strncpy(body_file_name,
m_mktemp(toabsdir(invo_name), NULL, NULL),
sizeof (body_file_name));
** non-attachment header fields to the temporary composition file.
** Then add the dashed line separator.
*/
-
rewind(draft_file);
-
while (get_line() != EOF && *field != '\0' && *field != '-')
- if (strncasecmp(field, attachment_header_field_name, length)
- != 0 || field[length] != ':')
+ if (strncasecmp(field, attach_hdr, length) != 0 ||
+ field[length] != ':')
fprintf(composition_file, "%s\n", field);
-
fputs("--------\n", composition_file);
/*
** Copy the message body to a temporary file.
*/
-
if (has_body) {
while ((c = getc(draft_file)) != EOF)
putc(c, body_file);
-
fclose(body_file);
}
** Add a mhbuild MIME composition file line for the body if
** there was one.
*/
-
- if (has_body)
- make_mime_composition_file_entry(body_file_name, attachformat);
+ if (has_body) {
+ /* old: make_mime_composition_file_entry(body_file_name); */
+ /* charset will be discovered/guessed by buildmimeproc */
+ fprintf(composition_file, "#text/plain %s\n", body_file_name);
+ }
/*
** Now, go back to the beginning of the draft file and look for
** header fields that specify attachments. Add a mhbuild MIME
** composition file for each.
*/
-
rewind(draft_file);
-
while (get_line() != EOF && *field != '\0' && *field != '-') {
- if (strncasecmp(field, attachment_header_field_name, length)
- == 0 && field[length] == ':') {
+ if (strncasecmp(field, attach_hdr, length) == 0 &&
+ field[length] == ':') {
for (p = field + length + 1; *p == ' ' || *p == '\t';
p++)
;
-
- make_mime_composition_file_entry(p, attachformat);
+ make_mime_composition_file_entry(p);
}
}
** We're ready to roll! Run mhbuild on the composition file.
** Note that mhbuild is in the context as buildmimeproc.
*/
-
sprintf(buf, "%s %s", buildmimeproc, composition_file_name);
if (system(buf) != 0) {
}
static void
-make_mime_composition_file_entry(char *file_name, int attachformat)
+make_mime_composition_file_entry(char *file_name)
{
int binary; /* binary character found flag */
int c; /* current character */
- char cmd[MAXPATHLEN + 6]; /* file command buffer */
char *content_type; /* mime content type */
FILE *fp; /* content and pipe file pointer */
struct node *np; /* context scan node pointer */
char *p; /* miscellaneous string pointer */
- struct stat st; /* file status buffer */
content_type = NULL;
** (if possible) by running the "file" command on the file.
*/
- if (stat(file_name, &st) == -1 || access(file_name, R_OK) != 0) {
+ if (access(file_name, R_OK) != 0) {
clean_up_temporary_files();
adios(NULL, "unable to access file \"%s\"", file_name);
}
- switch (attachformat) {
- case 0:
- /* Insert name, file mode, and Content-Id. */
- fprintf(composition_file,
- "#%s; name=\"%s\"; x-unix-mode=0%.3ho",
- content_type, ((p = strrchr(file_name, '/'))
- == NULL) ? file_name : p + 1,
- (unsigned short)(st.st_mode & 0777));
-
- if (strlen(file_name) > MAXPATHLEN) {
- clean_up_temporary_files();
- adios(NULL, "attachment file name `%s' too long.",
- file_name);
- }
-
- sprintf(cmd, "file '%s'", file_name);
-
- if ((fp = popen(cmd, "r")) != (FILE *)0 &&
- fgets(cmd, sizeof (cmd), fp) != NULL) {
- *strchr(cmd, '\n') = '\0';
-
- /*
- ** The output of the "file" command is of the form
- **
- ** file: description
- **
- ** Strip off the "file:" and subsequent white space.
- */
-
- for (p = cmd; *p != '\0'; p++) {
- if (*p == ':') {
- for (p++; *p != '\0'; p++) {
- if (*p != '\t')
- break;
- }
- break;
- }
- }
-
- if (*p != '\0')
- /* Insert Content-Description. */
- fprintf(composition_file, " [ %s ]", p);
-
- pclose(fp);
- }
-
- break;
- case 1:
- if (stringdex(toabsdir(invo_name), file_name) == 0) {
- /*
- ** Content had been placed by send into a temp file.
- ** Don't generate Content-Disposition header, because
- ** it confuses Microsoft Outlook, Build 10.0.6626, at
- ** least.
- */
- fprintf(composition_file, "#%s <>", content_type);
- } else {
- /*
- ** Suppress Content-Id, insert simple
- ** Content-Disposition.
- */
- fprintf(composition_file,
- "#%s; name=\"%s\" <>{attachment}",
- content_type,
- ((p = strrchr(file_name, '/')) == NULL) ?
- file_name : p + 1);
- }
-
- break;
- case 2:
- if (stringdex(toabsdir(invo_name), file_name) == 0) {
- /*
- ** Content had been placed by send into a temp file.
- ** Don't generate Content-Disposition header, because
- ** it confuses Microsoft Outlook, Build 10.0.6626, at
- ** least.
- */
- fprintf(composition_file, "#%s <>", content_type);
- } else {
- /*
- ** Suppress Content-Id, insert Content-Disposition
- ** with modification date.
- */
- fprintf(composition_file,
- "#%s; name=\"%s\" <>{attachment; modification-date=\"%s\"}",
- content_type,
- ((p = strrchr(file_name, '/')) == NULL) ?
- file_name : p + 1, dtime(&st.st_mtime, 0));
- }
- break;
- default:
- adios(NULL, "unsupported attachformat %d", attachformat);
+ if (stringdex(toabsdir(invo_name), file_name) == 0) {
+ /*
+ ** Content had been placed by send into a temp file.
+ ** Don't generate Content-Disposition header, because
+ ** it confuses Microsoft Outlook, Build 10.0.6626, at
+ ** least.
+ */
+ fprintf(composition_file, "#%s <>", content_type);
+ } else {
+ /*
+ ** Suppress Content-Id, insert simple
+ ** Content-Disposition.
+ */
+ fprintf(composition_file,
+ "#%s; name=\"%s\" <>{attachment}",
+ content_type,
+ ((p = strrchr(file_name, '/')) == NULL) ?
+ file_name : p + 1);
}
- /*
- ** Finish up with the file name.
- */
-
+ /* Finish up with the file name. */
fprintf(composition_file, " %s\n", file_name);
return;
if (verbsw)
vec[vecp++] = "-verbose";
- switch (sendsbr(vec, vecp, tmpfil, &st, 0, NULL, 0)) {
+ switch (sendsbr(vec, vecp, tmpfil, &st, 0)) {
case DONE:
case NOTOK:
status++;
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
**
-** Several options have been added to ease the inclusion of attachments
-** using the header field name mechanism added to anno and send. The
-** -attach option is used to specify the header field name for attachments.
+** The inclusion of attachments is eased by
+** using the header field name mechanism added to anno and send.
+** The header field name for attachments is predefined.
**
** Several commands have been added at the whatnow prompt:
**
{ "version", 0 },
#define HELPSW 4
{ "help", 0 },
-#define ATTACHSW 5
- { "attach header-field-name", 0 },
{ NULL, 0 }
};
char buf[BUFSIZ], prompt[BUFSIZ];
char **argp, **arguments;
struct stat st;
- char *attach = NULL; /* attachment header field name */
char cwd[MAXPATHLEN + 1]; /* current working directory */
char file[MAXPATHLEN + 1]; /* file name buffer */
char shell[MAXPATHLEN + 1]; /* shell response buffer */
argp[-2]);
continue;
- case ATTACHSW:
- if (attach != NULL)
- adios(NULL, "only one attachment header field name at a time!");
- if (!(attach = *argp++) || *attach == '-')
- adios(NULL, "missing argument to %s",
- argp[-2]);
- continue;
}
}
if (drft)
** -n numbers listing
*/
- if (attach == NULL) {
- advise(NULL, "can't list because no header field name was given.");
- break;
- }
-
l = NULL;
n = 0;
advise(NULL, "usage is alist [-ln].");
else
- annolist(drft, attach, l, n);
+ annolist(drft, attach_hdr, l, n);
break;
** Attach files to current draft.
*/
- if (attach == NULL) {
- advise(NULL, "can't attach because no header field name was given.");
- break;
- }
-
if (*(argp+1) == NULL) {
advise(NULL, "attach command requires file argument(s).");
break;
*(strchr(shell, '\n')) = '\0';
if (*shell == '/')
- annotate(drft, attach, shell,
- 1, 0, -2, 1);
+ annotate(drft, attach_hdr,
+ shell, 1, 0,
+ -2, 1);
else {
sprintf(file, "%s/%s", cwd,
shell);
- annotate(drft, attach, file,
- 1, 0, -2, 1);
+ annotate(drft, attach_hdr,
+ file, 1, 0,
+ -2, 1);
}
}
case DETACHCMDSW:
/*
** Detach files from current draft.
- */
-
- if (attach == NULL) {
- advise(NULL, "can't detach because no header field name was given.");
- break;
- }
-
- /*
+ **
** Scan the arguments for a -n. Mixed file
** names and numbers aren't allowed, so this
** catches a -n anywhere in the argument list.
if (**arguments != '\0') {
n = atoi(*arguments);
- annotate(drft, attach, NULL,
- 1, 0, n, 1);
+ annotate(drft, attach_hdr,
+ NULL, 1, 0,
+ n, 1);
for (argp = arguments + 1; *argp != NULL; argp++) {
if (atoi(*argp) > n) {
while (fgets(shell, sizeof (shell), f)
!= NULL) {
*(strchr(shell, '\n')) = '\0';
- annotate(drft, attach, shell,
+ annotate(drft, attach_hdr, shell,
1, 0, 0, 1);
}
pclose(f);
{ "server host", 6 },
#define SNOOPSW 31
{ "snoop", -5 },
-#define SNDATTACHSW 32
- { "attach file", 6 },
-#define SNDATTACHFORMAT 33
- { "attachformat", 7 },
-#define PORTSW 34
+#define PORTSW 32
{ "port server-port-name/number", 4 },
{ NULL, 0 }
};
char *cp, buf[BUFSIZ], **argp;
char **arguments, *vec[MAXARGS];
struct stat st;
- char *attach = NULL; /* attachment header field name */
- int attachformat = 0; /* mhbuild format specifier for attachments */
#ifndef lint
int distsw = 0;
vec[vecp++] = cp;
continue;
- case SNDATTACHSW:
- if (!(attach = *argp++) || *attach == '-') {
- advise(NULL, "missing argument to %s",
- argp[-2]);
- return;
- }
- continue;
-
- case SNDATTACHFORMAT:
- if (! *argp || **argp == '-')
- adios(NULL, "missing argument to %s",
- argp[-1]);
- else {
- attachformat = atoi(*argp);
- if (attachformat < 0 || attachformat > ATTACHFORMATS - 1) {
- advise(NULL, "unsupported attachformat %d", attachformat);
- continue;
- }
- }
- ++argp;
- continue;
}
}
advise(NULL, "usage: %s [switches]", sp);
vec[0] = mhbasename(postproc);
closefds(3);
- if (sendsbr(vec, vecp, file, &st, 1, attach, attachformat) == OK)
+ if (sendsbr(vec, vecp, file, &st, 1) == OK)
done(0);
}