X("nofixboundary", 0, NFIXBOUNDARYSW) \
X("fixcte", 0, FIXCTESW) \
X("nofixcte", 0, NFIXCTESW) \
+ X("fixtype mimetype", 0, FIXTYPESW) \
X("file file", 0, FILESW) \
X("outfile file", 0, OUTFILESW) \
X("rmmproc program", 0, RPROCSW) \
typedef struct fix_transformations {
int fixboundary;
int fixcte;
+ svector_t fixtypes;
int reformat;
int replacetextplain;
int decodetext;
static int fix_boundary (CT *, int *);
static int get_multipart_boundary (CT, char **);
static int replace_boundary (CT, char *, char *);
+static int fix_types (CT, svector_t, int *);
+static char *replace_substring (char **, const char *, const char *);
static int fix_multipart_cte (CT, int *);
static int set_ce (CT, int);
static int ensure_text_plain (CT *, CT, int *, int);
int status = OK;
fix_transformations fx;
fx.reformat = fx.fixcte = fx.fixboundary = 1;
+ fx.fixtypes = NULL;
fx.replacetextplain = 0;
fx.decodetext = CE_8BIT;
fx.textcharset = NULL;
case NFIXCTESW:
fx.fixcte = 0;
continue;
+ case FIXTYPESW:
+ if (! (cp = *argp++) || (*cp == '-' && cp[1]))
+ adios (NULL, "missing argument to %s", argp[-2]);
+ if (! strncasecmp (cp, "multipart/", 10) ||
+ ! strncasecmp (cp, "message/", 8)) {
+ adios (NULL, "-fixtype %s not allowed", cp);
+ } else if (! strchr (cp, '/')) {
+ adios (NULL, "-fixtype requires type/subtype");
+ }
+ if (fx.fixtypes == NULL) { fx.fixtypes = svector_create (10); }
+ svector_push_back (fx.fixtypes, cp);
+ continue;
case REFORMATSW:
fx.reformat = 1;
continue;
status = 1;
}
+ if (fx.fixtypes != NULL) { svector_free (fx.fixtypes); }
free (outfile);
free (file);
if (status == OK && fx->fixboundary) {
status = fix_boundary (ctp, &message_mods);
}
+ if (status == OK && fx->fixtypes != NULL) {
+ status = fix_types (*ctp, fx->fixtypes, &message_mods);
+ }
if (status == OK && fx->fixcte) {
status = fix_multipart_cte (*ctp, &message_mods);
}
}
+static int
+fix_types (CT ct, svector_t fixtypes, int *message_mods) {
+ int status = OK;
+
+ switch (ct->c_type) {
+ case CT_MULTIPART: {
+ struct multipart *m = (struct multipart *) ct->c_ctparams;
+ struct part *part;
+
+ for (part = m->mp_parts; status == OK && part; part = part->mp_next) {
+ status = fix_types (part->mp_part, fixtypes, message_mods);
+ }
+ break;
+ }
+
+ case CT_MESSAGE:
+ if (ct->c_subtype == MESSAGE_EXTERNAL) {
+ struct exbody *e = (struct exbody *) ct->c_ctparams;
+
+ status = fix_types (e->eb_content, fixtypes, message_mods);
+ }
+ break;
+
+ default: {
+ char **typep, *type;
+
+ if (ct->c_ctinfo.ci_type && ct->c_ctinfo.ci_subtype) {
+ for (typep = svector_strs (fixtypes);
+ typep && (type = *typep);
+ ++typep) {
+ char *type_subtype =
+ concat (ct->c_ctinfo.ci_type, "/", ct->c_ctinfo.ci_subtype,
+ NULL);
+
+ if (! strcasecmp (type, type_subtype) &&
+ decode_part (ct) == OK &&
+ ct->c_cefile.ce_file != NULL) {
+ char *ct_type_subtype = mime_type (ct->c_cefile.ce_file);
+ char *cp;
+
+ if ((cp = strchr (ct_type_subtype, ';'))) {
+ /* Truncate to remove any parameter list from
+ mime_type () result. */
+ *cp = '\0';
+ }
+
+ if (strcasecmp (type, ct_type_subtype)) {
+ char *ct_type, *ct_subtype;
+ HF hf;
+
+ /* The Content-Type header does not match the
+ content, so update these struct Content
+ fields to match:
+ * c_type, c_subtype
+ * c_ctinfo.ci_type, c_ctinfo.ci_subtype
+ * c_ctline
+ */
+ /* Extract type and subtype from type/subtype. */
+ ct_type = getcpy (ct_type_subtype);
+ if ((cp = strchr (ct_type, '/'))) {
+ *cp = '\0';
+ ct_subtype = getcpy (++cp);
+ } else {
+ advise (NULL, "missing / in MIME type of %s %s",
+ ct->c_file, ct->c_partno);
+ free (ct_type);
+ return NOTOK;
+ }
+
+ ct->c_type = ct_str_type (ct_type);
+ ct->c_subtype = ct_str_subtype (ct->c_type, ct_subtype);
+
+ free (ct->c_ctinfo.ci_type);
+ ct->c_ctinfo.ci_type = ct_type;
+ free (ct->c_ctinfo.ci_subtype);
+ ct->c_ctinfo.ci_subtype = ct_subtype;
+ if (! replace_substring (&ct->c_ctline, type,
+ ct_type_subtype)) {
+ advise (NULL, "did not find %s in %s",
+ type, ct->c_ctline);
+ }
+
+ /* Update Content-Type header field. */
+ for (hf = ct->c_first_hf; hf; hf = hf->next) {
+ if (! strcasecmp (TYPE_FIELD, hf->name)) {
+ if (replace_substring (&hf->value, type,
+ ct_type_subtype)) {
+ ++*message_mods;
+ if (verbosw) {
+ report (NULL, ct->c_partno, ct->c_file,
+ "change Content-Type in header "
+ "from %s to %s",
+ type, ct_type_subtype);
+ }
+ break;
+ } else {
+ advise (NULL, "did not find %s in %s",
+ type, hf->value);
+ }
+ }
+ }
+ }
+ free (ct_type_subtype);
+ }
+ free (type_subtype);
+ }
+ }
+ }}
+
+ return status;
+}
+
+char *
+replace_substring (char **str, const char *old, const char *new) {
+ char *cp;
+
+ if ((cp = strstr (*str, old))) {
+ char *remainder = cp + strlen (old);
+ char *prefix, *new_str;
+
+ if (cp - *str) {
+ prefix = getcpy (*str);
+ *(prefix + (cp - *str)) = '\0';
+ new_str = concat (prefix, new, remainder, NULL);
+ free (prefix);
+ } else {
+ new_str = concat (new, remainder, NULL);
+ }
+
+ free (*str);
+
+ return *str = new_str;
+ } else {
+ return NULL;
+ }
+}
+
+
static int
fix_multipart_cte (CT ct, int *message_mods) {
int status = OK;
if ((tempfile = m_mktemp2 (NULL, invo_name, NULL, NULL)) == NULL) {
advise (NULL, "unable to create temporary file in %s",
get_temp_dir());
- }
- tmp_plain_file = add (tempfile, NULL);
- if (reformat_part (tp_part, tmp_plain_file,
- tp_part->c_ctinfo.ci_type,
- tp_part->c_ctinfo.ci_subtype,
- tp_part->c_type) == OK) {
- return tp_part;
+ } else {
+ tmp_plain_file = add (tempfile, NULL);
+ if (reformat_part (tp_part, tmp_plain_file,
+ tp_part->c_ctinfo.ci_type,
+ tp_part->c_ctinfo.ci_subtype,
+ tp_part->c_type) == OK) {
+ return tp_part;
+ }
}
}
for (hf = ct->c_first_hf; hf; hf = hf->next) {
size_t len = strlen (hf->value);
+ if (strcasecmp (hf->name, TYPE_FIELD) != 0 &&
+ strcasecmp (hf->name, DISPO_FIELD) != 0) {
+ /* Only do this for Content-Type and
+ Content-Disposition fields because those are the
+ only headers that parse_mime() warns about. */
+ continue;
+ }
+
/* whitespace following a trailing ';' will be nuked as well */
- if (hf->value[len - 1] == '\n')
- while (isspace((unsigned char)(hf->value[len - 2]))) len--;
+ if (hf->value[len - 1] == '\n') {
+ while (isspace((unsigned char)(hf->value[len - 2]))) {
+ if (len-- == 0) { break; }
+ }
+ }
if (hf->value[len - 2] == ';') {
/* Remove trailing ';' from parameter value. */
/* Also, if Content-Type parameter, remove trailing ';'
from ct->c_ctline. This probably isn't necessary
but can't hurt. */
- if (strcasecmp(hf->name, "Content-Type") == 0 && ct->c_ctline) {
+ if (strcasecmp(hf->name, TYPE_FIELD) == 0 && ct->c_ctline) {
size_t l = strlen(ct->c_ctline) - 1;
- while (isspace((unsigned char)(ct->c_ctline[l])) || ct->c_ctline[l] == ';')
+ while (isspace((unsigned char)(ct->c_ctline[l])) ||
+ ct->c_ctline[l] == ';') {
ct->c_ctline[l--] = '\0';
+ if (l == 0) { break; }
+ }
}
++*message_mods;