This is a incorperates all this patches into a single patch. encoded_newline.patch fallback_charset.patch minimal_multipart.patch rot13_umlaut.patch us-ascii_override.patch Take this patch if you want all the patches. Thomas Wiegner 29.Sep.2007 ===----------------------=== Index: configure.in =================================================================== --- configure.in (revision 2) +++ configure.in (revision 8) @@ -7,7 +7,7 @@ AM_MAINTAINER_MODE dnl remember to update src/VMSMAKE.COM and src/version.h ! -AM_INIT_AUTOMAKE(slrn, 0.9.8.1pl1) +AM_INIT_AUTOMAKE(slrn, 0.9.8.1pl2.1) AC_SUBST(PACKAGE) AC_SUBST(VERSION) Index: src/util.c =================================================================== --- src/util.c (revision 2) +++ src/util.c (revision 8) @@ -153,19 +153,18 @@ { register unsigned char cha, chb, *bmax; + if (a == NULL) + { + if (b == NULL) + return 0; + else + return -1; + } + if (b == NULL) + return 1; #if SLANG_VERSION >= 20000 if (Slrn_UTF8_Mode) { - if (a == NULL) - { - if (b == NULL) - return 0; - else - return -1; - } - if (b == NULL) - return 1; - return SLutf8_compare(a, a+strlen(a), b, b+strlen(b), n , 0); } #endif @@ -191,19 +190,19 @@ { register unsigned char cha, chb; int len_a,len_b,min; - + + if (a == NULL) + { + if (b == NULL) + return 0; + else + return -1; + } + if (b == NULL) + return 1; #if SLANG_VERSION >= 20000 if (Slrn_UTF8_Mode) { - if (a == NULL) - { - if (b == NULL) - return 0; - else - return -1; - } - if (b == NULL) - return 1; len_a=strlen(a); len_b=strlen(b); if (len_a > len_b) Index: src/charset.c =================================================================== --- src/charset.c (revision 2) +++ src/charset.c (revision 8) @@ -77,6 +77,21 @@ } #ifdef HAVE_ICONV_H +iconv_t slrn_iconv_open(const char *tocode, const char *fromcode) +{ + iconv_t cd; + + if (((cd = iconv_open(tocode, fromcode)) == (iconv_t)(-1)) && (Slrn_Fallback_Input_Charset != NULL) ) + { + /* try fallback charset if it didn't work the first time*/ + cd = iconv_open(tocode, Slrn_Fallback_Input_Charset); + } + return cd; +} +#endif + + +#ifdef HAVE_ICONV_H /* returns the converted string, or NULL on error or if no convertion is needed*/ static char *iconv_convert_string(iconv_t cd, char *in_str, int offset, size_t len, int test, int *error) { @@ -173,11 +188,11 @@ iconv_t cd; char *tmp; - if ((cd = iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) - { - slrn_error (_("Can't convert %s -> %s\n"), from_charset, to_charset); - return NULL; - } + if ((cd = slrn_iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) + { + slrn_error (_("Can't convert %s -> %s\n"), from_charset, Slrn_Fallback_Input_Charset); + return NULL; + } tmp = iconv_convert_string(cd, str, offset, len, test, NULL); iconv_close(cd); return tmp; @@ -247,7 +262,7 @@ char *tmp; struct Slrn_Article_Line_Type *line=a->lines; - if ((cd = iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) + if ((cd = slrn_iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) { slrn_error (_("Can't convert %s -> %s\n"), from_charset, to_charset); return -1; @@ -283,7 +298,7 @@ iconv_t cd; - if ((cd = iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) + if ((cd = slrn_iconv_open(to_charset, from_charset)) == (iconv_t)(-1)) { slrn_error (_("Can't convert %s -> %s\n"), from_charset, to_charset); return -1; Index: src/version.c =================================================================== --- src/version.c (revision 2) +++ src/version.c (revision 8) @@ -80,6 +80,16 @@ {NULL, 0} }; +static char *included_patches[] = +{ + "minimal_multipart", + "encoded_newline", + "rot13_umlaut", + "usascii_override", + "fallback_charset", + "" +}; + static void show_compile_time_options (void) { Compile_Option_Type *opt; @@ -171,6 +181,17 @@ fprintf (stdout, _(" Default posting mechanism: %s\n"), slrn_map_object_id_to_name (1, SLRN_DEFAULT_POST_OBJ)); + if(*included_patches[0] != '\0') + { + int i=0; + fprintf (stdout, _(" PATCHES:\n")); + while(*(included_patches[i]) != '\0') + { + fprintf (stdout, _(" %s\n"),included_patches[i]); + i++; + } + } + /* #if SLRN_HAS_CHARACTER_MAP slrn_chmap_show_supported (); Index: src/mime.c =================================================================== --- src/mime.c (revision 2) +++ src/mime.c (revision 8) @@ -52,6 +52,9 @@ int Slrn_Use_Meta_Mail = 1; int Slrn_Fold_Headers = 1; char *Slrn_MetaMail_Cmd; +char *Slrn_Fallback_Input_Charset = NULL; +int Slrn_Usascii_Override = 0; +int Slrn_Minimal_Multipart =1; #ifndef SLRNPULL_CODE #define CONTENT_TYPE_TEXT 0x01 @@ -125,19 +128,68 @@ } else if (0 == slrn_case_strncmp ((unsigned char *)b, (unsigned char *) "message/", - 5)) + 8)) { a->mime.content_type = CONTENT_TYPE_MESSAGE; a->mime.content_subtype = CONTENT_SUBTYPE_UNKNOWN; b += 8; } else if (0 == slrn_case_strncmp ((unsigned char *)b, + (unsigned char *) "application/", + 12)) + { + b += 12; + if (0 != slrn_case_strncmp ((unsigned char *)b, + (unsigned char *) "pgp-signature", + 13)) + { + a->mime.content_type = CONTENT_TYPE_UNSUPPORTED; + a->mime.content_subtype = CONTENT_SUBTYPE_UNSUPPORTED; + return -1; + } + a->mime.content_type = CONTENT_TYPE_TEXT; + a->mime.content_subtype = CONTENT_SUBTYPE_PLAIN; + } + else if (0 == slrn_case_strncmp ((unsigned char *)b, (unsigned char *) "multipart/", 5)) { a->mime.content_type = CONTENT_TYPE_MULTIPART; a->mime.content_subtype = CONTENT_SUBTYPE_UNKNOWN; b += 10; + while (NULL != (b = slrn_strchr (b, ';'))) + { + char *boundary; + unsigned int len; + + b = slrn_skip_whitespace (b + 1); + + if (0 != slrn_case_strncmp ((unsigned char *)b, + (unsigned char *)"boundary", + 8)) + continue; + + b = slrn_skip_whitespace (b + 8); + + if (*b != '=') continue; + b++; + if (*b == '"') b++; + boundary = b; + while (*b && (*b != ';') + && (*b != ' ') && (*b != '\t') && (*b != '\n') + && (*b != '"')) + b++; + len = b - boundary; + + /* add a "--" at the start of boundary */ + a->mime.boundary = slrn_safe_malloc(len+5); + a->mime.boundary[0]='-'; + a->mime.boundary[1]='-'; + slrn_strncpy(a->mime.boundary+2, boundary, len+1); + a->mime.boundary[len+2]='-'; + a->mime.boundary[len+3]='-'; + return 0; + } } else { @@ -179,7 +231,7 @@ && (*b != '"')) b++; len = b - charset; - + a->mime.charset = slrn_safe_strnmalloc (charset, len); return 0; } @@ -230,6 +282,37 @@ /*}}}*/ #endif /* NOT SLRNPULL_CODE*/ +/* call this with option keep_nl_tab = 0 when handling headers */ +static char *cp_decoded_char(char *dest, char ch, int keep_nl_tab) +{ + if(keep_nl_tab) + { + *dest++=ch; + } + else + { + /* Throw away newlines and carriage returns. + * Change tabs to spaces. + * only broken readers place them in headers and + * slrn does not like that. */ + switch(ch) + { + case '\n': + case '\r': + break; + + case '\t': + *dest++ = ' '; + break; + + default: + *dest++ = ch; + break; + } + } + return dest; +} + static int Index_Hex[128] =/*{{{*/ { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -248,10 +331,10 @@ static char *decode_quoted_printable (char *dest,/*{{{*/ char *src, char *srcmax, int treat_underscore_as_space, - int strip_8bit) + int keep_nl_tab) { char *allowed_in_qp = "0123456789ABCDEFabcdef"; - unsigned char ch, mask = 0x0; + unsigned char ch; /* #ifndef SLRNPULL_CODE if (strip_8bit && (NULL == Char_Set)) @@ -267,8 +350,8 @@ && (NULL != slrn_strchr (allowed_in_qp, src[1]))) { ch = (16 * HEX(src[0])) + HEX(src[1]); - if (ch & mask) ch = '?'; - *dest++ = (char) ch; + /* remove nl and tab from line if wanted */ + dest=cp_decoded_char(dest, ch, keep_nl_tab); src += 2; } else if ((ch == '_') && treat_underscore_as_space) @@ -296,17 +379,23 @@ /*}}}*/ #define BASE64(c) (Index_64[(unsigned char)(c) & 0x7F]) -static char *decode_base64 (char *dest, char *src, char *srcmax) /*{{{*/ + +static char *decode_base64 (char *dest, char *src, char *srcmax, int keep_nl_tab) /*{{{*/ { + char tmp; + while (src + 3 < srcmax) { - *dest++ = (BASE64(src[0]) << 2) | (BASE64(src[1]) >> 4); - + tmp = (BASE64(src[0]) << 2) | (BASE64(src[1]) >> 4); + dest=cp_decoded_char(dest, tmp, keep_nl_tab); + if (src[2] == '=') break; - *dest++ = ((BASE64(src[1]) & 0xf) << 4) | (BASE64(src[2]) >> 2); + tmp = ((BASE64(src[1]) & 0xf) << 4) | (BASE64(src[2]) >> 2); + dest=cp_decoded_char(dest, tmp, keep_nl_tab); if (src[3] == '=') break; - *dest++ = ((BASE64(src[2]) & 0x3) << 6) | BASE64(src[3]); + tmp = ((BASE64(src[2]) & 0x3) << 6) | BASE64(src[3]); + dest=cp_decoded_char(dest, tmp, keep_nl_tab); src += 4; } return dest; @@ -333,8 +422,22 @@ s= *s_ptr; if (slrn_string_nonascii(s)) - return -1; - + { + /* Only ascii is allowed in headers, if we find unencoded 8 bit chars + * convert them with the fallback charset. */ + if(Slrn_Fallback_Input_Charset != NULL) + { + char *c; + + c= slrn_convert_substring(s, 0, 0, Slrn_Display_Charset, Slrn_Fallback_Input_Charset, 0); + if(c != NULL) + { + slrn_free(*s_ptr); + *s_ptr=c; + } + } + return -1; + } while (1) { while ((NULL != (s = slrn_strchr (s, '='))) @@ -409,7 +512,7 @@ s2 = s1; if (method == 'B') - s1 = decode_base64 (s1, txt, s); + s1 = decode_base64 (s1, txt, s, 0); else s1 = decode_quoted_printable (s1, txt, s, 1, 0); /* Now move everything over */ @@ -530,9 +633,10 @@ } /* put decoded article into buf_dest */ - buf_pos = decode_base64(buf_dest, buf_src, buf_src+len); + buf_pos = decode_base64(buf_dest, buf_src, buf_src+len, 1); *buf_pos = '\0'; +#ifndef HAVE_ICONV_H if (a->mime.charset == NULL) { buf_pos = buf_dest; @@ -542,6 +646,7 @@ buf_pos++; } } +#endif l = body_start; body_start = body_start->prev; @@ -696,6 +801,7 @@ m->was_parsed = 0; m->needs_metamail = 0; m->charset = NULL; + m->boundary = NULL; m->content_type = 0; m->content_subtype = 0; } @@ -708,6 +814,10 @@ { slrn_free(m->charset); } + if (m->boundary != NULL) + { + slrn_free(m->boundary); + } } /*}}}*/ @@ -757,9 +867,164 @@ obj=tmp; } } +/*}}}*/ +int slrn_convert_multipart_article(Slrn_Article_Type *a, char *to_charset)/*{{{*/ +{ + struct Slrn_Article_Line_Type *line, *first_line, *line_start_part, *line_end_part, *line_tmp; + int len; + int i=0; + int j=1; + int endfound=0; + + Slrn_Mime_Type mime_bak; + + first_line =a->lines; + line =a->lines; + len = strlen(a->mime.boundary)-2; + /* search 1st boundary */ + do + { + if((line->flags & HEADER_LINE) == 0) + { + line->flags |= HIDDEN_LINE; + } + line=line->next; + } while ((line->next != NULL) && slrn_case_strncmp(line->buf, a->mime.boundary, len) != 0); + + while ((line->next != NULL) && (endfound == 0)) + { + j++; + line_start_part = line; + do + { + line->flags |= HIDDEN_LINE; + line->flags |= HEADER_LINE; + line=line->next; + + } while ((line->next != NULL) && (*(line->buf) != 0)); + + /* search boundary */ + while ((line->next != NULL) && (slrn_case_strncmp(line->buf, a->mime.boundary, len) != 0)) + { + line_end_part=line; + line=line->next; + } + if(slrn_case_strncmp(line->buf, a->mime.boundary, len+2) == 0) + { + /* we found the normal ending boundary */ + endfound=1; + } + else if(line->next==NULL) + { + /* end of article, indicating a corrupted article */ + endfound=2; + } + + /* pseudo article with only multipart */ + mime_bak = a->mime; + slrn_mime_init(&a->mime); + a->lines = line_start_part; + + /* start of pseudo article has no prev, save old prev value in line_start_part */ + line_start_part=line_start_part->prev; + a->lines->prev = NULL; + + if(endfound != 2) + { + line_end_part->next = NULL; + } + + _slrn_art_unfold_header_lines(a); + slrn_mime_process_article (a); + + line->flags |= HIDDEN_LINE; + line_tmp = a->lines; + do + { + if( line_tmp->flags & HEADER_LINE) + { + line_tmp->flags ^= HEADER_LINE; + } + else if(a->mime.content_type == CONTENT_TYPE_UNSUPPORTED) + { + /* hide not supported multiparts */ + line_tmp->flags |= HIDDEN_LINE; + } + line_end_part=line_tmp; + line_tmp=line_tmp->next; + } while (line_tmp != NULL); + + /* set back article */ + line_start_part->next=a->lines; + a->lines->prev=line_start_part; + + a->lines = first_line; + if(endfound != 2) + { + line_end_part->next = line; + line->prev = line_end_part; + } + else + { + slrn_message (_("Multipart article is corrupted.")); + } + + if(a->mime.content_type == CONTENT_TYPE_UNSUPPORTED) + slrn_message (_("Unsupported multiparts were hidden.")); + + /* free space */ + slrn_mime_free(&a->mime); + + a->mime=mime_bak; + } + return 0; +} /*}}}*/ +int split_qp_lines(Slrn_Article_Type *a)/*{{{*/ +{ + struct Slrn_Article_Line_Type *line, *new_line; + char *qp_nl, *tmp_buf; + + line=a->lines; + + /* skip header lines */ + while((line->flags & HEADER_LINE) == 0) + { + line=line->next; + } + + while(line != NULL) + { + qp_nl = slrn_strchr((unsigned char*)line->buf, 0x0A); + /* if there is a newline inside a Slrn_Article_Line_Type->buf split this line into two */ + if(qp_nl != NULL) + { + *qp_nl = 0; + + new_line=(Slrn_Article_Line_Type *) slrn_malloc(sizeof(Slrn_Article_Line_Type),1,1); + new_line->buf=slrn_safe_strmalloc(qp_nl+1); + + tmp_buf=slrn_safe_strmalloc((unsigned char*)line->buf); + slrn_free((unsigned char*)line->buf); + line->buf=tmp_buf; + + new_line->next = line->next; + new_line->prev = line; + if(line->next != NULL) + { + line->next->prev = new_line; + } + line->next = new_line; + + } + line=line->next; + } +} +/*}}}*/ + + int slrn_mime_process_article (Slrn_Article_Type *a)/*{{{*/ { if ((a == NULL) || (a->mime.was_parsed)) @@ -778,6 +1043,12 @@ a->mime.needs_metamail = 1; return 0; } + + + if ((a->mime.content_type == CONTENT_TYPE_MULTIPART) && (Slrn_Minimal_Multipart != 0) ) + { + return slrn_convert_multipart_article(a, Slrn_Display_Charset); + } switch (parse_content_transfer_encoding_line (a)) { @@ -797,20 +1068,35 @@ case ENCODED_QUOTED: decode_mime_quoted_printable (a); + split_qp_lines(a); break; default: a->mime.needs_metamail = 1; return; } - + + if( (slrn_case_strncmp((unsigned char *)"us-ascii", (unsigned char *)a->mime.charset,8) == 0) + && (Slrn_Fallback_Input_Charset != NULL) && (Slrn_Usascii_Override != 0) ) + { + slrn_free(a->mime.charset); + a->mime.charset = slrn_safe_strmalloc(Slrn_Fallback_Input_Charset); + } + if ((a->mime.needs_metamail == 0) && (a->mime.charset == NULL)) { - a->mime.charset = slrn_safe_strmalloc("us-ascii"); - return 0; + if(Slrn_Fallback_Input_Charset != NULL) + { + a->mime.charset = slrn_safe_strmalloc(Slrn_Fallback_Input_Charset); + } + else + { + a->mime.charset = slrn_safe_strmalloc("us-ascii"); + return 0; + } } - + if ((a->mime.needs_metamail == 0) && (slrn_case_strncmp((unsigned char *)"us-ascii", (unsigned char *)a->mime.charset,8) != 0) && Index: src/version.h =================================================================== --- src/version.h (revision 2) +++ src/version.h (revision 8) @@ -22,8 +22,8 @@ /* Keep this separate from autoconf's settings for now */ #define MY_VERSION "0.9.8.1" #define SLRN_VERSION_NUMBER 90802 -#define PATCH_LEVEL "2" -#define SLRN_RELEASE_DATE "2005-02-17" +#define PATCH_LEVEL "2.1" +#define SLRN_RELEASE_DATE "2007-07-17" #ifdef PATCH_LEVEL # define SLRN_VERSION (MY_VERSION "pl" PATCH_LEVEL) Index: src/mime.h =================================================================== --- src/mime.h (revision 2) +++ src/mime.h (revision 8) @@ -31,6 +31,8 @@ extern int Slrn_Fold_Headers; extern int Slrn_Use_Meta_Mail; extern char *Slrn_MetaMail_Cmd; +extern char *Slrn_Fallback_Input_Charset; +extern int Slrn_Usascii_Override; +extern int Slrn_Minimal_Multipart; - #endif /* _SLRN_MIME_H */ Index: src/startup.c =================================================================== --- src/startup.c (revision 2) +++ src/startup.c (revision 8) @@ -614,6 +614,8 @@ {"max_queued_groups", &Slrn_Max_Queued_Groups}, {"use_header_numbers", &Slrn_Use_Header_Numbers}, {"use_localtime", &Slrn_Use_Localtime}, + {"usascii_override", &Slrn_Usascii_Override}, + {"minimal_multipart", &Slrn_Minimal_Multipart}, #if SLRN_HAS_SPOILERS {"spoiler_char", &Slrn_Spoiler_Char}, {"spoiler_display_mode", &Slrn_Spoiler_Display_Mode}, @@ -742,6 +744,7 @@ #else {"charset", NULL}, #endif + {"fallback_charset", &Slrn_Fallback_Input_Charset}, #ifndef VMS {"sendmail_command", &Slrn_SendMail_Command}, #endif Index: src/art.c =================================================================== --- src/art.c (revision 2) +++ src/art.c (revision 8) @@ -8215,16 +8215,23 @@ static void write_rot13 (unsigned char *buf, int color, int use_emph) /*{{{*/ { unsigned char ch; + unsigned char *buf_rot13; - (void) color; - (void) use_emph; init_rot13(); - - while ((ch = *buf++) != 0) + buf_rot13 = slrn_safe_strmalloc (buf); + + decode_rot13(buf_rot13); + if (use_emph) { - ch = Rot13buf[ch]; - slrn_write_nbytes ((char *) &ch, 1); +#if SLRN_HAS_EMPHASIZED_TEXT + smg_write_emphasized_text (buf_rot13, color); + slrn_free(buf_rot13); + return; +#endif } + smg_write_string (buf_rot13); + slrn_free(buf_rot13); + } /*}}}*/ @@ -8906,7 +8913,7 @@ if (Do_Rot13 && use_rot13) { - write_rot13 ((unsigned char *) lbuf, use_emph_mask, color); + write_rot13 ((unsigned char *) lbuf, color, use_emph_mask); return; } Index: src/art.h =================================================================== --- src/art.h (revision 2) +++ src/art.h (revision 8) @@ -184,6 +184,7 @@ int was_modified; int was_parsed; int needs_metamail; + char *boundary; } Slrn_Mime_Type; Index: po/POTFILES.in =================================================================== --- po/POTFILES.in (revision 2) +++ po/POTFILES.in (revision 8) @@ -2,7 +2,7 @@ src/art_misc.c src/art_sort.c src/chkslang.c -src/chmap.c +src/charset.c src/clientlib.c src/decode.c src/editscore.c