Blame


1 5cdb1798 2005-10-29 devnull /*
2 5cdb1798 2005-10-29 devnull * this is a filter that changes mime types and names of
3 5cdb1798 2005-10-29 devnull * suspect executable attachments.
4 5cdb1798 2005-10-29 devnull */
5 5cdb1798 2005-10-29 devnull #include "common.h"
6 5cdb1798 2005-10-29 devnull #include <ctype.h>
7 20bd3ca2 2006-04-09 devnull
8 20bd3ca2 2006-04-09 devnull enum {
9 20bd3ca2 2006-04-09 devnull Accept = 0xA,
10 20bd3ca2 2006-04-09 devnull Discard = 0xD,
11 20bd3ca2 2006-04-09 devnull };
12 5cdb1798 2005-10-29 devnull
13 5cdb1798 2005-10-29 devnull Biobuf in;
14 5cdb1798 2005-10-29 devnull Biobuf out;
15 5cdb1798 2005-10-29 devnull
16 5cdb1798 2005-10-29 devnull typedef struct Mtype Mtype;
17 5cdb1798 2005-10-29 devnull typedef struct Hdef Hdef;
18 5cdb1798 2005-10-29 devnull typedef struct Hline Hline;
19 5cdb1798 2005-10-29 devnull typedef struct Part Part;
20 5cdb1798 2005-10-29 devnull
21 5cdb1798 2005-10-29 devnull static int badfile(char *name);
22 5cdb1798 2005-10-29 devnull static int badtype(char *type);
23 5cdb1798 2005-10-29 devnull static void ctype(Part*, Hdef*, char*);
24 5cdb1798 2005-10-29 devnull static void cencoding(Part*, Hdef*, char*);
25 5cdb1798 2005-10-29 devnull static void cdisposition(Part*, Hdef*, char*);
26 5cdb1798 2005-10-29 devnull static int decquoted(char *out, char *in, char *e);
27 5cdb1798 2005-10-29 devnull static char* getstring(char *p, String *s, int dolower);
28 5cdb1798 2005-10-29 devnull static void init_hdefs(void);
29 5cdb1798 2005-10-29 devnull static int isattribute(char **pp, char *attr);
30 5cdb1798 2005-10-29 devnull static int latin1toutf(char *out, char *in, char *e);
31 5cdb1798 2005-10-29 devnull static String* mkboundary(void);
32 5cdb1798 2005-10-29 devnull static Part* part(Part *pp);
33 5cdb1798 2005-10-29 devnull static Part* passbody(Part *p, int dobound);
34 5cdb1798 2005-10-29 devnull static void passnotheader(void);
35 5cdb1798 2005-10-29 devnull static void passunixheader(void);
36 5cdb1798 2005-10-29 devnull static Part* problemchild(Part *p);
37 5cdb1798 2005-10-29 devnull static void readheader(Part *p);
38 5cdb1798 2005-10-29 devnull static Hline* readhl(void);
39 5cdb1798 2005-10-29 devnull static void readmtypes(void);
40 5cdb1798 2005-10-29 devnull static int save(Part *p, char *file);
41 5cdb1798 2005-10-29 devnull static void setfilename(Part *p, char *name);
42 5cdb1798 2005-10-29 devnull static char* skiptosemi(char *p);
43 5cdb1798 2005-10-29 devnull static char* skipwhite(char *p);
44 5cdb1798 2005-10-29 devnull static String* tokenconvert(String *t);
45 5cdb1798 2005-10-29 devnull static void writeheader(Part *p, int);
46 5cdb1798 2005-10-29 devnull
47 5cdb1798 2005-10-29 devnull enum
48 5cdb1798 2005-10-29 devnull {
49 cbeb0b26 2006-04-01 devnull /* encodings */
50 5cdb1798 2005-10-29 devnull Enone= 0,
51 5cdb1798 2005-10-29 devnull Ebase64,
52 5cdb1798 2005-10-29 devnull Equoted,
53 5cdb1798 2005-10-29 devnull
54 cbeb0b26 2006-04-01 devnull /* disposition possibilities */
55 5cdb1798 2005-10-29 devnull Dnone= 0,
56 5cdb1798 2005-10-29 devnull Dinline,
57 5cdb1798 2005-10-29 devnull Dfile,
58 5cdb1798 2005-10-29 devnull Dignore,
59 5cdb1798 2005-10-29 devnull
60 cbeb0b26 2006-04-01 devnull PAD64= '='
61 5cdb1798 2005-10-29 devnull };
62 5cdb1798 2005-10-29 devnull
63 5cdb1798 2005-10-29 devnull /*
64 5cdb1798 2005-10-29 devnull * a message part; either the whole message or a subpart
65 5cdb1798 2005-10-29 devnull */
66 5cdb1798 2005-10-29 devnull struct Part
67 5cdb1798 2005-10-29 devnull {
68 5cdb1798 2005-10-29 devnull Part *pp; /* parent part */
69 5cdb1798 2005-10-29 devnull Hline *hl; /* linked list of header lines */
70 5cdb1798 2005-10-29 devnull int disposition;
71 5cdb1798 2005-10-29 devnull int encoding;
72 5cdb1798 2005-10-29 devnull int badfile;
73 5cdb1798 2005-10-29 devnull int badtype;
74 5cdb1798 2005-10-29 devnull String *boundary; /* boundary for multiparts */
75 5cdb1798 2005-10-29 devnull int blen;
76 5cdb1798 2005-10-29 devnull String *charset; /* character set */
77 5cdb1798 2005-10-29 devnull String *type; /* content type */
78 5cdb1798 2005-10-29 devnull String *filename; /* file name */
79 5cdb1798 2005-10-29 devnull Biobuf *tmpbuf; /* diversion input buffer */
80 5cdb1798 2005-10-29 devnull };
81 5cdb1798 2005-10-29 devnull
82 5cdb1798 2005-10-29 devnull /*
83 5cdb1798 2005-10-29 devnull * a (multi)line header
84 5cdb1798 2005-10-29 devnull */
85 5cdb1798 2005-10-29 devnull struct Hline
86 5cdb1798 2005-10-29 devnull {
87 5cdb1798 2005-10-29 devnull Hline *next;
88 5cdb1798 2005-10-29 devnull String *s;
89 5cdb1798 2005-10-29 devnull };
90 5cdb1798 2005-10-29 devnull
91 5cdb1798 2005-10-29 devnull /*
92 5cdb1798 2005-10-29 devnull * header definitions for parsing
93 5cdb1798 2005-10-29 devnull */
94 5cdb1798 2005-10-29 devnull struct Hdef
95 5cdb1798 2005-10-29 devnull {
96 5cdb1798 2005-10-29 devnull char *type;
97 5cdb1798 2005-10-29 devnull void (*f)(Part*, Hdef*, char*);
98 5cdb1798 2005-10-29 devnull int len;
99 5cdb1798 2005-10-29 devnull };
100 5cdb1798 2005-10-29 devnull
101 5cdb1798 2005-10-29 devnull Hdef hdefs[] =
102 5cdb1798 2005-10-29 devnull {
103 5cdb1798 2005-10-29 devnull { "content-type:", ctype, },
104 5cdb1798 2005-10-29 devnull { "content-transfer-encoding:", cencoding, },
105 5cdb1798 2005-10-29 devnull { "content-disposition:", cdisposition, },
106 cbeb0b26 2006-04-01 devnull { 0, }
107 5cdb1798 2005-10-29 devnull };
108 5cdb1798 2005-10-29 devnull
109 5cdb1798 2005-10-29 devnull /*
110 5cdb1798 2005-10-29 devnull * acceptable content types and their extensions
111 5cdb1798 2005-10-29 devnull */
112 5cdb1798 2005-10-29 devnull struct Mtype {
113 5cdb1798 2005-10-29 devnull Mtype *next;
114 5cdb1798 2005-10-29 devnull char *ext; /* extension */
115 5cdb1798 2005-10-29 devnull char *gtype; /* generic content type */
116 5cdb1798 2005-10-29 devnull char *stype; /* specific content type */
117 5cdb1798 2005-10-29 devnull char class;
118 5cdb1798 2005-10-29 devnull };
119 5cdb1798 2005-10-29 devnull Mtype *mtypes;
120 5cdb1798 2005-10-29 devnull
121 5cdb1798 2005-10-29 devnull int justreject;
122 5cdb1798 2005-10-29 devnull char *savefile;
123 5cdb1798 2005-10-29 devnull
124 5cdb1798 2005-10-29 devnull void
125 b5f65921 2006-02-11 devnull usage(void)
126 b5f65921 2006-02-11 devnull {
127 b5f65921 2006-02-11 devnull fprint(2, "usage: upas/vf [-r] [-s savefile]\n");
128 b5f65921 2006-02-11 devnull exits("usage");
129 b5f65921 2006-02-11 devnull }
130 b5f65921 2006-02-11 devnull
131 b5f65921 2006-02-11 devnull void
132 5cdb1798 2005-10-29 devnull main(int argc, char **argv)
133 5cdb1798 2005-10-29 devnull {
134 5cdb1798 2005-10-29 devnull ARGBEGIN{
135 5cdb1798 2005-10-29 devnull case 'r':
136 5cdb1798 2005-10-29 devnull justreject = 1;
137 5cdb1798 2005-10-29 devnull break;
138 5cdb1798 2005-10-29 devnull case 's':
139 b5f65921 2006-02-11 devnull savefile = EARGF(usage());
140 5cdb1798 2005-10-29 devnull break;
141 b5f65921 2006-02-11 devnull default:
142 b5f65921 2006-02-11 devnull usage();
143 5cdb1798 2005-10-29 devnull }ARGEND;
144 b5f65921 2006-02-11 devnull
145 b5f65921 2006-02-11 devnull if(argc)
146 b5f65921 2006-02-11 devnull usage();
147 5cdb1798 2005-10-29 devnull
148 5cdb1798 2005-10-29 devnull Binit(&in, 0, OREAD);
149 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
150 5cdb1798 2005-10-29 devnull
151 5cdb1798 2005-10-29 devnull init_hdefs();
152 5cdb1798 2005-10-29 devnull readmtypes();
153 5cdb1798 2005-10-29 devnull
154 5cdb1798 2005-10-29 devnull /* pass through our standard 'From ' line */
155 5cdb1798 2005-10-29 devnull passunixheader();
156 5cdb1798 2005-10-29 devnull
157 5cdb1798 2005-10-29 devnull /* parse with the top level part */
158 5cdb1798 2005-10-29 devnull part(nil);
159 5cdb1798 2005-10-29 devnull
160 5cdb1798 2005-10-29 devnull exits(0);
161 5cdb1798 2005-10-29 devnull }
162 5cdb1798 2005-10-29 devnull
163 5cdb1798 2005-10-29 devnull void
164 5cdb1798 2005-10-29 devnull refuse(void)
165 5cdb1798 2005-10-29 devnull {
166 5cdb1798 2005-10-29 devnull postnote(PNGROUP, getpid(), "mail refused: we don't accept executable attachments");
167 5cdb1798 2005-10-29 devnull exits("mail refused: we don't accept executable attachments");
168 5cdb1798 2005-10-29 devnull }
169 5cdb1798 2005-10-29 devnull
170 5cdb1798 2005-10-29 devnull
171 5cdb1798 2005-10-29 devnull /*
172 5cdb1798 2005-10-29 devnull * parse a part; returns the ancestor whose boundary terminated
173 5cdb1798 2005-10-29 devnull * this part or nil on EOF.
174 5cdb1798 2005-10-29 devnull */
175 5cdb1798 2005-10-29 devnull static Part*
176 5cdb1798 2005-10-29 devnull part(Part *pp)
177 5cdb1798 2005-10-29 devnull {
178 5cdb1798 2005-10-29 devnull Part *p, *np;
179 5cdb1798 2005-10-29 devnull
180 5cdb1798 2005-10-29 devnull p = mallocz(sizeof *p, 1);
181 5cdb1798 2005-10-29 devnull p->pp = pp;
182 5cdb1798 2005-10-29 devnull readheader(p);
183 5cdb1798 2005-10-29 devnull
184 5cdb1798 2005-10-29 devnull if(p->boundary != nil){
185 5cdb1798 2005-10-29 devnull /* the format of a multipart part is always:
186 5cdb1798 2005-10-29 devnull * header
187 5cdb1798 2005-10-29 devnull * null or ignored body
188 5cdb1798 2005-10-29 devnull * boundary
189 5cdb1798 2005-10-29 devnull * header
190 5cdb1798 2005-10-29 devnull * body
191 5cdb1798 2005-10-29 devnull * boundary
192 5cdb1798 2005-10-29 devnull * ...
193 5cdb1798 2005-10-29 devnull */
194 5cdb1798 2005-10-29 devnull writeheader(p, 1);
195 5cdb1798 2005-10-29 devnull np = passbody(p, 1);
196 5cdb1798 2005-10-29 devnull if(np != p)
197 5cdb1798 2005-10-29 devnull return np;
198 5cdb1798 2005-10-29 devnull for(;;){
199 5cdb1798 2005-10-29 devnull np = part(p);
200 5cdb1798 2005-10-29 devnull if(np != p)
201 5cdb1798 2005-10-29 devnull return np;
202 5cdb1798 2005-10-29 devnull }
203 5cdb1798 2005-10-29 devnull } else {
204 5cdb1798 2005-10-29 devnull /* no boundary */
205 5cdb1798 2005-10-29 devnull /* may still be multipart if this is a forwarded message */
206 5cdb1798 2005-10-29 devnull if(p->type && cistrcmp(s_to_c(p->type), "message/rfc822") == 0){
207 5cdb1798 2005-10-29 devnull /* the format of forwarded message is:
208 5cdb1798 2005-10-29 devnull * header
209 5cdb1798 2005-10-29 devnull * header
210 5cdb1798 2005-10-29 devnull * body
211 5cdb1798 2005-10-29 devnull */
212 5cdb1798 2005-10-29 devnull writeheader(p, 1);
213 5cdb1798 2005-10-29 devnull passnotheader();
214 5cdb1798 2005-10-29 devnull return part(p);
215 5cdb1798 2005-10-29 devnull } else {
216 5cdb1798 2005-10-29 devnull /*
217 5cdb1798 2005-10-29 devnull * This is the meat. This may be an executable.
218 5cdb1798 2005-10-29 devnull * if so, wrap it and change its type
219 5cdb1798 2005-10-29 devnull */
220 5cdb1798 2005-10-29 devnull if(p->badtype || p->badfile){
221 5cdb1798 2005-10-29 devnull if(p->badfile == 2){
222 5cdb1798 2005-10-29 devnull if(savefile != nil)
223 5cdb1798 2005-10-29 devnull save(p, savefile);
224 5cdb1798 2005-10-29 devnull syslog(0, "vf", "vf rejected %s %s", p->type?s_to_c(p->type):"?",
225 5cdb1798 2005-10-29 devnull p->filename?s_to_c(p->filename):"?");
226 5cdb1798 2005-10-29 devnull fprint(2, "The mail contained an executable attachment.\n");
227 5cdb1798 2005-10-29 devnull fprint(2, "We refuse all mail containing such.\n");
228 5cdb1798 2005-10-29 devnull refuse();
229 5cdb1798 2005-10-29 devnull }
230 5cdb1798 2005-10-29 devnull np = problemchild(p);
231 5cdb1798 2005-10-29 devnull if(np != p)
232 5cdb1798 2005-10-29 devnull return np;
233 5cdb1798 2005-10-29 devnull /* if problemchild returns p, it turns out p is okay: fall thru */
234 5cdb1798 2005-10-29 devnull }
235 5cdb1798 2005-10-29 devnull writeheader(p, 1);
236 5cdb1798 2005-10-29 devnull return passbody(p, 1);
237 5cdb1798 2005-10-29 devnull }
238 5cdb1798 2005-10-29 devnull }
239 5cdb1798 2005-10-29 devnull }
240 5cdb1798 2005-10-29 devnull
241 5cdb1798 2005-10-29 devnull /*
242 5cdb1798 2005-10-29 devnull * read and parse a complete header
243 5cdb1798 2005-10-29 devnull */
244 5cdb1798 2005-10-29 devnull static void
245 5cdb1798 2005-10-29 devnull readheader(Part *p)
246 5cdb1798 2005-10-29 devnull {
247 5cdb1798 2005-10-29 devnull Hline *hl, **l;
248 5cdb1798 2005-10-29 devnull Hdef *hd;
249 5cdb1798 2005-10-29 devnull
250 5cdb1798 2005-10-29 devnull l = &p->hl;
251 5cdb1798 2005-10-29 devnull for(;;){
252 5cdb1798 2005-10-29 devnull hl = readhl();
253 5cdb1798 2005-10-29 devnull if(hl == nil)
254 5cdb1798 2005-10-29 devnull break;
255 5cdb1798 2005-10-29 devnull *l = hl;
256 5cdb1798 2005-10-29 devnull l = &hl->next;
257 5cdb1798 2005-10-29 devnull
258 5cdb1798 2005-10-29 devnull for(hd = hdefs; hd->type != nil; hd++){
259 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), hd->type, hd->len) == 0){
260 5cdb1798 2005-10-29 devnull (*hd->f)(p, hd, s_to_c(hl->s));
261 5cdb1798 2005-10-29 devnull break;
262 5cdb1798 2005-10-29 devnull }
263 5cdb1798 2005-10-29 devnull }
264 5cdb1798 2005-10-29 devnull }
265 5cdb1798 2005-10-29 devnull }
266 5cdb1798 2005-10-29 devnull
267 5cdb1798 2005-10-29 devnull /*
268 5cdb1798 2005-10-29 devnull * read a possibly multiline header line
269 5cdb1798 2005-10-29 devnull */
270 5cdb1798 2005-10-29 devnull static Hline*
271 5cdb1798 2005-10-29 devnull readhl(void)
272 5cdb1798 2005-10-29 devnull {
273 5cdb1798 2005-10-29 devnull Hline *hl;
274 5cdb1798 2005-10-29 devnull String *s;
275 5cdb1798 2005-10-29 devnull char *p;
276 5cdb1798 2005-10-29 devnull int n;
277 5cdb1798 2005-10-29 devnull
278 5cdb1798 2005-10-29 devnull p = Brdline(&in, '\n');
279 5cdb1798 2005-10-29 devnull if(p == nil)
280 5cdb1798 2005-10-29 devnull return nil;
281 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
282 5cdb1798 2005-10-29 devnull if(memchr(p, ':', n) == nil){
283 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
284 5cdb1798 2005-10-29 devnull return nil;
285 5cdb1798 2005-10-29 devnull }
286 5cdb1798 2005-10-29 devnull s = s_nappend(s_new(), p, n);
287 5cdb1798 2005-10-29 devnull for(;;){
288 5cdb1798 2005-10-29 devnull p = Brdline(&in, '\n');
289 5cdb1798 2005-10-29 devnull if(p == nil)
290 5cdb1798 2005-10-29 devnull break;
291 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
292 5cdb1798 2005-10-29 devnull if(*p != ' ' && *p != '\t'){
293 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
294 5cdb1798 2005-10-29 devnull break;
295 5cdb1798 2005-10-29 devnull }
296 5cdb1798 2005-10-29 devnull s = s_nappend(s, p, n);
297 5cdb1798 2005-10-29 devnull }
298 5cdb1798 2005-10-29 devnull hl = malloc(sizeof *hl);
299 5cdb1798 2005-10-29 devnull hl->s = s;
300 5cdb1798 2005-10-29 devnull hl->next = nil;
301 5cdb1798 2005-10-29 devnull return hl;
302 5cdb1798 2005-10-29 devnull }
303 5cdb1798 2005-10-29 devnull
304 5cdb1798 2005-10-29 devnull /*
305 5cdb1798 2005-10-29 devnull * write out a complete header
306 5cdb1798 2005-10-29 devnull */
307 5cdb1798 2005-10-29 devnull static void
308 5cdb1798 2005-10-29 devnull writeheader(Part *p, int xfree)
309 5cdb1798 2005-10-29 devnull {
310 5cdb1798 2005-10-29 devnull Hline *hl, *next;
311 5cdb1798 2005-10-29 devnull
312 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = next){
313 5cdb1798 2005-10-29 devnull Bprint(&out, "%s", s_to_c(hl->s));
314 5cdb1798 2005-10-29 devnull if(xfree)
315 5cdb1798 2005-10-29 devnull s_free(hl->s);
316 5cdb1798 2005-10-29 devnull next = hl->next;
317 5cdb1798 2005-10-29 devnull if(xfree)
318 5cdb1798 2005-10-29 devnull free(hl);
319 5cdb1798 2005-10-29 devnull }
320 5cdb1798 2005-10-29 devnull if(xfree)
321 5cdb1798 2005-10-29 devnull p->hl = nil;
322 5cdb1798 2005-10-29 devnull }
323 5cdb1798 2005-10-29 devnull
324 5cdb1798 2005-10-29 devnull /*
325 5cdb1798 2005-10-29 devnull * pass a body through. return if we hit one of our ancestors'
326 5cdb1798 2005-10-29 devnull * boundaries or EOF. if we hit a boundary, return a pointer to
327 5cdb1798 2005-10-29 devnull * that ancestor. if we hit EOF, return nil.
328 5cdb1798 2005-10-29 devnull */
329 5cdb1798 2005-10-29 devnull static Part*
330 5cdb1798 2005-10-29 devnull passbody(Part *p, int dobound)
331 5cdb1798 2005-10-29 devnull {
332 5cdb1798 2005-10-29 devnull Part *pp;
333 5cdb1798 2005-10-29 devnull Biobuf *b;
334 5cdb1798 2005-10-29 devnull char *cp;
335 5cdb1798 2005-10-29 devnull
336 5cdb1798 2005-10-29 devnull for(;;){
337 5cdb1798 2005-10-29 devnull if(p->tmpbuf){
338 5cdb1798 2005-10-29 devnull b = p->tmpbuf;
339 5cdb1798 2005-10-29 devnull cp = Brdline(b, '\n');
340 5cdb1798 2005-10-29 devnull if(cp == nil){
341 5cdb1798 2005-10-29 devnull Bterm(b);
342 5cdb1798 2005-10-29 devnull p->tmpbuf = nil;
343 5cdb1798 2005-10-29 devnull goto Stdin;
344 5cdb1798 2005-10-29 devnull }
345 5cdb1798 2005-10-29 devnull }else{
346 5cdb1798 2005-10-29 devnull Stdin:
347 5cdb1798 2005-10-29 devnull b = &in;
348 5cdb1798 2005-10-29 devnull cp = Brdline(b, '\n');
349 5cdb1798 2005-10-29 devnull }
350 5cdb1798 2005-10-29 devnull if(cp == nil)
351 5cdb1798 2005-10-29 devnull return nil;
352 5cdb1798 2005-10-29 devnull for(pp = p; pp != nil; pp = pp->pp)
353 5cdb1798 2005-10-29 devnull if(pp->boundary != nil
354 5cdb1798 2005-10-29 devnull && strncmp(cp, s_to_c(pp->boundary), pp->blen) == 0){
355 5cdb1798 2005-10-29 devnull if(dobound)
356 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(b));
357 5cdb1798 2005-10-29 devnull else
358 5cdb1798 2005-10-29 devnull Bseek(b, -Blinelen(b), 1);
359 5cdb1798 2005-10-29 devnull return pp;
360 5cdb1798 2005-10-29 devnull }
361 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(b));
362 5cdb1798 2005-10-29 devnull }
363 5cdb1798 2005-10-29 devnull return nil;
364 5cdb1798 2005-10-29 devnull }
365 5cdb1798 2005-10-29 devnull
366 5cdb1798 2005-10-29 devnull /*
367 5cdb1798 2005-10-29 devnull * save the message somewhere
368 5cdb1798 2005-10-29 devnull */
369 5cdb1798 2005-10-29 devnull static vlong bodyoff; /* clumsy hack */
370 5cdb1798 2005-10-29 devnull static int
371 5cdb1798 2005-10-29 devnull save(Part *p, char *file)
372 5cdb1798 2005-10-29 devnull {
373 5cdb1798 2005-10-29 devnull int fd;
374 5cdb1798 2005-10-29 devnull char *cp;
375 5cdb1798 2005-10-29 devnull
376 5cdb1798 2005-10-29 devnull Bterm(&out);
377 5cdb1798 2005-10-29 devnull memset(&out, 0, sizeof(out));
378 5cdb1798 2005-10-29 devnull
379 5cdb1798 2005-10-29 devnull fd = open(file, OWRITE);
380 5cdb1798 2005-10-29 devnull if(fd < 0)
381 5cdb1798 2005-10-29 devnull return -1;
382 5cdb1798 2005-10-29 devnull seek(fd, 0, 2);
383 5cdb1798 2005-10-29 devnull Binit(&out, fd, OWRITE);
384 5cdb1798 2005-10-29 devnull cp = ctime(time(0));
385 5cdb1798 2005-10-29 devnull cp[28] = 0;
386 5cdb1798 2005-10-29 devnull Bprint(&out, "From virusfilter %s\n", cp);
387 5cdb1798 2005-10-29 devnull writeheader(p, 0);
388 5cdb1798 2005-10-29 devnull bodyoff = Boffset(&out);
389 5cdb1798 2005-10-29 devnull passbody(p, 1);
390 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
391 5cdb1798 2005-10-29 devnull Bterm(&out);
392 5cdb1798 2005-10-29 devnull close(fd);
393 5cdb1798 2005-10-29 devnull
394 5cdb1798 2005-10-29 devnull memset(&out, 0, sizeof out);
395 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
396 5cdb1798 2005-10-29 devnull return 0;
397 5cdb1798 2005-10-29 devnull }
398 5cdb1798 2005-10-29 devnull
399 5cdb1798 2005-10-29 devnull /*
400 5cdb1798 2005-10-29 devnull * write to a file but save the fd for passbody.
401 5cdb1798 2005-10-29 devnull */
402 5cdb1798 2005-10-29 devnull static char*
403 5cdb1798 2005-10-29 devnull savetmp(Part *p)
404 5cdb1798 2005-10-29 devnull {
405 5cdb1798 2005-10-29 devnull char buf[40], *name;
406 5cdb1798 2005-10-29 devnull int fd;
407 5cdb1798 2005-10-29 devnull
408 20bd3ca2 2006-04-09 devnull strcpy(buf, "/var/tmp/vf.XXXXXXXXXXX");
409 20bd3ca2 2006-04-09 devnull if((fd = mkstemp(buf)) < 0){
410 5cdb1798 2005-10-29 devnull fprint(2, "error creating temporary file: %r\n");
411 5cdb1798 2005-10-29 devnull refuse();
412 5cdb1798 2005-10-29 devnull }
413 20bd3ca2 2006-04-09 devnull name = buf;
414 5cdb1798 2005-10-29 devnull close(fd);
415 5cdb1798 2005-10-29 devnull if(save(p, name) < 0){
416 5cdb1798 2005-10-29 devnull fprint(2, "error saving temporary file: %r\n");
417 5cdb1798 2005-10-29 devnull refuse();
418 5cdb1798 2005-10-29 devnull }
419 5cdb1798 2005-10-29 devnull if(p->tmpbuf){
420 5cdb1798 2005-10-29 devnull fprint(2, "error in savetmp: already have tmp file!\n");
421 5cdb1798 2005-10-29 devnull refuse();
422 5cdb1798 2005-10-29 devnull }
423 5cdb1798 2005-10-29 devnull p->tmpbuf = Bopen(name, OREAD|ORCLOSE);
424 5cdb1798 2005-10-29 devnull if(p->tmpbuf == nil){
425 5cdb1798 2005-10-29 devnull fprint(2, "error reading tempoary file: %r\n");
426 5cdb1798 2005-10-29 devnull refuse();
427 5cdb1798 2005-10-29 devnull }
428 5cdb1798 2005-10-29 devnull Bseek(p->tmpbuf, bodyoff, 0);
429 5cdb1798 2005-10-29 devnull return strdup(name);
430 5cdb1798 2005-10-29 devnull }
431 5cdb1798 2005-10-29 devnull
432 5cdb1798 2005-10-29 devnull /*
433 b5f65921 2006-02-11 devnull * Run the external checker to do content-based checks.
434 5cdb1798 2005-10-29 devnull */
435 5cdb1798 2005-10-29 devnull static int
436 5cdb1798 2005-10-29 devnull runchecker(Part *p)
437 5cdb1798 2005-10-29 devnull {
438 5cdb1798 2005-10-29 devnull int pid;
439 5cdb1798 2005-10-29 devnull char *name;
440 5cdb1798 2005-10-29 devnull Waitmsg *w;
441 20bd3ca2 2006-04-09 devnull static char *val;
442 5cdb1798 2005-10-29 devnull
443 20bd3ca2 2006-04-09 devnull if(val == nil)
444 20bd3ca2 2006-04-09 devnull val = unsharp("#9/mail/lib/validateattachment");
445 20bd3ca2 2006-04-09 devnull if(val == nil || access(val, AEXEC) < 0)
446 20bd3ca2 2006-04-09 devnull return 0;
447 20bd3ca2 2006-04-09 devnull
448 5cdb1798 2005-10-29 devnull name = savetmp(p);
449 5cdb1798 2005-10-29 devnull fprint(2, "run checker %s\n", name);
450 5cdb1798 2005-10-29 devnull switch(pid = fork()){
451 5cdb1798 2005-10-29 devnull case -1:
452 5cdb1798 2005-10-29 devnull sysfatal("fork: %r");
453 5cdb1798 2005-10-29 devnull case 0:
454 5cdb1798 2005-10-29 devnull dup(2, 1);
455 20bd3ca2 2006-04-09 devnull execl(val, "validateattachment", name, nil);
456 5cdb1798 2005-10-29 devnull _exits("exec failed");
457 5cdb1798 2005-10-29 devnull }
458 5cdb1798 2005-10-29 devnull
459 5cdb1798 2005-10-29 devnull /*
460 5cdb1798 2005-10-29 devnull * Okay to return on error - will let mail through but wrapped.
461 5cdb1798 2005-10-29 devnull */
462 5cdb1798 2005-10-29 devnull w = wait();
463 5cdb1798 2005-10-29 devnull if(w == nil){
464 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wait failed: %r");
465 5cdb1798 2005-10-29 devnull return 0;
466 5cdb1798 2005-10-29 devnull }
467 5cdb1798 2005-10-29 devnull if(w->pid != pid){
468 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrong pid %d != %d", w->pid, pid);
469 5cdb1798 2005-10-29 devnull return 0;
470 5cdb1798 2005-10-29 devnull }
471 5cdb1798 2005-10-29 devnull if(p->filename)
472 5cdb1798 2005-10-29 devnull name = s_to_c(p->filename);
473 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Discard){
474 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment rejected %s", name);
475 5cdb1798 2005-10-29 devnull refuse();
476 5cdb1798 2005-10-29 devnull }
477 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Accept){
478 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment accepted %s", name);
479 5cdb1798 2005-10-29 devnull return 1;
480 5cdb1798 2005-10-29 devnull }
481 5cdb1798 2005-10-29 devnull free(w);
482 5cdb1798 2005-10-29 devnull return 0;
483 5cdb1798 2005-10-29 devnull }
484 5cdb1798 2005-10-29 devnull
485 5cdb1798 2005-10-29 devnull /*
486 5cdb1798 2005-10-29 devnull * emit a multipart Part that explains the problem
487 5cdb1798 2005-10-29 devnull */
488 5cdb1798 2005-10-29 devnull static Part*
489 5cdb1798 2005-10-29 devnull problemchild(Part *p)
490 5cdb1798 2005-10-29 devnull {
491 5cdb1798 2005-10-29 devnull Part *np;
492 5cdb1798 2005-10-29 devnull Hline *hl;
493 5cdb1798 2005-10-29 devnull String *boundary;
494 5cdb1798 2005-10-29 devnull char *cp;
495 5cdb1798 2005-10-29 devnull
496 5cdb1798 2005-10-29 devnull /*
497 5cdb1798 2005-10-29 devnull * We don't know whether the attachment is okay.
498 5cdb1798 2005-10-29 devnull * If there's an external checker, let it have a crack at it.
499 5cdb1798 2005-10-29 devnull */
500 5cdb1798 2005-10-29 devnull if(runchecker(p) > 0)
501 5cdb1798 2005-10-29 devnull return p;
502 5cdb1798 2005-10-29 devnull
503 b5f65921 2006-02-11 devnull if(justreject)
504 b5f65921 2006-02-11 devnull return p;
505 b5f65921 2006-02-11 devnull
506 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrapped %s %s", p->type?s_to_c(p->type):"?",
507 5cdb1798 2005-10-29 devnull p->filename?s_to_c(p->filename):"?");
508 5cdb1798 2005-10-29 devnull
509 5cdb1798 2005-10-29 devnull boundary = mkboundary();
510 5cdb1798 2005-10-29 devnull /* print out non-mime headers */
511 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
512 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) != 0)
513 5cdb1798 2005-10-29 devnull Bprint(&out, "%s", s_to_c(hl->s));
514 5cdb1798 2005-10-29 devnull
515 5cdb1798 2005-10-29 devnull /* add in our own multipart headers and message */
516 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: multipart/mixed;\n");
517 5cdb1798 2005-10-29 devnull Bprint(&out, "\tboundary=\"%s\"\n", s_to_c(boundary));
518 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
519 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
520 5cdb1798 2005-10-29 devnull Bprint(&out, "This is a multi-part message in MIME format.\n");
521 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
522 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
523 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
524 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: 7bit\n");
525 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
526 5cdb1798 2005-10-29 devnull Bprint(&out, "from postmaster@%s:\n", sysname());
527 5cdb1798 2005-10-29 devnull Bprint(&out, "The following attachment had content that we can't\n");
528 5cdb1798 2005-10-29 devnull Bprint(&out, "prove to be harmless. To avoid possible automatic\n");
529 5cdb1798 2005-10-29 devnull Bprint(&out, "execution, we changed the content headers.\n");
530 5cdb1798 2005-10-29 devnull Bprint(&out, "The original header was:\n\n");
531 5cdb1798 2005-10-29 devnull
532 5cdb1798 2005-10-29 devnull /* print out original header lines */
533 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
534 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) == 0)
535 5cdb1798 2005-10-29 devnull Bprint(&out, "\t%s", s_to_c(hl->s));
536 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
537 5cdb1798 2005-10-29 devnull
538 5cdb1798 2005-10-29 devnull /* change file name */
539 5cdb1798 2005-10-29 devnull if(p->filename)
540 5cdb1798 2005-10-29 devnull s_append(p->filename, ".suspect");
541 5cdb1798 2005-10-29 devnull else
542 5cdb1798 2005-10-29 devnull p->filename = s_copy("file.suspect");
543 5cdb1798 2005-10-29 devnull
544 5cdb1798 2005-10-29 devnull /* print out new header */
545 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: application/octet-stream\n");
546 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: attachment; filename=\"%s\"\n", s_to_c(p->filename));
547 5cdb1798 2005-10-29 devnull switch(p->encoding){
548 5cdb1798 2005-10-29 devnull case Enone:
549 5cdb1798 2005-10-29 devnull break;
550 5cdb1798 2005-10-29 devnull case Ebase64:
551 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: base64\n");
552 5cdb1798 2005-10-29 devnull break;
553 5cdb1798 2005-10-29 devnull case Equoted:
554 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: quoted-printable\n");
555 5cdb1798 2005-10-29 devnull break;
556 5cdb1798 2005-10-29 devnull }
557 5cdb1798 2005-10-29 devnull
558 5cdb1798 2005-10-29 devnull /* pass the body */
559 5cdb1798 2005-10-29 devnull np = passbody(p, 0);
560 5cdb1798 2005-10-29 devnull
561 5cdb1798 2005-10-29 devnull /* add the new boundary and the original terminator */
562 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s--\n", s_to_c(boundary));
563 5cdb1798 2005-10-29 devnull if(np && np->boundary){
564 5cdb1798 2005-10-29 devnull cp = Brdline(&in, '\n');
565 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(&in));
566 5cdb1798 2005-10-29 devnull }
567 5cdb1798 2005-10-29 devnull
568 5cdb1798 2005-10-29 devnull return np;
569 5cdb1798 2005-10-29 devnull }
570 5cdb1798 2005-10-29 devnull
571 5cdb1798 2005-10-29 devnull static int
572 5cdb1798 2005-10-29 devnull isattribute(char **pp, char *attr)
573 5cdb1798 2005-10-29 devnull {
574 5cdb1798 2005-10-29 devnull char *p;
575 5cdb1798 2005-10-29 devnull int n;
576 5cdb1798 2005-10-29 devnull
577 5cdb1798 2005-10-29 devnull n = strlen(attr);
578 5cdb1798 2005-10-29 devnull p = *pp;
579 5cdb1798 2005-10-29 devnull if(cistrncmp(p, attr, n) != 0)
580 5cdb1798 2005-10-29 devnull return 0;
581 5cdb1798 2005-10-29 devnull p += n;
582 5cdb1798 2005-10-29 devnull while(*p == ' ')
583 5cdb1798 2005-10-29 devnull p++;
584 5cdb1798 2005-10-29 devnull if(*p++ != '=')
585 5cdb1798 2005-10-29 devnull return 0;
586 5cdb1798 2005-10-29 devnull while(*p == ' ')
587 5cdb1798 2005-10-29 devnull p++;
588 5cdb1798 2005-10-29 devnull *pp = p;
589 5cdb1798 2005-10-29 devnull return 1;
590 5cdb1798 2005-10-29 devnull }
591 5cdb1798 2005-10-29 devnull
592 5cdb1798 2005-10-29 devnull /*
593 5cdb1798 2005-10-29 devnull * parse content type header
594 5cdb1798 2005-10-29 devnull */
595 5cdb1798 2005-10-29 devnull static void
596 5cdb1798 2005-10-29 devnull ctype(Part *p, Hdef *h, char *cp)
597 5cdb1798 2005-10-29 devnull {
598 5cdb1798 2005-10-29 devnull String *s;
599 5cdb1798 2005-10-29 devnull
600 5cdb1798 2005-10-29 devnull cp += h->len;
601 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
602 5cdb1798 2005-10-29 devnull
603 5cdb1798 2005-10-29 devnull p->type = s_new();
604 5cdb1798 2005-10-29 devnull cp = getstring(cp, p->type, 1);
605 5cdb1798 2005-10-29 devnull if(badtype(s_to_c(p->type)))
606 5cdb1798 2005-10-29 devnull p->badtype = 1;
607 5cdb1798 2005-10-29 devnull
608 5cdb1798 2005-10-29 devnull while(*cp){
609 5cdb1798 2005-10-29 devnull if(isattribute(&cp, "boundary")){
610 5cdb1798 2005-10-29 devnull s = s_new();
611 5cdb1798 2005-10-29 devnull cp = getstring(cp, s, 0);
612 5cdb1798 2005-10-29 devnull p->boundary = s_reset(p->boundary);
613 5cdb1798 2005-10-29 devnull s_append(p->boundary, "--");
614 5cdb1798 2005-10-29 devnull s_append(p->boundary, s_to_c(s));
615 5cdb1798 2005-10-29 devnull p->blen = s_len(p->boundary);
616 5cdb1798 2005-10-29 devnull s_free(s);
617 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "multipart", 9) == 0){
618 5cdb1798 2005-10-29 devnull /*
619 5cdb1798 2005-10-29 devnull * the first unbounded part of a multipart message,
620 5cdb1798 2005-10-29 devnull * the preamble, is not displayed or saved
621 5cdb1798 2005-10-29 devnull */
622 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "name")){
623 5cdb1798 2005-10-29 devnull setfilename(p, cp);
624 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "charset")){
625 5cdb1798 2005-10-29 devnull if(p->charset == nil)
626 5cdb1798 2005-10-29 devnull p->charset = s_new();
627 5cdb1798 2005-10-29 devnull cp = getstring(cp, s_reset(p->charset), 0);
628 5cdb1798 2005-10-29 devnull }
629 5cdb1798 2005-10-29 devnull
630 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
631 5cdb1798 2005-10-29 devnull }
632 5cdb1798 2005-10-29 devnull }
633 5cdb1798 2005-10-29 devnull
634 5cdb1798 2005-10-29 devnull /*
635 5cdb1798 2005-10-29 devnull * parse content encoding header
636 5cdb1798 2005-10-29 devnull */
637 5cdb1798 2005-10-29 devnull static void
638 5cdb1798 2005-10-29 devnull cencoding(Part *m, Hdef *h, char *p)
639 5cdb1798 2005-10-29 devnull {
640 5cdb1798 2005-10-29 devnull p += h->len;
641 5cdb1798 2005-10-29 devnull p = skipwhite(p);
642 5cdb1798 2005-10-29 devnull if(cistrncmp(p, "base64", 6) == 0)
643 5cdb1798 2005-10-29 devnull m->encoding = Ebase64;
644 5cdb1798 2005-10-29 devnull else if(cistrncmp(p, "quoted-printable", 16) == 0)
645 5cdb1798 2005-10-29 devnull m->encoding = Equoted;
646 5cdb1798 2005-10-29 devnull }
647 5cdb1798 2005-10-29 devnull
648 5cdb1798 2005-10-29 devnull /*
649 5cdb1798 2005-10-29 devnull * parse content disposition header
650 5cdb1798 2005-10-29 devnull */
651 5cdb1798 2005-10-29 devnull static void
652 5cdb1798 2005-10-29 devnull cdisposition(Part *p, Hdef *h, char *cp)
653 5cdb1798 2005-10-29 devnull {
654 5cdb1798 2005-10-29 devnull cp += h->len;
655 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
656 5cdb1798 2005-10-29 devnull while(*cp){
657 5cdb1798 2005-10-29 devnull if(cistrncmp(cp, "inline", 6) == 0){
658 5cdb1798 2005-10-29 devnull p->disposition = Dinline;
659 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "attachment", 10) == 0){
660 5cdb1798 2005-10-29 devnull p->disposition = Dfile;
661 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "filename=", 9) == 0){
662 5cdb1798 2005-10-29 devnull cp += 9;
663 5cdb1798 2005-10-29 devnull setfilename(p, cp);
664 5cdb1798 2005-10-29 devnull }
665 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
666 5cdb1798 2005-10-29 devnull }
667 5cdb1798 2005-10-29 devnull
668 5cdb1798 2005-10-29 devnull }
669 5cdb1798 2005-10-29 devnull
670 5cdb1798 2005-10-29 devnull static void
671 5cdb1798 2005-10-29 devnull setfilename(Part *p, char *name)
672 5cdb1798 2005-10-29 devnull {
673 5cdb1798 2005-10-29 devnull if(p->filename == nil)
674 5cdb1798 2005-10-29 devnull p->filename = s_new();
675 5cdb1798 2005-10-29 devnull getstring(name, s_reset(p->filename), 0);
676 5cdb1798 2005-10-29 devnull p->filename = tokenconvert(p->filename);
677 5cdb1798 2005-10-29 devnull p->badfile = badfile(s_to_c(p->filename));
678 5cdb1798 2005-10-29 devnull }
679 5cdb1798 2005-10-29 devnull
680 5cdb1798 2005-10-29 devnull static char*
681 5cdb1798 2005-10-29 devnull skipwhite(char *p)
682 5cdb1798 2005-10-29 devnull {
683 5cdb1798 2005-10-29 devnull while(isspace(*p))
684 5cdb1798 2005-10-29 devnull p++;
685 5cdb1798 2005-10-29 devnull return p;
686 5cdb1798 2005-10-29 devnull }
687 5cdb1798 2005-10-29 devnull
688 5cdb1798 2005-10-29 devnull static char*
689 5cdb1798 2005-10-29 devnull skiptosemi(char *p)
690 5cdb1798 2005-10-29 devnull {
691 5cdb1798 2005-10-29 devnull while(*p && *p != ';')
692 5cdb1798 2005-10-29 devnull p++;
693 5cdb1798 2005-10-29 devnull while(*p == ';' || isspace(*p))
694 5cdb1798 2005-10-29 devnull p++;
695 5cdb1798 2005-10-29 devnull return p;
696 5cdb1798 2005-10-29 devnull }
697 5cdb1798 2005-10-29 devnull
698 5cdb1798 2005-10-29 devnull /*
699 5cdb1798 2005-10-29 devnull * parse a possibly "'d string from a header. A
700 5cdb1798 2005-10-29 devnull * ';' terminates the string.
701 5cdb1798 2005-10-29 devnull */
702 5cdb1798 2005-10-29 devnull static char*
703 5cdb1798 2005-10-29 devnull getstring(char *p, String *s, int dolower)
704 5cdb1798 2005-10-29 devnull {
705 5cdb1798 2005-10-29 devnull s = s_reset(s);
706 5cdb1798 2005-10-29 devnull p = skipwhite(p);
707 5cdb1798 2005-10-29 devnull if(*p == '"'){
708 5cdb1798 2005-10-29 devnull p++;
709 5cdb1798 2005-10-29 devnull for(;*p && *p != '"'; p++)
710 5cdb1798 2005-10-29 devnull if(dolower)
711 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
712 5cdb1798 2005-10-29 devnull else
713 5cdb1798 2005-10-29 devnull s_putc(s, *p);
714 5cdb1798 2005-10-29 devnull if(*p == '"')
715 5cdb1798 2005-10-29 devnull p++;
716 5cdb1798 2005-10-29 devnull s_terminate(s);
717 5cdb1798 2005-10-29 devnull
718 5cdb1798 2005-10-29 devnull return p;
719 5cdb1798 2005-10-29 devnull }
720 5cdb1798 2005-10-29 devnull
721 5cdb1798 2005-10-29 devnull for(; *p && !isspace(*p) && *p != ';'; p++)
722 5cdb1798 2005-10-29 devnull if(dolower)
723 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
724 5cdb1798 2005-10-29 devnull else
725 5cdb1798 2005-10-29 devnull s_putc(s, *p);
726 5cdb1798 2005-10-29 devnull s_terminate(s);
727 5cdb1798 2005-10-29 devnull
728 5cdb1798 2005-10-29 devnull return p;
729 5cdb1798 2005-10-29 devnull }
730 5cdb1798 2005-10-29 devnull
731 5cdb1798 2005-10-29 devnull static void
732 5cdb1798 2005-10-29 devnull init_hdefs(void)
733 5cdb1798 2005-10-29 devnull {
734 5cdb1798 2005-10-29 devnull Hdef *hd;
735 5cdb1798 2005-10-29 devnull static int already;
736 5cdb1798 2005-10-29 devnull
737 5cdb1798 2005-10-29 devnull if(already)
738 5cdb1798 2005-10-29 devnull return;
739 5cdb1798 2005-10-29 devnull already = 1;
740 5cdb1798 2005-10-29 devnull
741 5cdb1798 2005-10-29 devnull for(hd = hdefs; hd->type != nil; hd++)
742 5cdb1798 2005-10-29 devnull hd->len = strlen(hd->type);
743 5cdb1798 2005-10-29 devnull }
744 5cdb1798 2005-10-29 devnull
745 5cdb1798 2005-10-29 devnull /*
746 5cdb1798 2005-10-29 devnull * create a new boundary
747 5cdb1798 2005-10-29 devnull */
748 5cdb1798 2005-10-29 devnull static String*
749 5cdb1798 2005-10-29 devnull mkboundary(void)
750 5cdb1798 2005-10-29 devnull {
751 5cdb1798 2005-10-29 devnull char buf[32];
752 5cdb1798 2005-10-29 devnull int i;
753 5cdb1798 2005-10-29 devnull static int already;
754 5cdb1798 2005-10-29 devnull
755 5cdb1798 2005-10-29 devnull if(already == 0){
756 5cdb1798 2005-10-29 devnull srand((time(0)<<16)|getpid());
757 5cdb1798 2005-10-29 devnull already = 1;
758 5cdb1798 2005-10-29 devnull }
759 5cdb1798 2005-10-29 devnull strcpy(buf, "upas-");
760 5cdb1798 2005-10-29 devnull for(i = 5; i < sizeof(buf)-1; i++)
761 5cdb1798 2005-10-29 devnull buf[i] = 'a' + nrand(26);
762 5cdb1798 2005-10-29 devnull buf[i] = 0;
763 5cdb1798 2005-10-29 devnull return s_copy(buf);
764 5cdb1798 2005-10-29 devnull }
765 5cdb1798 2005-10-29 devnull
766 5cdb1798 2005-10-29 devnull /*
767 5cdb1798 2005-10-29 devnull * skip blank lines till header
768 5cdb1798 2005-10-29 devnull */
769 5cdb1798 2005-10-29 devnull static void
770 5cdb1798 2005-10-29 devnull passnotheader(void)
771 5cdb1798 2005-10-29 devnull {
772 5cdb1798 2005-10-29 devnull char *cp;
773 5cdb1798 2005-10-29 devnull int i, n;
774 5cdb1798 2005-10-29 devnull
775 5cdb1798 2005-10-29 devnull while((cp = Brdline(&in, '\n')) != nil){
776 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
777 5cdb1798 2005-10-29 devnull for(i = 0; i < n-1; i++)
778 5cdb1798 2005-10-29 devnull if(cp[i] != ' ' && cp[i] != '\t' && cp[i] != '\r'){
779 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
780 5cdb1798 2005-10-29 devnull return;
781 5cdb1798 2005-10-29 devnull }
782 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, n);
783 5cdb1798 2005-10-29 devnull }
784 5cdb1798 2005-10-29 devnull }
785 5cdb1798 2005-10-29 devnull
786 5cdb1798 2005-10-29 devnull /*
787 5cdb1798 2005-10-29 devnull * pass unix header lines
788 5cdb1798 2005-10-29 devnull */
789 5cdb1798 2005-10-29 devnull static void
790 5cdb1798 2005-10-29 devnull passunixheader(void)
791 5cdb1798 2005-10-29 devnull {
792 5cdb1798 2005-10-29 devnull char *p;
793 5cdb1798 2005-10-29 devnull int n;
794 5cdb1798 2005-10-29 devnull
795 5cdb1798 2005-10-29 devnull while((p = Brdline(&in, '\n')) != nil){
796 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
797 5cdb1798 2005-10-29 devnull if(strncmp(p, "From ", 5) != 0){
798 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
799 5cdb1798 2005-10-29 devnull break;
800 5cdb1798 2005-10-29 devnull }
801 5cdb1798 2005-10-29 devnull Bwrite(&out, p, n);
802 5cdb1798 2005-10-29 devnull }
803 5cdb1798 2005-10-29 devnull }
804 5cdb1798 2005-10-29 devnull
805 5cdb1798 2005-10-29 devnull /*
806 5cdb1798 2005-10-29 devnull * Read mime types
807 5cdb1798 2005-10-29 devnull */
808 5cdb1798 2005-10-29 devnull static void
809 5cdb1798 2005-10-29 devnull readmtypes(void)
810 5cdb1798 2005-10-29 devnull {
811 5cdb1798 2005-10-29 devnull Biobuf *b;
812 5cdb1798 2005-10-29 devnull char *p;
813 5cdb1798 2005-10-29 devnull char *f[6];
814 5cdb1798 2005-10-29 devnull Mtype *m;
815 5cdb1798 2005-10-29 devnull Mtype **l;
816 5cdb1798 2005-10-29 devnull
817 7ce2007c 2006-02-12 devnull b = Bopen(unsharp("#9/lib/mimetype"), OREAD);
818 5cdb1798 2005-10-29 devnull if(b == nil)
819 5cdb1798 2005-10-29 devnull return;
820 5cdb1798 2005-10-29 devnull
821 5cdb1798 2005-10-29 devnull l = &mtypes;
822 5cdb1798 2005-10-29 devnull while((p = Brdline(b, '\n')) != nil){
823 5cdb1798 2005-10-29 devnull if(*p == '#')
824 5cdb1798 2005-10-29 devnull continue;
825 5cdb1798 2005-10-29 devnull p[Blinelen(b)-1] = 0;
826 5cdb1798 2005-10-29 devnull if(tokenize(p, f, nelem(f)) < 5)
827 5cdb1798 2005-10-29 devnull continue;
828 5cdb1798 2005-10-29 devnull m = mallocz(sizeof *m, 1);
829 5cdb1798 2005-10-29 devnull if(m == nil)
830 5cdb1798 2005-10-29 devnull goto err;
831 5cdb1798 2005-10-29 devnull m->ext = strdup(f[0]);
832 5cdb1798 2005-10-29 devnull if(m->ext == 0)
833 5cdb1798 2005-10-29 devnull goto err;
834 5cdb1798 2005-10-29 devnull m->gtype = strdup(f[1]);
835 5cdb1798 2005-10-29 devnull if(m->gtype == 0)
836 5cdb1798 2005-10-29 devnull goto err;
837 5cdb1798 2005-10-29 devnull m->stype = strdup(f[2]);
838 5cdb1798 2005-10-29 devnull if(m->stype == 0)
839 5cdb1798 2005-10-29 devnull goto err;
840 5cdb1798 2005-10-29 devnull m->class = *f[4];
841 5cdb1798 2005-10-29 devnull *l = m;
842 5cdb1798 2005-10-29 devnull l = &(m->next);
843 5cdb1798 2005-10-29 devnull }
844 5cdb1798 2005-10-29 devnull Bterm(b);
845 5cdb1798 2005-10-29 devnull return;
846 5cdb1798 2005-10-29 devnull err:
847 5cdb1798 2005-10-29 devnull if(m == nil)
848 5cdb1798 2005-10-29 devnull return;
849 5cdb1798 2005-10-29 devnull free(m->ext);
850 5cdb1798 2005-10-29 devnull free(m->gtype);
851 5cdb1798 2005-10-29 devnull free(m->stype);
852 5cdb1798 2005-10-29 devnull free(m);
853 5cdb1798 2005-10-29 devnull Bterm(b);
854 5cdb1798 2005-10-29 devnull }
855 5cdb1798 2005-10-29 devnull
856 5cdb1798 2005-10-29 devnull /*
857 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y', accept it
858 5cdb1798 2005-10-29 devnull * if the class is 'p' check a previous extension
859 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
860 5cdb1798 2005-10-29 devnull */
861 5cdb1798 2005-10-29 devnull static int
862 5cdb1798 2005-10-29 devnull badfile(char *name)
863 5cdb1798 2005-10-29 devnull {
864 5cdb1798 2005-10-29 devnull char *p;
865 5cdb1798 2005-10-29 devnull Mtype *m;
866 5cdb1798 2005-10-29 devnull int rv;
867 5cdb1798 2005-10-29 devnull
868 5cdb1798 2005-10-29 devnull p = strrchr(name, '.');
869 5cdb1798 2005-10-29 devnull if(p == nil)
870 5cdb1798 2005-10-29 devnull return 0;
871 5cdb1798 2005-10-29 devnull
872 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next)
873 5cdb1798 2005-10-29 devnull if(cistrcmp(p, m->ext) == 0){
874 5cdb1798 2005-10-29 devnull switch(m->class){
875 5cdb1798 2005-10-29 devnull case 'm':
876 5cdb1798 2005-10-29 devnull case 'y':
877 5cdb1798 2005-10-29 devnull return 0;
878 5cdb1798 2005-10-29 devnull case 'p':
879 5cdb1798 2005-10-29 devnull *p = 0;
880 5cdb1798 2005-10-29 devnull rv = badfile(name);
881 5cdb1798 2005-10-29 devnull *p = '.';
882 5cdb1798 2005-10-29 devnull return rv;
883 5cdb1798 2005-10-29 devnull case 'r':
884 5cdb1798 2005-10-29 devnull return 2;
885 5cdb1798 2005-10-29 devnull }
886 5cdb1798 2005-10-29 devnull }
887 5cdb1798 2005-10-29 devnull return 1;
888 5cdb1798 2005-10-29 devnull }
889 5cdb1798 2005-10-29 devnull
890 5cdb1798 2005-10-29 devnull /*
891 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y' or 'p', accept it
892 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
893 5cdb1798 2005-10-29 devnull */
894 5cdb1798 2005-10-29 devnull static int
895 5cdb1798 2005-10-29 devnull badtype(char *type)
896 5cdb1798 2005-10-29 devnull {
897 5cdb1798 2005-10-29 devnull Mtype *m;
898 5cdb1798 2005-10-29 devnull char *s, *fix;
899 5cdb1798 2005-10-29 devnull int rv = 1;
900 5cdb1798 2005-10-29 devnull
901 5cdb1798 2005-10-29 devnull fix = s = strchr(type, '/');
902 5cdb1798 2005-10-29 devnull if(s != nil)
903 5cdb1798 2005-10-29 devnull *s++ = 0;
904 5cdb1798 2005-10-29 devnull else
905 5cdb1798 2005-10-29 devnull s = "-";
906 5cdb1798 2005-10-29 devnull
907 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next){
908 5cdb1798 2005-10-29 devnull if(cistrcmp(type, m->gtype) != 0)
909 5cdb1798 2005-10-29 devnull continue;
910 5cdb1798 2005-10-29 devnull if(cistrcmp(s, m->stype) != 0)
911 5cdb1798 2005-10-29 devnull continue;
912 5cdb1798 2005-10-29 devnull switch(m->class){
913 5cdb1798 2005-10-29 devnull case 'y':
914 5cdb1798 2005-10-29 devnull case 'p':
915 5cdb1798 2005-10-29 devnull case 'm':
916 5cdb1798 2005-10-29 devnull rv = 0;
917 5cdb1798 2005-10-29 devnull break;
918 5cdb1798 2005-10-29 devnull }
919 5cdb1798 2005-10-29 devnull break;
920 5cdb1798 2005-10-29 devnull }
921 5cdb1798 2005-10-29 devnull
922 5cdb1798 2005-10-29 devnull if(fix != nil)
923 5cdb1798 2005-10-29 devnull *fix = '/';
924 5cdb1798 2005-10-29 devnull return rv;
925 5cdb1798 2005-10-29 devnull }
926 5cdb1798 2005-10-29 devnull
927 5cdb1798 2005-10-29 devnull /* rfc2047 non-ascii */
928 5cdb1798 2005-10-29 devnull typedef struct Charset Charset;
929 5cdb1798 2005-10-29 devnull struct Charset {
930 5cdb1798 2005-10-29 devnull char *name;
931 5cdb1798 2005-10-29 devnull int len;
932 5cdb1798 2005-10-29 devnull int convert;
933 5cdb1798 2005-10-29 devnull } charsets[] =
934 5cdb1798 2005-10-29 devnull {
935 5cdb1798 2005-10-29 devnull { "us-ascii", 8, 1, },
936 5cdb1798 2005-10-29 devnull { "utf-8", 5, 0, },
937 cbeb0b26 2006-04-01 devnull { "iso-8859-1", 10, 1, }
938 5cdb1798 2005-10-29 devnull };
939 5cdb1798 2005-10-29 devnull
940 5cdb1798 2005-10-29 devnull /*
941 5cdb1798 2005-10-29 devnull * convert to UTF if need be
942 5cdb1798 2005-10-29 devnull */
943 5cdb1798 2005-10-29 devnull static String*
944 5cdb1798 2005-10-29 devnull tokenconvert(String *t)
945 5cdb1798 2005-10-29 devnull {
946 5cdb1798 2005-10-29 devnull String *s;
947 5cdb1798 2005-10-29 devnull char decoded[1024];
948 5cdb1798 2005-10-29 devnull char utfbuf[2*1024];
949 5cdb1798 2005-10-29 devnull int i, len;
950 5cdb1798 2005-10-29 devnull char *e;
951 5cdb1798 2005-10-29 devnull char *token;
952 5cdb1798 2005-10-29 devnull
953 5cdb1798 2005-10-29 devnull token = s_to_c(t);
954 5cdb1798 2005-10-29 devnull len = s_len(t);
955 5cdb1798 2005-10-29 devnull
956 5cdb1798 2005-10-29 devnull if(token[0] != '=' || token[1] != '?' ||
957 5cdb1798 2005-10-29 devnull token[len-2] != '?' || token[len-1] != '=')
958 5cdb1798 2005-10-29 devnull goto err;
959 5cdb1798 2005-10-29 devnull e = token+len-2;
960 5cdb1798 2005-10-29 devnull token += 2;
961 5cdb1798 2005-10-29 devnull
962 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the character set */
963 5cdb1798 2005-10-29 devnull for(i = 0; i < nelem(charsets); i++)
964 5cdb1798 2005-10-29 devnull if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
965 5cdb1798 2005-10-29 devnull if(token[charsets[i].len] == '?'){
966 5cdb1798 2005-10-29 devnull token += charsets[i].len + 1;
967 5cdb1798 2005-10-29 devnull break;
968 5cdb1798 2005-10-29 devnull }
969 5cdb1798 2005-10-29 devnull if(i >= nelem(charsets))
970 5cdb1798 2005-10-29 devnull goto err;
971 5cdb1798 2005-10-29 devnull
972 cbeb0b26 2006-04-01 devnull /* bail if it doesn't fit */
973 5cdb1798 2005-10-29 devnull if(strlen(token) > sizeof(decoded)-1)
974 5cdb1798 2005-10-29 devnull goto err;
975 5cdb1798 2005-10-29 devnull
976 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the encoding */
977 5cdb1798 2005-10-29 devnull if(cistrncmp(token, "b?", 2) == 0){
978 5cdb1798 2005-10-29 devnull token += 2;
979 5cdb1798 2005-10-29 devnull len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
980 5cdb1798 2005-10-29 devnull decoded[len] = 0;
981 5cdb1798 2005-10-29 devnull } else if(cistrncmp(token, "q?", 2) == 0){
982 5cdb1798 2005-10-29 devnull token += 2;
983 5cdb1798 2005-10-29 devnull len = decquoted(decoded, token, e);
984 5cdb1798 2005-10-29 devnull if(len > 0 && decoded[len-1] == '\n')
985 5cdb1798 2005-10-29 devnull len--;
986 5cdb1798 2005-10-29 devnull decoded[len] = 0;
987 5cdb1798 2005-10-29 devnull } else
988 5cdb1798 2005-10-29 devnull goto err;
989 5cdb1798 2005-10-29 devnull
990 5cdb1798 2005-10-29 devnull s = nil;
991 5cdb1798 2005-10-29 devnull switch(charsets[i].convert){
992 5cdb1798 2005-10-29 devnull case 0:
993 5cdb1798 2005-10-29 devnull s = s_copy(decoded);
994 5cdb1798 2005-10-29 devnull break;
995 5cdb1798 2005-10-29 devnull case 1:
996 5cdb1798 2005-10-29 devnull s = s_new();
997 5cdb1798 2005-10-29 devnull latin1toutf(utfbuf, decoded, decoded+len);
998 5cdb1798 2005-10-29 devnull s_append(s, utfbuf);
999 5cdb1798 2005-10-29 devnull break;
1000 5cdb1798 2005-10-29 devnull }
1001 5cdb1798 2005-10-29 devnull
1002 5cdb1798 2005-10-29 devnull return s;
1003 5cdb1798 2005-10-29 devnull err:
1004 5cdb1798 2005-10-29 devnull return s_clone(t);
1005 5cdb1798 2005-10-29 devnull }
1006 5cdb1798 2005-10-29 devnull
1007 5cdb1798 2005-10-29 devnull /*
1008 5cdb1798 2005-10-29 devnull * decode quoted
1009 5cdb1798 2005-10-29 devnull */
1010 5cdb1798 2005-10-29 devnull enum
1011 5cdb1798 2005-10-29 devnull {
1012 5cdb1798 2005-10-29 devnull Self= 1,
1013 cbeb0b26 2006-04-01 devnull Hex= 2
1014 5cdb1798 2005-10-29 devnull };
1015 5cdb1798 2005-10-29 devnull uchar tableqp[256];
1016 5cdb1798 2005-10-29 devnull
1017 5cdb1798 2005-10-29 devnull static void
1018 5cdb1798 2005-10-29 devnull initquoted(void)
1019 5cdb1798 2005-10-29 devnull {
1020 5cdb1798 2005-10-29 devnull int c;
1021 5cdb1798 2005-10-29 devnull
1022 5cdb1798 2005-10-29 devnull memset(tableqp, 0, 256);
1023 5cdb1798 2005-10-29 devnull for(c = ' '; c <= '<'; c++)
1024 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1025 5cdb1798 2005-10-29 devnull for(c = '>'; c <= '~'; c++)
1026 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1027 5cdb1798 2005-10-29 devnull tableqp['\t'] = Self;
1028 5cdb1798 2005-10-29 devnull tableqp['='] = Hex;
1029 5cdb1798 2005-10-29 devnull }
1030 5cdb1798 2005-10-29 devnull
1031 5cdb1798 2005-10-29 devnull static int
1032 5cdb1798 2005-10-29 devnull hex2int(int x)
1033 5cdb1798 2005-10-29 devnull {
1034 5cdb1798 2005-10-29 devnull if(x >= '0' && x <= '9')
1035 5cdb1798 2005-10-29 devnull return x - '0';
1036 5cdb1798 2005-10-29 devnull if(x >= 'A' && x <= 'F')
1037 5cdb1798 2005-10-29 devnull return (x - 'A') + 10;
1038 5cdb1798 2005-10-29 devnull if(x >= 'a' && x <= 'f')
1039 5cdb1798 2005-10-29 devnull return (x - 'a') + 10;
1040 5cdb1798 2005-10-29 devnull return 0;
1041 5cdb1798 2005-10-29 devnull }
1042 5cdb1798 2005-10-29 devnull
1043 5cdb1798 2005-10-29 devnull static char*
1044 5cdb1798 2005-10-29 devnull decquotedline(char *out, char *in, char *e)
1045 5cdb1798 2005-10-29 devnull {
1046 5cdb1798 2005-10-29 devnull int c, soft;
1047 5cdb1798 2005-10-29 devnull
1048 5cdb1798 2005-10-29 devnull /* dump trailing white space */
1049 5cdb1798 2005-10-29 devnull while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
1050 5cdb1798 2005-10-29 devnull e--;
1051 5cdb1798 2005-10-29 devnull
1052 5cdb1798 2005-10-29 devnull /* trailing '=' means no newline */
1053 5cdb1798 2005-10-29 devnull if(*e == '='){
1054 5cdb1798 2005-10-29 devnull soft = 1;
1055 5cdb1798 2005-10-29 devnull e--;
1056 5cdb1798 2005-10-29 devnull } else
1057 5cdb1798 2005-10-29 devnull soft = 0;
1058 5cdb1798 2005-10-29 devnull
1059 5cdb1798 2005-10-29 devnull while(in <= e){
1060 5cdb1798 2005-10-29 devnull c = (*in++) & 0xff;
1061 5cdb1798 2005-10-29 devnull switch(tableqp[c]){
1062 5cdb1798 2005-10-29 devnull case Self:
1063 5cdb1798 2005-10-29 devnull *out++ = c;
1064 5cdb1798 2005-10-29 devnull break;
1065 5cdb1798 2005-10-29 devnull case Hex:
1066 5cdb1798 2005-10-29 devnull c = hex2int(*in++)<<4;
1067 5cdb1798 2005-10-29 devnull c |= hex2int(*in++);
1068 5cdb1798 2005-10-29 devnull *out++ = c;
1069 5cdb1798 2005-10-29 devnull break;
1070 5cdb1798 2005-10-29 devnull }
1071 5cdb1798 2005-10-29 devnull }
1072 5cdb1798 2005-10-29 devnull if(!soft)
1073 5cdb1798 2005-10-29 devnull *out++ = '\n';
1074 5cdb1798 2005-10-29 devnull *out = 0;
1075 5cdb1798 2005-10-29 devnull
1076 5cdb1798 2005-10-29 devnull return out;
1077 5cdb1798 2005-10-29 devnull }
1078 5cdb1798 2005-10-29 devnull
1079 5cdb1798 2005-10-29 devnull static int
1080 5cdb1798 2005-10-29 devnull decquoted(char *out, char *in, char *e)
1081 5cdb1798 2005-10-29 devnull {
1082 5cdb1798 2005-10-29 devnull char *p, *nl;
1083 5cdb1798 2005-10-29 devnull
1084 5cdb1798 2005-10-29 devnull if(tableqp[' '] == 0)
1085 5cdb1798 2005-10-29 devnull initquoted();
1086 5cdb1798 2005-10-29 devnull
1087 5cdb1798 2005-10-29 devnull p = out;
1088 5cdb1798 2005-10-29 devnull while((nl = strchr(in, '\n')) != nil && nl < e){
1089 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, nl);
1090 5cdb1798 2005-10-29 devnull in = nl + 1;
1091 5cdb1798 2005-10-29 devnull }
1092 5cdb1798 2005-10-29 devnull if(in < e)
1093 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, e-1);
1094 5cdb1798 2005-10-29 devnull
1095 cbeb0b26 2006-04-01 devnull /* make sure we end with a new line */
1096 5cdb1798 2005-10-29 devnull if(*(p-1) != '\n'){
1097 5cdb1798 2005-10-29 devnull *p++ = '\n';
1098 5cdb1798 2005-10-29 devnull *p = 0;
1099 5cdb1798 2005-10-29 devnull }
1100 5cdb1798 2005-10-29 devnull
1101 5cdb1798 2005-10-29 devnull return p - out;
1102 5cdb1798 2005-10-29 devnull }
1103 5cdb1798 2005-10-29 devnull
1104 5cdb1798 2005-10-29 devnull /* translate latin1 directly since it fits neatly in utf */
1105 5cdb1798 2005-10-29 devnull static int
1106 5cdb1798 2005-10-29 devnull latin1toutf(char *out, char *in, char *e)
1107 5cdb1798 2005-10-29 devnull {
1108 5cdb1798 2005-10-29 devnull Rune r;
1109 5cdb1798 2005-10-29 devnull char *p;
1110 5cdb1798 2005-10-29 devnull
1111 5cdb1798 2005-10-29 devnull p = out;
1112 5cdb1798 2005-10-29 devnull for(; in < e; in++){
1113 5cdb1798 2005-10-29 devnull r = (*in) & 0xff;
1114 5cdb1798 2005-10-29 devnull p += runetochar(p, &r);
1115 5cdb1798 2005-10-29 devnull }
1116 5cdb1798 2005-10-29 devnull *p = 0;
1117 5cdb1798 2005-10-29 devnull return p - out;
1118 5cdb1798 2005-10-29 devnull }