addrutil: Re-indent.
[wk-misc.git] / 8bit-in-header.c
1 /* 8bit-in-header.c
2  * A tool to check for non ASCII characters in RFC822 messages header.
3  *    Copyright (C) 2000  Werner Koch
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  *
19  *
20  * 2000-01-06 wk
21  * Written as an attempt to get rid of strange chinese mails
22  *
23  */
24
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #define PGMNAME "8bit-in-header"
32
33 /* bail out after more than BAD_LIMIT 8/bit chars in one logical line
34  * We allow for a few bad characters due to transmission errors or
35  * something like this when option -s is used.
36  */
37 #define BAD_LIMIT 3
38
39 #define MAX_NAMES 200
40
41 static int silent;
42 static int sloppy;
43
44 static void
45 usage(void)
46 {
47     fputs("usage: " PGMNAME " [-q] [-s] [--] [headernames]\n"
48           "\n"
49           "  -q   be silent\n"
50           "  -s   sloppy match: accept up to 2 non ascii characters\n"
51                       , stderr );
52     exit(2);
53 }
54
55 static int
56 name_cmp( const char *a, const char *b )
57 {
58     for( ; *a && *b; a++, b++ ) {
59         if( *a != *b
60             && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b) )
61             return 1;
62     }
63     return *a != *b;
64 }
65
66 static int
67 headerp( char *p, char **names )
68 {
69     int i, c;
70     char *p2;
71
72     p2 = strchr(p, ':');
73     if( !p2 || p == p2 ) {
74         if( !silent )
75             fputs( "header line without colon or name\n", stdout );
76         exit(1);
77     }
78     if( p2[-1] == ' ' || p2[-1] == '\t' ) {
79         if( !silent )
80             fputs( "invalid header field\n", stdout );
81         exit(1);
82     }
83
84     if( !names[0] )
85         return 1;  /* check all fields */
86     c = *p2;
87     *p2 = 0;
88     for(i=0 ; names[i]; i++ ) {
89         if( !name_cmp( names[i], p ) )
90             break;
91     }
92     *p2 = c;
93     return !!names[i];
94 }
95
96
97 static int
98 count_bad( const char *s )
99 {
100     int bad=0;
101
102     for(; *s; s++ ) {
103         if( *s & 0x80 )
104             bad++;
105     }
106     return bad;
107 }
108
109 int
110 main( int argc, char **argv )
111 {
112     int skip = 0;
113     char *names[MAX_NAMES+1];
114     int thisone, bad, i;
115     char line[2000];
116
117     if( argc < 1)
118         usage();  /* Hey, read how to uses exec*(2) */
119     argv++; argc--;
120
121     for( i=0; argc; argc--, argv++ ) {
122         const char *s = *argv;
123         if( !skip && *s == '-' ) {
124             s++;
125             if( *s == '-' && !s[1] ) {
126                 skip = 1;
127                 continue;
128             }
129             if( *s == '-' || !*s ) {
130                 usage();
131             }
132             while( *s ) {
133                 if( *s=='q' ) {
134                     silent=1;
135                     s++;
136                 }
137                 else if( *s=='s' ) {
138                     sloppy=1;
139                     s++;
140                 }
141                 else if( *s )
142                     usage();
143             }
144             continue;
145         }
146         if( i >= MAX_NAMES ) {
147             fputs(PGMNAME ": too many names given\n", stderr );
148             exit(2);
149         }
150         names[i++] = *argv;
151     }
152     names[i] = NULL;
153
154
155     /* now get the lines */
156     bad = thisone = 0;
157     while( fgets( line, sizeof line, stdin ) ) {
158         int n = strlen(line);
159         if( !n || line[n-1] != '\n' ) {
160             /* n == 0 should never happen */
161             if( !silent )
162                 fputs( "line too long - see RFC822\n", stdout );
163             exit(1);  /* maybe someone wants to circument this */
164         }
165         line[--n] = 0;
166         if( n && line[n-1] == '\r' )
167             line[--n] = 0;
168         if( !n ) {
169             exit(0); /* Here is the body - stop */
170         }
171         if( *line == ' ' || *line == '\t' ) {
172             /* we don't care when the first line is an invalid cont.-line */
173             if( thisone )
174                 bad += count_bad( line );
175         }
176         else if( headerp( line, names ) ) {
177             thisone = 1;
178             bad = count_bad( line );
179         }
180         else {
181             thisone = 0;
182             bad = 0;
183         }
184         if( (bad && !sloppy) || bad >= BAD_LIMIT ) {
185             if( !silent ) {
186                 if( sloppy )
187                     fputs( "too many 8-bit characters in a header\n", stdout );
188                 else
189                     fputs( "8-bit character in a header\n", stdout );
190             }
191             exit(1);
192         }
193     }
194     if( ferror(stdin) ) {
195         fputs( PGMNAME ": read error\n", stderr );
196         exit(1);
197     }
198     return 0;
199 }
200
201 /*
202 Local Variables:
203 compile-command: "gcc -Wall -g -o 8bit-in-header 8bit-in-header.c"
204 End:
205 */