2006-02-20 David Levine <levinedl@acm.org>
+ * h/mh.h, h/prototypes.h, uip/mhbuildsbr.c, uip/send.c,
+ uip/sendsbr.c, uip/viamail.c, uip/whatnowsbr.c, man/send.man:
+ added -attachformat switch to send, to support alternate MIME
+ header contents when using -attach. See send man page for
+ description.
+
* man/mhbuild.man: wrapped one appearance of "Content-Disposition"
with quotes, to be consistent with others.
extern struct swit anoyes[]; /* standard yes/no switches */
+#define ATTACHFORMATS 3 /* Number of send attach formats. */
+
/*
* general folder attributes
*/
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 sendsbr (char **, int, char *, struct stat *, int, char *, int);
int what_now (char *, int, int, char *, char *,
int, struct msgs *, char *, int, char *);
.RB [ \-help ]
.RB [ \-attach
.IR header-field-name ]
+.RB [ \-attachformat
+.IR 0 " | " 1 " | " 2 ]
.ad
.SH DESCRIPTION
.B Send
.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.
+.PP
+Here are example message part headers for each of the
+.B -attachformat
+values:
+.PP
+.nf
+-attachformat 0:
+Content-Type: text/plain; name="VERSION"; x-unix-mode="0644";
+Content-Description: ASCII text
+
+-attachformat 1:
+Content-Type: text/plain; charset="us-ascii"
+Content-Disposition: attachment; filename="VERSION"
+
+-attachformat 2:
+Content-Type: text/plain; charset="us-ascii"
+Content-Disposition: attachment; filename="VERSION"; modification-date="Mon, 19 Dec 2005 22:39:51 -0600"
+.fi
+.PP
If
.B \-push
is specified,
.RB ` \-noverbose '
.RB ` \-nowatch '
.RB ` "\-width\ 72" '
+.RB ` "\-attachformat\ 0" '
.fi
.SH CONTEXT
The r1bindex call skips any leading directory
components. */
if (ct->c_dispo)
- ct->c_dispo =
- incl_name_value (ct->c_dispo,
- "filename",
- r1bindex (extract_name_value ("name",
- ci->ci_magic),
- '/'));
+ ct->c_dispo =
+ incl_name_value (ct->c_dispo,
+ "filename",
+ r1bindex (extract_name_value ("name",
+ ci->
+ ci_magic),
+ '/'));
}
else
advise (NULL,
"extraneous information in message %s's %s: field\n%*.*s(%s)",
- ct->c_file, TYPE_FIELD, i, i, "", cp);
+ ct->c_file, TYPE_FIELD, i, i, "", cp);
}
return OK;
/* Make sure that buf contains at least one appearance of name,
- followed by =. If not, append both name and value. Note that name
- should not contain a trailing =. And quotes will be added around
- the value. Typical usage: make sure that a Content-Disposition
- header contains filename="foo". If it doesn't and value does, use
- value from that. */
+ followed by =. If not, insert both name and value, just after
+ first semicolon, if any. Note that name should not contain a
+ trailing =. And quotes will be added around the value. Typical
+ usage: make sure that a Content-Disposition header contains
+ filename="foo". If it doesn't and value does, use value from
+ that. */
static char *
incl_name_value (char *buf, char *name, char *value) {
- char *newbuf = buf;
+ char *newbuf = buf;
- /* Assume that name is non-null. */
- if (buf && value) {
- char *name_plus_equal = concat (name, "=", NULL);
+ /* Assume that name is non-null. */
+ if (buf && value) {
+ char *name_plus_equal = concat (name, "=", NULL);
- if (! strstr (buf, name_plus_equal)) {
- char *appendage;
- char *cp;
+ if (! strstr (buf, name_plus_equal)) {
+ char *insertion;
+ char *cp;
+ char *prefix, *suffix;
- /* Trim trailing space, esp. newline. */
- for (cp = &buf[strlen (buf) - 1]; cp >= buf && isspace (*cp); --cp) {
- *cp = '\0';
- }
+ /* Trim trailing space, esp. newline. */
+ for (cp = &buf[strlen (buf) - 1];
+ cp >= buf && isspace (*cp);
+ --cp) {
+ *cp = '\0';
+ }
- appendage = concat ("; ", name, "=", "\"", value, "\"\n", NULL);
- newbuf = add (appendage, buf);
- free (appendage);
- }
+ insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
+
+ /* Insert at first semicolon, if any. If none, append to
+ end. */
+ prefix = add (buf, NULL);
+ if ((cp = strchr (prefix, ';'))) {
+ suffix = concat (cp, NULL);
+ *cp = '\0';
+ newbuf = concat (prefix, insertion, suffix, "\n", NULL);
+ free (suffix);
+ } else {
+ /* Append to end. */
+ newbuf = concat (buf, insertion, "\n", NULL);
+ }
+
+ free (prefix);
+ free (insertion);
+ free (buf);
+ }
- free (name_plus_equal);
- }
+ free (name_plus_equal);
+ }
- return newbuf;
+ return newbuf;
}
-/* Extract just name_suffix="foo", if any, from value. If there isn't
+/* Extract just name_suffix="foo", if any, from value. If there isn't
one, return the entire value. Note that, for example, a name_suffix
of name will match filename="foo", and return foo. */
static char *
extract_name_value (char *name_suffix, char *value) {
- char *extracted_name_value = value;
- char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
- char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
- char *cp;
-
- free (name_suffix_plus_quote);
- if (name_suffix_equals) {
- char *name_suffix_begin;
-
- /* Find first \". */
- for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
- name_suffix_begin = ++cp;
- /* Find second \". */
- for (; *cp != '"'; ++cp) /* empty */;
-
- extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
- memcpy (extracted_name_value, name_suffix_begin, cp - name_suffix_begin);
- extracted_name_value[cp - name_suffix_begin] = '\0';
- }
-
- return extracted_name_value;
+ char *extracted_name_value = value;
+ char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
+ char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
+ char *cp;
+
+ free (name_suffix_plus_quote);
+ if (name_suffix_equals) {
+ char *name_suffix_begin;
+
+ /* Find first \". */
+ for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
+ name_suffix_begin = ++cp;
+ /* Find second \". */
+ for (; *cp != '"'; ++cp) /* empty */;
+
+ extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
+ memcpy (extracted_name_value,
+ name_suffix_begin,
+ cp - name_suffix_begin);
+ extracted_name_value[cp - name_suffix_begin] = '\0';
+ }
+
+ return extracted_name_value;
}
{ "user", SASLminc(-4) },
#define ATTACHSW 40
{ "attach", 6 },
+#define ATTACHFORMATSW 41
+ { "attachformat", 7 },
{ NULL, 0 }
};
struct msgs *mp;
struct stat st;
char *attach = (char *)0; /* header field name for attachments */
+ int attachformat = 0; /* mhbuild format specifier for attachments */
#ifdef UCI
FILE *fp;
#endif /* UCI */
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)) {
+ switch (sendsbr (vec, vecp, msgs[msgnum], &st, 1, attach,
+ attachformat)) {
case DONE:
done (++status);
case NOTOK:
/*
* external prototypes
*/
-int sendsbr (char **, int, char *, struct stat *, int, char *);
+int sendsbr (char **, int, char *, struct stat *, int, char *, int);
int done (int);
char *getusername (void);
static int splitmsg (char **, int, char *, struct stat *, int);
static int sendaux (char **, int, char *, struct stat *);
-static int attach(char *, char *);
+static int attach(char *, char *, int);
static void clean_up_temporary_files(void);
static int get_line(void);
-static void make_mime_composition_file_entry(char *);
+static void make_mime_composition_file_entry(char *, int);
/*
*/
int
-sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft, char *attachment_header_field_name)
+sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft, char *attachment_header_field_name, int attachformat)
{
int status;
char buffer[BUFSIZ], file[BUFSIZ];
*/
if (attachment_header_field_name != (char *)0) {
- switch (attach(attachment_header_field_name, drft)) {
+ switch (attach(attachment_header_field_name, drft, attachformat)) {
case OK:
drft = composition_file_name;
break;
}
static int
-attach(char *attachment_header_field_name, char *draft_file_name)
+attach(char *attachment_header_field_name, char *draft_file_name,
+ int attachformat)
{
char buf[MAXPATHLEN + 6]; /* miscellaneous buffer */
int c; /* current character for body copy */
*/
if (has_body)
- make_mime_composition_file_entry(body_file_name);
+ make_mime_composition_file_entry(body_file_name, attachformat);
/*
* Now, go back to the beginning of the draft file and look for header fields
for (p = field + length + 1; *p == ' ' || *p == '\t'; p++)
;
- make_mime_composition_file_entry(p);
+ make_mime_composition_file_entry(p, attachformat);
}
}
}
static void
-make_mime_composition_file_entry(char *file_name)
+make_mime_composition_file_entry(char *file_name, int attachformat)
{
int binary; /* binary character found flag */
int c; /* current character */
adios((char *)0, "unable to access file \"%s\"", file_name);
}
- (void)fprintf(composition_file, "#%s; name=\"%s\"; x-unix-mode=0%.3ho",
- content_type, ((p = strrchr(file_name, '/')) == (char *)0) ? file_name : p + 1, (unsigned short)(st.st_mode & 0777));
-
- if (strlen(file_name) > MAXPATHLEN) {
- clean_up_temporary_files();
- adios((char *)0, "attachment file name `%s' too long.", file_name);
- }
-
- (void)sprintf(cmd, "file '%s'", file_name);
-
- if ((fp = popen(cmd, "r")) != (FILE *)0 && fgets(cmd, sizeof (cmd), fp) != (char *)0) {
- *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')
- (void)fprintf(composition_file, " [ %s ]", p);
-
- (void)pclose(fp);
+ switch (attachformat) {
+ case 0:
+ /* Insert name, file mode, and Content-Id. */
+ (void)fprintf(composition_file, "#%s; name=\"%s\"; x-unix-mode=0%.3ho",
+ content_type, ((p = strrchr(file_name, '/')) == (char *)0) ? file_name : p + 1, (unsigned short)(st.st_mode & 0777));
+
+ if (strlen(file_name) > MAXPATHLEN) {
+ clean_up_temporary_files();
+ adios((char *)0, "attachment file name `%s' too long.", file_name);
+ }
+
+ (void)sprintf(cmd, "file '%s'", file_name);
+
+ if ((fp = popen(cmd, "r")) != (FILE *)0 && fgets(cmd, sizeof (cmd), fp) != (char *)0) {
+ *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. */
+ (void)fprintf(composition_file, " [ %s ]", p);
+
+ (void)pclose(fp);
+ }
+
+ break;
+ case 1:
+ /* Suppress Content-Id, insert simple Content-Disposition. */
+ (void) fprintf (composition_file, "#%s <>{attachment}", content_type);
+
+ break;
+ case 2:
+ /* Suppress Content-Id, insert Content-Disposition with
+ modification date. */
+ (void) fprintf (composition_file,
+ "#%s <>{attachment; modification-date=\"%s\"}",
+ content_type,
+ dtime (&st.st_mtim, 0));
+
+ break;
+ default:
+ adios ((char *)0, "unsupported attachformat %d", attachformat);
}
/*
if (verbsw)
vec[vecp++] = "-verbose";
- switch (sendsbr (vec, vecp, tmpfil, &st, 0, (char *)0)) {
+ switch (sendsbr (vec, vecp, tmpfil, &st, 0, (char *)0, 0)) {
case DONE:
case NOTOK:
status++;
{ "user", SASLminc(-4) },
#define SNDATTACHSW 39
{ "attach file", 6 },
+#define SNDATTACHFORMAT 40
+ { "attachformat", 7 },
{ NULL, 0 }
};
char **arguments, *vec[MAXARGS];
struct stat st;
char *attach = (char *)0; /* attachment header field name */
+ int attachformat = 0; /* mhbuild format specifier for
+ attachments */
#ifndef lint
int distsw = 0;
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] = r1bindex (postproc, '/');
closefds (3);
- if (sendsbr (vec, vecp, file, &st, 1, attach) == OK)
+ if (sendsbr (vec, vecp, file, &st, 1, attach, attachformat) == OK)
done (0);
}