@@ -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 }
0 commit comments