/* Anti-Mime 1.2 */ #include #include #include #include #define CODETABLES_MAC 1 /* DOS-437 oder Mac-Zeichensatz */ #define CODETABLES_DOSE 0 /* oder ISO-8859-1 (Win)-Zeichensatz */ #define CODETABLES_ISO 0 #define CODETABLES_PATH "" /* Zwischen die "" den Pfad eintragen */ #define END_OF_LINE_LF 1 /* 1 bei Linuxen, 0 bei DOSen */ #define STRICMP_DEFINED 0 /* 1 bei Dosen, 0 bei Linuxen */ struct charset_struct { char *name,*file; } charsets[] = #if CODETABLES_DOSE { "us-ascii", NULL, "iso-8859-1", CODETABLES_PATH "iso-8859.437", NULL, NULL }; #endif #if CODETABLES_MAC { "us-ascii", CODETABLES_PATH "us-ascii.mac", "iso-8859-1", CODETABLES_PATH "iso-8859.mac", NULL, NULL }; #endif #if CODETABLES_ISO { "us-ascii", CODETABLES_PATH "us-ascii.iso", "iso-8859-1", NULL, NULL, NULL }; #endif #define MAX_CHARSET_STRING_LEN 100 #define MAX_LINE_SIZE 20000 #define MAX_TAG_SIZE 60 #define MAX_QUOTE_NEST 64 static char *mime_tags[] = { "excerpt", "bold", "underline", "italic", "param", "nofill", "x-author", NULL }; enum mime_tags_special { mime_excerpt,mime_bold,mime_underline,mime_italic,mime_param,mime_nofill,mime_author }; #define FLAGS_BOLD 1 #define FLAGS_UNDERLINE 2 #define FLAGS_ITALIC 4 #define mytoupper(x) ((islower(x)) ? toupper(x) : (x)) #define mytolower(x) ((isupper(x)) ? tolower(x) : (x)) #if !STRICMP_DEFINED int stricmp(const char *s1,const char *s2) { for(; mytoupper(*s1) == mytoupper(*s2) && *s1; s1++,s2++); if (mytoupper(*s1) < mytoupper(*s2)) return(-1); if (mytoupper(*s1) > mytoupper(*s2)) return(+1); return(0); } #endif #if END_OF_LINE_LF char *myfgets(char *s,int n,FILE *f) { char *p,*q; int l; p = fgets(s,n,f); if (p == NULL) return(NULL); l = strlen(p); if (l < 2) return(p); q = p+(l-2); if (*q == '\r') *q++ = '\n',*q = 0; return(p); } int myfputs(char *s,FILE *f) { char *p; int l; l = strlen(s); if (l > 1 && *(p = s+l-1) == '\n') *p++ = '\r',*p++ = '\n',*p++ = 0; return(fputs(s,f)); } #else # define myfgets(s,n,f) fgets(s,n,f) # define myfputs(s,f) fputs(s,f) #endif /* quotecheck: prueft auf vorhandenes Quotezeichen */ int quote_check(char *line) { char *p,chr; while(*line == '<') { p = strchr(line,'>'); if (p == NULL) return(0); line = p+1; } if (line[0] == ':' && (line[1] == ' ' || line[1] == '\t' || line[1] == 0)) return(1); if (*line == ' ') line++; p = strchr(line,'>'); if (p == NULL || p-line > 5) return(0); for(p = line; *p != '>'; p++) { chr = mytoupper(*p); if (chr < 'A' || chr > 'Z') return(0); } return(1); } static int setflags,actflags; int update_flags(char *out,char **t,char **p) { if (setflags != actflags && **p != '\n') { int delta = setflags^actflags,move; move = (*t > out && *(*t-1) == ' '); if (move) (*t)--; if (delta & FLAGS_BOLD && actflags & FLAGS_BOLD) *(*t)++ = '*'; if (delta & FLAGS_UNDERLINE && actflags & FLAGS_UNDERLINE) *(*t)++ = '_'; if (delta & FLAGS_ITALIC && actflags & FLAGS_ITALIC) *(*t)++ = '/'; if (move) *(*t)++ = ' '; move = (**p == ' ' || **p == '\t'); if (move) *(*t)++ = *(*p)++; if (delta & FLAGS_ITALIC && setflags & FLAGS_ITALIC) *(*t)++ = '/'; if (delta & FLAGS_UNDERLINE && setflags & FLAGS_UNDERLINE) *(*t)++ = '_'; if (delta & FLAGS_BOLD && setflags & FLAGS_BOLD) *(*t)++ = '*'; actflags = setflags; if (move) return(1); } return(0); } /* convert_line: Konvertiert eine Zeile im Mime-Format */ char *convert_line(char *out,char *t,char *line,char *table,int newmail) { char *p = line,*q,*s = t,**mime_tag; int c,onoff; static int excerpt,inparam,quoted,nofill,x_author,x_ap; static char author[MAX_QUOTE_NEST+1][5]; if (newmail) { excerpt = inparam = nofill = x_author = setflags = actflags = 0; strcpy(author[0],"> "); } if (t == out) quoted = 0; if (!quoted) quoted = quote_check(line); for(;;) switch(*p) { case 0 : if (*(t-1) != '\n') *t++ = '\n'; *t = 0; return((nofill) ? s : (t-1)); case '<' : if (p[1] == '<') { *t++ = '<'; p += 2; break; } q = strchr(p,'>'); if (q != NULL) { c = q-p; if (c < MAX_TAG_SIZE) { *q = 0; onoff = p[1] != '/'; for(mime_tag = mime_tags; *mime_tag != NULL; mime_tag++) if (!stricmp(p+(2-onoff),*mime_tag)) break; *q = '>'; p = q+1; if (*mime_tag != NULL) switch(mime_tag-mime_tags) { case mime_excerpt : if (onoff) strcpy(author[++excerpt],"> "); else excerpt--; if (excerpt < 0) excerpt = 0; if (excerpt > MAX_QUOTE_NEST) excerpt = MAX_QUOTE_NEST; break; case mime_bold : if (onoff) setflags |= FLAGS_BOLD; else setflags &= ~FLAGS_BOLD; break; case mime_underline : if (onoff) setflags |= FLAGS_UNDERLINE; else setflags &= ~FLAGS_UNDERLINE; break; case mime_italic : if (onoff) setflags |= FLAGS_ITALIC; else setflags &= ~FLAGS_ITALIC; break; case mime_param : if (onoff) inparam++; else inparam--; if (inparam < x_author) inparam = x_author; break; case mime_nofill : if (onoff) nofill++; else nofill--; if (nofill < 0) nofill = 0; break; case mime_author : if (onoff) ((x_author++) ? 0 : (x_ap = 0)),inparam++; else (x_author) ? (x_author--,inparam--) : 0; if (!x_author) { x_ap = (x_ap+1)/2; author[excerpt][x_ap++] = '>'; author[excerpt][x_ap++] = ' '; author[excerpt][x_ap++] = 0; } break; } break; } } default : if (inparam) { if (inparam == x_author && x_ap < 3) { if (*p == ' ' || *p == '\t' || *p == '\n') { if (x_ap & 1) x_ap++; } else if (!(x_ap & 1)) author[excerpt][x_ap++ >> 1] = mytoupper(*p); } p++; } else { if (excerpt && !quoted && *p != ' ' && *p != '\t' && *p != '\n') { x_ap = strlen(author[excerpt-1]); memmove(out+x_ap,out,t-out+1); memcpy(out,author[excerpt-1],x_ap); quoted = 1; t += x_ap; } if (s == t && *p != ' ' && *p != '\t' && *p != '\n' && t > out && *(t-1) != ' ' && *(t-1) != '\t') *t++ = ' '; if (setflags != actflags) if (update_flags(out,&t,&p)) continue; *t++ = table[(unsigned char)*p++]; } break; } } /* detect_charset: Durchsucht die M-Zeile nach Zeichensatzangabe */ int detect_charset(char *line) { char *p,*q,charset_string[MAX_CHARSET_STRING_LEN+1]; struct charset_struct *charset_ptr; int c; p = strstr(line,"charset="); if (p == NULL) charset_string[0] = 0; else { p += sizeof("charset=")-1; while(*p == ' ' || *p == '\t') p++; for(q = p; !(*q == ' ' || *q == '\n' || *q == ';' || *q == '\t' || *q == 0); q++); c = q-p; if (c > MAX_CHARSET_STRING_LEN) c = MAX_CHARSET_STRING_LEN; strncpy(charset_string,p,c); charset_string[c] = 0; } for(charset_ptr = charsets; charset_ptr->name != NULL; charset_ptr++) if (!stricmp(charset_string,charset_ptr->name)) break; return(charset_ptr-charsets); } /* detect_enriched: Dursucht die M-Zeile nach text/enriched */ int detect_enriched(char *line) { char *p,*q; int c; p = strstr(line,"content-type:"); if (p == NULL) return(0); p += sizeof("content-type:")-1; while(*p == ' ' || *p == '\t') p++; for(q = p; !(*q == ' ' || *q == '\n' || *q == ';' || *q == '\t' || *q == 0); q++); c = q-p; if (c != (sizeof("text/enriched")-1)) return(0); return(!strncmp(p,"text/enriched",sizeof("text/enriched")-1)); } /* detect_qpe: Dursucht den Mime-Anhang nach quoted-printable */ int detect_qpe(char *line) { char *p,*q; int c; p = strstr(line,"content-transfer-encoding:"); if (p == NULL) return(0); p += sizeof("content-transfer-encoding:")-1; while(*p == ' ' || *p == '\t') p++; for(q = p; !(*q == ' ' || *q == '\n' || *q == ';' || *q == '\t' || *q == 0); q++); c = q-p; if (c != (sizeof("quoted-printable")-1)) return(0); return(!strncmp(p,"quoted-printable",sizeof("quoted-printable")-1)); } /* Konvertiert quoted-printable nach 8bit */ void convert_qpe(char *line) { char *p,*q; q = line+strlen(line); for(p = line+1; p < q; p++) { if (*p == '=') if ((*(p+1) >= 'A' && *(p+1) <= 'F' || *(p+1) >= '0' && *(p+1) <= '9') && (*(p+2) >= 'A' && *(p+2) <= 'F' || *(p+2) >= '0' && *(p+2) <= '9')) { *p = ((*(p+1) >= 'A') ? (*(p+1)-'A'+10) : (*(p+1)-'0')) << 4; *p |= (*(p+2) >= 'A') ? (*(p+2)-'A'+10) : (*(p+2)-'0'); q -= 2; memmove(p+1,p+3,q-p); } } } /* Konvertiert Mime-Header-Zeilen mit qpe nach 8bit */ void convert_mime_header(char *line) { char *p,*q,*r,*s,charset_string[MAX_CHARSET_STRING_LEN+1],char_table[256]; FILE *table_file; struct charset_struct *charset_ptr; int c; for(p = line+1; *p; p++) if (*p == '=' && *(p+1) == '?') { c = 0; for(q = p+2; *q; q++) if (*q == '?') if (++c == 3) { if (*++q == '=') { q--; s = strchr(p+2,'?'); if (s == NULL) break; r = strchr(s+1,'?'); if (r == NULL) break; c = s-(p+2); if (c > MAX_CHARSET_STRING_LEN) c = MAX_CHARSET_STRING_LEN; strncpy(charset_string,p+2,c); charset_string[c] = 0; for(charset_ptr = charsets; charset_ptr->name != NULL; charset_ptr++) if (!stricmp(charset_string,charset_ptr->name)) break; c = charset_ptr-charsets; if (charsets[c].file != NULL) if ((table_file = fopen(charsets[c].file,"rb")) != NULL) { if (fread(char_table,1,256,table_file) != 256) { fprintf(stderr,"Warning: Table file %s corrupt!\n",charsets[c].file); c = -1; } fclose(table_file); } else { fprintf(stderr,"Warning: Table file %s does not exist!\n",charsets[c].file); c = -1; } if (c < 0 || charsets[c].file == NULL) for(c = c; c < 256; c++) char_table[c] = c; for(r++; r < q; r++) { if (*r == '=') { if ((*(r+1) >= 'A' && *(r+1) <= 'F' || *(r+1) >= '0' && *(r+1) <= '9') && (*(r+2) >= 'A' && *(r+2) <= 'F' || *(r+2) >= '0' && *(r+2) <= '9')) { c = ((*(r+1) >= 'A') ? (*(r+1)-'A'+10) : (*(r+1)-'0')) << 4; *p++ = char_table[(unsigned char)(c | ((*(r+2) >= 'A') ? (*(r+2)-'A'+10) : (*(r+2)-'0')))]; r += 2; } } else if (*r == '_') *p++ = ' '; else *p++ = char_table[(unsigned char)*r]; } memmove(p,q+2,strlen(q)-1); } break; } } } /* Hauptroutine zum Konvertieren eines Outfiles */ int process(FILE *in_file,FILE *out_file) { char *buf,*output,char_table[256],*t,*s,*p; long lines = 0,mails = 0,mimemails = 0; int mime = 0,enriched = 0,qpe = 0,charset,newmail = 0,firstline = 0; int a; FILE *table_file; buf = malloc(MAX_LINE_SIZE+2); output = malloc(MAX_LINE_SIZE+1); output[0] = ':',output[1] = 0; t = output+1; if (buf == NULL) { fprintf(stderr,"Error: Not enough memory!\n"); return(4); } for(; myfgets(buf,MAX_LINE_SIZE,in_file) != NULL;) { lines++; switch(buf[0]) { case '#' : newmail = 1; firstline = 1; if (mails) { putchar((mime) ? '*' : '.'); fflush(stdout); if (mime) mimemails++; } if (buf[1] != '\n' && buf[1] != 0) mails++; mime = enriched = qpe = 0; break; case 'N' : case 'W' : convert_mime_header(buf); break; case 'M' : mime = 1; for(p = buf; *p; p++) *p = mytolower(*p); charset = detect_charset(buf); enriched = detect_enriched(buf); if (charsets[charset].file != NULL) if ((table_file = fopen(charsets[charset].file,"rb")) != NULL) { if (fread(char_table,1,256,table_file) != 256) { fprintf(stderr,"Warning: Table file %s corrupt!\n",charsets[charset].file); charset = -1; } fclose(table_file); } else { fprintf(stderr,"Warning: Table file %s does not exist!\n",charsets[charset].file); charset = -1; } if (charset < 0 || charsets[charset].file == NULL) for(a = 0; a < 256; a++) char_table[a] = a; continue; case ':' : if (mime) { if (firstline) { if (buf[1] == '-') firstline = 0; else { for(p = buf; *p; p++) *p = mytolower(*p); qpe |= detect_qpe(buf); } continue; } if (qpe) convert_qpe(buf); if (enriched) { if (*(buf+(strlen(buf)-1)) != '\n') strcat(buf,"\n"); s = convert_line(output+1,t,buf+1,char_table,newmail); if (s == t && buf[1] == '\n') { if (actflags) { int tmpflags = setflags; setflags = 0; *s = 0; update_flags(output+1,&s,&s); *s++ = '\n',*s = 0; setflags = tmpflags; } if (myfputs(output,out_file) == EOF) { fprintf(stderr,"Error: Could not write into file! (line %ld)\n",lines); free(buf); free(output); return(11); } t = output+1; } else t = s; newmail = 0; continue; } else for(p = buf; *p; p++) *p = char_table[(unsigned char)*p]; } break; } if (actflags) { int tmpflags = setflags; setflags = 0; *s = 0; update_flags(output+1,&s,&s); *s++ = '\n',*s = 0; setflags = tmpflags; } if (t != (output+1) && myfputs(output,out_file) == EOF) { fprintf(stderr,"Error: Could not write into file! (line %ld)\n",lines); free(buf); free(output); return(11); } t = output+1; if (myfputs(buf,out_file) == EOF) { fprintf(stderr,"Error: Could not write into file! (line %ld)\n",lines); free(buf); free(output); return(10); } } if (mime) { mimemails++; if (t != (output+1) && myfputs(output,out_file) == EOF) { fprintf(stderr,"Error: Could not write into file! (line %ld)\n",lines); free(buf); free(output); return(11); } } if (fflush(out_file) == EOF) { fprintf(stderr,"Error: Could not write into file! (line %ld)\n",lines); free(buf); free(output); return(10); } printf("\n\n%ld lines, %ld mails processed. %ld (%d%%) were MIME mails.\n",lines,mails,mimemails,(mails > 0) ? (int)((mimemails*100L+mails/2)/mails) : 0); free(buf); free(output); return(0); } int main(int argc,char **argv) { FILE *in_file,*out_file; int result; printf("\nAnti-Mime ;-) 1.2\n-----------------------\n\n"); if (argc != 3) { fprintf(stderr,"Error: 2 parameters needed (input file, output file)!\n"); return(1); } in_file = fopen(argv[1],"r"); if (in_file == NULL) { fprintf(stderr,"Error: Could not open input file \"%s\"!\n",argv[1]); return(2); } out_file = fopen(argv[2],"w"); if (out_file == NULL) { fclose(in_file); fprintf(stderr,"Error: Could not create output file \"%s\"!\n",argv[2]); return(3); } printf("Processing"); fflush(stdout); result = process(in_file,out_file); printf("\n"); fclose(in_file); fclose(out_file); if (result) unlink(argv[2]); return(result); }