Skip to content

Commit 7b36ddc

Browse files
committed
imap.c: Improve attachment handling.
Port the LBBS fix for attachment handling from: InterLinked1/lbbs@d31303c
1 parent f4f3b56 commit 7b36ddc

3 files changed

Lines changed: 120 additions & 52 deletions

File tree

evergreen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ struct attachment {
225225
struct attachment *next;
226226
struct attachment *prev;
227227
const char *name;
228+
unsigned int deleted:1;
229+
unsigned int detached:1;
228230
size_t size;
229231
char data[];
230232
};

imap.c

Lines changed: 116 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,27 +2590,30 @@ static int fetch_mime_recurse(struct message_data *mdata, struct mailmime *mime,
25902590
{
25912591
struct mailmime_fields *fields;
25922592
struct mailmime_content *content_type;
2593-
int text_plain = 0, text_html = 0;
2594-
int is_attachment = 0;
2593+
int is_attachment = 0, is_multipart = 0, is_text = 0, text_plain = 0, text_html = 0;
25952594
int encoding;
25962595
clistiter *cur;
25972596
clist *parameters;
25982597

25992598
level++;
26002599

2601-
#ifdef DEBUG_MODE
2600+
#ifdef DEBUG_MIME
2601+
#define MIME_DEBUG(level, fmt, ...) client_debug(level, fmt, ## __VA_ARGS__)
2602+
#else
2603+
#define MIME_DEBUG(level, fmt, ...)
2604+
#endif
2605+
26022606
switch (mime->mm_type) {
26032607
case MAILMIME_SINGLE:
2604-
client_debug(3, "Single part");
2608+
MIME_DEBUG(3, "Single part");
26052609
break;
26062610
case MAILMIME_MULTIPLE:
2607-
client_debug(3, "Multipart");
2611+
MIME_DEBUG(3, "Multipart");
26082612
break;
26092613
case MAILMIME_MESSAGE:
2610-
client_debug(3, "Message");
2614+
MIME_DEBUG(3, "Message");
26112615
break;
26122616
}
2613-
#endif
26142617

26152618
fields = mime->mm_mime_fields;
26162619

@@ -2628,62 +2631,48 @@ static int fetch_mime_recurse(struct message_data *mdata, struct mailmime *mime,
26282631
parameters = content_type->ct_parameters;
26292632
switch (content_type->ct_type->tp_type) {
26302633
case MAILMIME_TYPE_DISCRETE_TYPE:
2631-
#ifdef DEBUG_MODE
26322634
switch (content_type->ct_type->tp_data.tp_discrete_type->dt_type) {
26332635
case MAILMIME_DISCRETE_TYPE_TEXT:
2634-
client_debug(7, "[%d] text/%s", level, content_type->ct_subtype);
2636+
MIME_DEBUG(7, "[%d] text/%s", level, content_type->ct_subtype);
2637+
is_text = 1;
2638+
if (!strcasecmp(content_type->ct_subtype, "plain")) {
2639+
text_plain = 1;
2640+
} else if (!strcasecmp(content_type->ct_subtype, "html")) {
2641+
text_html = 1;
2642+
}
26352643
break;
26362644
case MAILMIME_DISCRETE_TYPE_IMAGE:
2637-
client_debug(7, "[%d] image/%s", level, content_type->ct_subtype);
2645+
MIME_DEBUG(7, "[%d] image/%s", level, content_type->ct_subtype);
26382646
break;
26392647
case MAILMIME_DISCRETE_TYPE_AUDIO:
2640-
client_debug(7, "[%d] audio/%s", level, content_type->ct_subtype);
2648+
MIME_DEBUG(7, "[%d] audio/%s", level, content_type->ct_subtype);
26412649
break;
26422650
case MAILMIME_DISCRETE_TYPE_VIDEO:
2643-
client_debug(7, "[%d] video/%s", level, content_type->ct_subtype);
2651+
MIME_DEBUG(7, "[%d] video/%s", level, content_type->ct_subtype);
26442652
break;
26452653
case MAILMIME_DISCRETE_TYPE_APPLICATION:
2646-
client_debug(7, "[%d] application/%s", level, content_type->ct_subtype);
2654+
MIME_DEBUG(7, "[%d] application/%s", level, content_type->ct_subtype);
26472655
break;
26482656
case MAILMIME_DISCRETE_TYPE_EXTENSION:
2649-
client_debug(7, "[%d] %s/%s", level, content_type->ct_type->tp_data.tp_discrete_type->dt_extension, content_type->ct_subtype);
2650-
break;
2651-
}
2652-
#endif
2653-
switch (content_type->ct_type->tp_data.tp_discrete_type->dt_type) {
2654-
case MAILMIME_DISCRETE_TYPE_TEXT:
2655-
if (!strcasecmp(content_type->ct_subtype, "plain")) {
2656-
text_plain = 1;
2657-
} else if (!strcasecmp(content_type->ct_subtype, "html")) {
2658-
text_html = 1;
2659-
}
2660-
break;
2661-
case MAILMIME_DISCRETE_TYPE_APPLICATION:
2662-
if (!strcmp(content_type->ct_subtype, "octet-stream")) {
2663-
is_attachment = 1;
2664-
}
2665-
break;
2666-
default:
2657+
MIME_DEBUG(7, "[%d] %s/%s", level, content_type->ct_type->tp_data.tp_discrete_type->dt_extension, content_type->ct_subtype);
26672658
break;
26682659
}
26692660
break;
26702661
case MAILMIME_TYPE_COMPOSITE_TYPE:
2671-
#ifdef DEBUG_MODE
26722662
switch (content_type->ct_type->tp_data.tp_composite_type->ct_type) {
26732663
case MAILMIME_COMPOSITE_TYPE_MESSAGE:
2674-
client_debug(7, "[%d] message/%s", level, content_type->ct_subtype);
2664+
MIME_DEBUG(7, "[%d] message/%s", level, content_type->ct_subtype);
26752665
break;
26762666
case MAILMIME_COMPOSITE_TYPE_MULTIPART:
2677-
client_debug(7, "[%d] multipart/%s", level, content_type->ct_subtype);
2667+
MIME_DEBUG(7, "[%d] multipart/%s", level, content_type->ct_subtype);
26782668
if (!strcasecmp(content_type->ct_subtype, "alternative")) {
26792669
text_html = 1;
26802670
}
26812671
break;
26822672
case MAILMIME_COMPOSITE_TYPE_EXTENSION:
2683-
client_debug(7, "[%d] %s/%s", level, content_type->ct_type->tp_data.tp_composite_type->ct_token, content_type->ct_subtype);
2673+
MIME_DEBUG(7, "[%d] %s/%s", level, content_type->ct_type->tp_data.tp_composite_type->ct_token, content_type->ct_subtype);
26842674
break;
26852675
}
2686-
#endif
26872676
switch (content_type->ct_type->tp_data.tp_composite_type->ct_type) {
26882677
case MAILMIME_COMPOSITE_TYPE_MULTIPART:
26892678
if (!strcasecmp(content_type->ct_subtype, "alternative")) {
@@ -2695,27 +2684,103 @@ static int fetch_mime_recurse(struct message_data *mdata, struct mailmime *mime,
26952684
}
26962685
}
26972686

2687+
/* Attachment parsing code from LBBS mod_webmail.c: */
2688+
2689+
/* Iterate parameters of Content-Type header */
26982690
for (cur = clist_begin(parameters); cur; cur = clist_next(cur)) {
26992691
struct mailmime_parameter *param = clist_content(cur);
2700-
client_debug(7, ";%s=%s", param->pa_name, param->pa_value);
2701-
if (text_plain && !strcmp(param->pa_name, "format")) {
2702-
if (!strcmp(param->pa_value, "flowed")) {
2703-
mdata->pt_flowed = 1;
2692+
MIME_DEBUG(7, ";%s=%s\n", param->pa_name, param->pa_value);
2693+
if (text_plain && !strcmp(param->pa_name, "format") && !strcmp(param->pa_value, "flowed")) {
2694+
mdata->pt_flowed = 1;
2695+
}
2696+
}
2697+
2698+
/* If its content-type is multipart, it's not an attachment.
2699+
* If its content-type is text, then you have to look at its content-disposition, which may be either inline or attachment.
2700+
* If it has another content-type, then it is an attachment." */
2701+
if (!is_multipart) { /* Multipart can't be an attachment */
2702+
for (cur = clist_begin(fields->fld_list); cur; cur = clist_next(cur)) {
2703+
clistiter *cur2;
2704+
const char *name = NULL;
2705+
size_t size = 0;
2706+
struct mailmime_disposition *disposition;
2707+
struct mailmime_disposition_type *dsp_type;
2708+
struct mailmime_field *field = clist_content(cur);
2709+
if (field->fld_type != MAILMIME_FIELD_DISPOSITION) {
2710+
continue; /* Only care about Content-Disposition header */
2711+
}
2712+
disposition = field->fld_data.fld_disposition;
2713+
dsp_type = disposition->dsp_type;
2714+
if (dsp_type->dsp_type != MAILMIME_DISPOSITION_TYPE_ATTACHMENT && is_text) {
2715+
/* Mozilla clients change the attachment to inline when deleted (but not when detached!)
2716+
* But it's really an attachment, so treat it as such. */
2717+
if (strcmp(content_type->ct_subtype, "x-moz-deleted")) {
2718+
continue; /* If Content-Type is text and disposition is not attachment, it's inline (not an attachment) */
2719+
}
27042720
}
2705-
} else if (!strcmp(param->pa_name, "name")) {
27062721
is_attachment = 1;
2722+
/* Extract info about the attachment, e.g. filename, size, etc. */
2723+
for (cur2 = clist_begin(disposition->dsp_parms); cur2; cur2 = clist_next(cur2)) {
2724+
struct mailmime_disposition_parm *param = clist_content(cur2);
2725+
if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
2726+
name = param->pa_data.pa_filename;
2727+
} else if (param->pa_type == MAILMIME_DISPOSITION_PARM_SIZE) {
2728+
size = param->pa_data.pa_size;
2729+
}
2730+
}
27072731
/* If it's an attachment, add the name (and size) to the list */
2708-
if (1) {
2709-
const char *body2;
2710-
struct attachment *attachment = calloc(1, sizeof(struct attachment) + strlen(param->pa_value) + 1);
2711-
if (attachment) {
2712-
strcpy(attachment->data, param->pa_value); /* Safe */
2713-
attachment->name = attachment->data;
2714-
if (mime->mm_type == MAILMIME_SINGLE) {
2715-
/* Get the size of the attachment by reusing fetch_mime_recurse_single for that purpose. */
2716-
fetch_mime_recurse_single(&body2, &attachment->size, mime->mm_data.mm_single);
2732+
if (name) {
2733+
int attachment_detached = 0, attachment_deleted = 0;
2734+
is_attachment = 1;
2735+
if (!size && mime->mm_type == MAILMIME_SINGLE) {
2736+
const char *bodytmp; /* Don't care */
2737+
/* Get the size of the attachment by reusing fetch_mime_recurse_single for that purpose. */
2738+
fetch_mime_recurse_single(&bodytmp, &size, mime->mm_data.mm_single);
2739+
}
2740+
if (size && size < 1000) { /* Attachment is small enough it may have been detached... */
2741+
/* It is possible that the user has detached or deleted the attachment using a Mozilla client,
2742+
* and this attachment is now a stub/placeholder for what used to exist.
2743+
* When this happens, these headers will be siblings to Content-Disposition:
2744+
*
2745+
* X-Mozilla-Altered: [AttachmentDetached|AttachmentDeleted]; date=<detach/delete-date>
2746+
* X-Mozilla-External-Attachment-URL: <detach-location> (detached messages only) */
2747+
const char *altered, *eoh;
2748+
size_t searchlen;
2749+
#define EOH "\r\n\r\n"
2750+
#define DETACH_ALTERED_HDR "X-Mozilla-Altered:"
2751+
#define DETACH_NEWLOC_HDR "X-Mozilla-External-Attachment-URL:"
2752+
eoh = memmem(mime->mm_mime_start, mime->mm_length, EOH, STRLEN(EOH));
2753+
if (eoh) {
2754+
searchlen = (size_t) (eoh - mime->mm_mime_start);
2755+
} else {
2756+
searchlen = mime->mm_length;
2757+
eoh = mime->mm_mime_start + searchlen;
2758+
}
2759+
altered = memmem(mime->mm_mime_start, searchlen, DETACH_ALTERED_HDR, STRLEN(DETACH_ALTERED_HDR));
2760+
/* We don't duplicate the headers here; however, that means we need to always check if we're in bounds. */
2761+
if (altered && (altered + STRLEN("X-Mozilla-Altered: AttachmentDetached; date=\"") < eoh) && !strncmp(altered, "X-Mozilla-Altered: Attachment", STRLEN("X-Mozilla-Altered: Attachment"))) {
2762+
altered += STRLEN("X-Mozilla-Altered: Attachment");
2763+
if (!strncmp(altered, "Detached", STRLEN("Detached"))) {
2764+
attachment_detached = 1;
2765+
} else if (!strncmp(altered, "Deleted", STRLEN("Deleted"))) {
2766+
attachment_deleted = 1;
2767+
} else {
2768+
/* It's safe to print at least the number of characters we previously expected. */
2769+
/* Can't use client_warning since we don't have client in this function */
2770+
client_debug(1, "BUG: Unexpected value for X-Mozilla-Altered: Attachment%.*s...", (int) STRLEN("Detached"), altered);
2771+
}
2772+
}
2773+
}
2774+
if (1) {
2775+
struct attachment *attachment = calloc(1, sizeof(struct attachment) + strlen(name) + 1);
2776+
if (attachment) {
2777+
strcpy(attachment->data, name); /* Safe */
2778+
attachment->name = attachment->data;
2779+
attachment->deleted = attachment_deleted;
2780+
attachment->detached = attachment_detached;
2781+
attachment->size = size;
2782+
link_attachment(mdata, attachment);
27172783
}
2718-
link_attachment(mdata, attachment);
27192784
}
27202785
}
27212786
}

viewer.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,8 @@ int view_message(struct client *client, struct pollfd *pfds, struct message *msg
678678
char sizebuf[8];
679679
wattron(pad, COLOR_PAIR(5));
680680
format_size(attachment->size, sizebuf, sizeof(sizebuf));
681-
atlen = (size_t) snprintf(atbuf, sizeof(atbuf), " [%d] %6s %s\n", atnum + 1, sizebuf, attachment->name);
681+
atlen = (size_t) snprintf(atbuf, sizeof(atbuf), " [%d] %6s %s%s\n", atnum + 1, sizebuf, attachment->name,
682+
attachment->deleted ? " (deleted)" : attachment->detached ? " (detached)" : "");
682683
pad_add(pad, atbuf, atlen, 0);
683684
atnum++;
684685
} while ((attachment = attachment->next));

0 commit comments

Comments
 (0)