1 /*:ts=8*/
2 /*****************************************************************************
3 * FIDOGATE --- Gateway UNIX Mail/News <-> FIDO NetMail/EchoMail
4 *
5 *
6 * Functions to process RFC822 header lines from messages
7 *
8 *****************************************************************************
9 * Copyright (C) 1990-2002
10 * _____ _____
11 * | |___ | Martin Junius <mj@fidogate.org>
12 * | | | | | | Radiumstr. 18
13 * |_|_|_|@home| D-51069 Koeln, Germany
14 *
15 * This file is part of FIDOGATE.
16 *
17 * FIDOGATE is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by the
19 * Free Software Foundation; either version 2, or (at your option) any
20 * later version.
21 *
22 * FIDOGATE is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with FIDOGATE; see the file COPYING. If not, write to the Free
29 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
30 *****************************************************************************/
31
32 #include "fidogate.h"
33
34 /*
35 * header_ca_rfc() --- Output ^ARFC-Xxxx kludges
36 */
header_ca_rfc(RFCHeader * header,FILE * out,int rfc_level)37 void header_ca_rfc(RFCHeader *header, FILE * out, int rfc_level)
38 {
39 static char *rfc_lvl_1[] = { RFC_LVL_1_HEADERS, NULL };
40 static char *rfc_lvl_3[] = { RFC_LVL_3_HEADERS, NULL };
41 Textlist *headers = &header->headers;
42
43 /* RFC level 0 - no ^ARFC-Xxxx kludges */
44 if (rfc_level <= 0) {
45 return;
46 }
47
48 /* RFC level 1 - selected set of ^ARFC-Xxxx kludges */
49 else if (rfc_level == 1) {
50 char **name;
51 Textline *p;
52 int len;
53 int ok = FALSE;
54
55 for (p = headers->first; p; p = p->next) {
56 if (*p->line && !is_space(p->line[0])) {
57 ok = FALSE;
58 for (name = rfc_lvl_1; *name; name++) {
59 len = strlen(*name);
60 if (!strnicmp(p->line, *name, len)
61 && (p->line[len] == ':' || p->line[len] == ' ')) {
62 ok = TRUE; /* OK to output */
63 break;
64 }
65 }
66 }
67 if (ok)
68 #ifndef RECODE_ALL_RFC
69 fprintf(out, "\001RFC-%s\r\n", p->line);
70 #else
71 fprintf(out, "\001RFC-%s\r\n", xlat_s(p->line, NULL));
72 #endif /* RECODE_ALL_RFC */
73 }
74 }
75
76 /* RFC level 2 - all ^ARFC-Xxxx kludges */
77 else if (rfc_level == 2) {
78 Textline *p;
79 char *crlf;
80 int i;
81
82 crlf = "";
83 for (p = headers->first; p; p = p->next) {
84 if (*p->line && !is_space(p->line[0])) {
85 #ifndef RECODE_ALL_RFC
86 fprintf(out, "%s\001RFC-%s", crlf, p->line);
87 #else
88 fprintf(out, "%s\001RFC-%s", crlf, xlat_s(p->line, NULL));
89 #endif /* RECODE_ALL_RFC */
90 } else {
91 for (i = 0; is_space(p->line[i]); i++) ;
92 fprintf(out, " %s", &(p->line[i]));
93 }
94 crlf = "\r\n";
95 }
96 fprintf(out, "%s", crlf);
97 }
98
99 /* RFC level 3 - all ^ARFC-Xxxx kludges, excluding some */
100 else if (rfc_level >= 3) {
101 Textline *p;
102 char **name;
103 int len;
104 int ok = FALSE;
105 char *crlf;
106 int i;
107
108 crlf = "";
109 for (p = headers->first; p; p = p->next) {
110 if (*p->line && !is_space(p->line[0])) {
111 ok = FALSE;
112 for (name = rfc_lvl_3; *name; name++) {
113 len = strlen(*name);
114 if (!strnicmp(p->line, *name, len)
115 && (p->line[len] == ':' || p->line[len] == ' ')) {
116 ok = TRUE;
117 break;
118 }
119 }
120 if (ok)
121 #ifndef RECODE_ALL_RFC
122 fprintf(out, "%s\001RFC-%s", crlf, p->line);
123 #else
124 fprintf(out, "%s\001RFC-%s", crlf, xlat_s(p->line, NULL));
125 #endif /* RECODE_ALL_RFC */
126
127 } else {
128 if (ok) {
129 for (i = 0; is_space(p->line[i]); i++) ;
130 fprintf(out, " %s", &(p->line[i]));
131 }
132 }
133 if (ok)
134 crlf = "\r\n";
135 }
136 fprintf(out, "%s", crlf);
137 }
138
139 return;
140 }
141
header_free(RFCHeader * header)142 void header_free(RFCHeader *header)
143 {
144 tl_clear(&header->headers);
145 free(header);
146 }
147
header_new(Textlist * tl)148 static RFCHeader *header_new(Textlist *tl)
149 {
150 RFCHeader *header;
151
152 header = xmalloc(sizeof(*header));
153 memset(header, 0, sizeof(*header));
154
155 if (tl != NULL)
156 header->headers = *tl;
157
158 return header;
159 }
160
header_append(RFCHeader * header,char * line)161 static void header_append(RFCHeader *header, char *line)
162 {
163 tl_append(&header->headers, line);
164 }
165
166 /*
167 * header_read() --- read header lines from file
168 */
header_read(FILE * file)169 RFCHeader *header_read(FILE *file)
170 {
171 static char buf[BUFFERSIZE];
172 static char queue[BUFFERSIZE];
173 short int first = TRUE;
174 Textlist *headers;
175 RFCHeader *header;
176
177 header = xmalloc(sizeof(*header));
178 memset(header, 0, sizeof(*header));
179
180 headers = &header->headers;
181 queue[0] = '\0';
182
183 while (read_line(buf, sizeof(buf), file)) {
184 if (*buf == '\r' || *buf == '\n')
185 break;
186 strip_crlf(buf);
187 if (is_blank(buf[0])) {
188 BUF_APPEND(queue, buf);
189 } else {
190 if (!first)
191 tl_append(headers, queue);
192 else
193 first = FALSE;
194 BUF_COPY(queue, buf);
195 }
196 }
197 if (strlen(queue) > 1)
198 tl_append(headers, queue);
199
200 return header;
201 }
202
203 /*
204 * header_read_list() --- read header lines from Textlist
205 */
header_read_list(Textlist * body)206 RFCHeader *header_read_list(Textlist *body)
207 {
208 static char buf[BUFFERSIZE];
209 static char queue[BUFFERSIZE];
210 short int first = TRUE;
211 Textline *line;
212 Textlist *headers;
213 RFCHeader *header;
214
215 if (body == NULL)
216 return NULL;
217
218 header = xmalloc(sizeof(*header));
219 memset(header, 0, sizeof(*header));
220
221 headers = &header->headers;
222
223 queue[0] = '\0';
224
225 for (line = body->first; line != NULL; line = line->next) {
226 strncpy(buf, line->line, BUFFERSIZE - 1);
227 buf[BUFFERSIZE - 1] = '\0';
228 if (*buf == '\r' || *buf == '\n')
229 break;
230 strip_crlf(buf);
231 if (is_blank(buf[0])) {
232 BUF_APPEND(queue, buf);
233 } else {
234 if (!first)
235 tl_append(headers, queue);
236 else
237 first = FALSE;
238 BUF_COPY(queue, buf);
239 }
240 }
241 if (strlen(queue) > 1)
242 tl_append(headers, queue);
243
244 return header;
245 }
246
247 /* delete rfc header from the beginning of body */
248
header_delete_from_body(Textlist * body)249 int header_delete_from_body(Textlist * body)
250 {
251 char *buf;
252 Textline *line;
253
254 if (body == NULL)
255 return ERROR;
256
257 for (line = body->first;; line = body->first) {
258 buf = line->line;
259 if (*buf == '\r' || *buf == '\n') {
260 tl_delete(body, line);
261 break;
262 }
263 tl_delete(body, line);
264 }
265 return OK;
266 }
267
268 /*
269 * header_hops() --- return # of hops (Received headers) of message
270 */
header_hops(RFCHeader * header)271 int header_hops(RFCHeader *header)
272 {
273 char *name = "Received";
274 Textline *p;
275 int len, hops;
276 Textlist *headers = &header->headers;
277
278 len = strlen(name);
279 hops = 0;
280
281 for (p = headers->first; p; p = p->next) {
282 #ifdef RECEIVED_BY_MAILER
283 if (!strnicmp(p->line, RECEIVED_BY_MAILER, strlen(RECEIVED_BY_MAILER)))
284 continue;
285 #endif /* RECEIVED_BY_MAILER */
286 if (!strnicmp(p->line, name, len) && p->line[len] == ':')
287 hops++;
288 }
289
290 return hops;
291 }
292
header_get_from_tl(RFCHeader * header,Textlist * tl,char * name)293 static char *header_get_from_tl(RFCHeader *header, Textlist *tl, char *name)
294 {
295 Textline *p;
296 int len;
297 char *s;
298
299 len = strlen(name);
300
301 for (p = tl->first; p; p = p->next) {
302 if (!strnicmp(p->line, name, len) && p->line[len] == ':') {
303 for (s = p->line + len + 1; is_space(*s); s++) ;
304 if (header != NULL)
305 header->last_header = p;
306 return s;
307 }
308 }
309
310 if (header != NULL)
311 header->last_header = NULL;
312 return NULL;
313 }
314
315 /* Keep for legacy ftn2rfc code */
rfcheader_get(Textlist * tl,char * name)316 char *rfcheader_get(Textlist *tl, char *name)
317 {
318 return header_get_from_tl(NULL, tl, name);
319 }
320
header_get(RFCHeader * header,char * name)321 char *header_get(RFCHeader *header, char *name)
322 {
323 if (header == NULL)
324 return NULL;
325 return header_get_from_tl(header, &header->headers, name);
326 }
327
328 /*
329 * header_geth() --- get 1st/next header line from Textlist
330 *
331 * Modi: name="X", first=TRUE return 1st X header line
332 * name=NULL, first=FALSE return continuation header lines only
333 * name="X", first=FALSE return continuation header lines and
334 * new X header lines
335 *
336 * Return: contents of header lines or NULL.
337 */
header_geth(RFCHeader * header,char * name,int first)338 char *header_geth(RFCHeader *header, char *name, int first)
339 {
340 static Textline *p_last;
341 Textline *p;
342 int len;
343 char *s;
344 Textlist *tl = &header->headers;
345
346 if (header == NULL)
347 return NULL;
348
349 if (first) {
350 /* Restart search */
351 p_last = NULL;
352 p = tl->first;
353 } else if (p_last) {
354 /* Continue search */
355 p_last = p_last->next;
356 p = p_last;
357 /* Check for continuation header, white space at start of line */
358 if (p_last && is_space(p_last->line[0])) {
359 for (s = p_last->line; is_space(*s); s++) ;
360 return s;
361 }
362 } else {
363 p = NULL;
364 }
365
366 /* If p or name is NULL, stop here */
367 if (!p || !name) {
368 p_last = NULL;
369 return NULL;
370 }
371
372 /* Search for header line starting with NAME: */
373 len = strlen(name);
374 for (; p; p = p->next) {
375 if (!strnicmp(p->line, name, len) && p->line[len] == ':') {
376 for (s = p->line + len + 1; is_space(*s); s++) ;
377 p_last = p;
378 return s;
379 }
380 }
381
382 p_last = NULL;
383 return NULL;
384 }
385
386 /*
387 * header_getnext() --- get next header line
388 */
389
header_getnext(RFCHeader * header)390 char *header_getnext(RFCHeader *header)
391 {
392 char *s;
393
394 if (header->last_header == NULL)
395 return NULL;
396
397 header->last_header = header->last_header->next;
398 if (header->last_header == NULL)
399 return NULL;
400 if (!is_space(header->last_header->line[0])) {
401 header->last_header = NULL;
402 return NULL;
403 }
404
405 for (s = header->last_header->line; is_space(*s); s++) ;
406 return s;
407 }
408
409 /*
410 * Return complete header line, concat continuation lines if necessary.
411 */
412 #define TMPS_LEN_GETCOMPLETE BUFFERSIZE
413
s_header_getcomplete(RFCHeader * header,char * name)414 char *s_header_getcomplete(RFCHeader *header, char *name)
415 {
416 char *p;
417 TmpS *s;
418
419 if ((p = header_get(header, name))) {
420 s = tmps_alloc(TMPS_LEN_GETCOMPLETE);
421 str_copy(s->s, s->len, p);
422
423 while ((p = header_getnext(header))) {
424 str_append(s->s, s->len, " ");
425 str_append(s->s, s->len, p);
426 }
427
428 tmps_stripsize(s);
429 return s->s;
430 }
431
432 return NULL;
433 }
434
435 /*
436 * Change header line
437 */
438
header_alter(RFCHeader * header,char * name,char * newval)439 int header_alter(RFCHeader *header, char *name, char *newval)
440 {
441 Textline *line;
442 char *new_header;
443 Textlist *tl = &header->headers;
444
445 if (header == NULL || name == NULL)
446 return ERROR;
447
448 for (line = tl->first; line != NULL; line = line->next) {
449 if (!strneq(line->line, name, strlen(name)))
450 continue;
451
452 if (newval == NULL) {
453 tl_delete(tl, line);
454 return OK;
455 }
456 /* name + ": " + newval + '\n' */
457 new_header = xmalloc(strlen(name) + 2 + strlen(newval) + 1);
458 strcpy(new_header, name);
459 strcat(new_header, ": ");
460 strcat(new_header, newval);
461 xfree(line->line);
462 line->line = new_header;
463 return OK;
464 }
465 return ERROR;
466 }
467
468 /*
469 * addr_token() --- get addresses from string (',' separated)
470 */
addr_token(char * line)471 char *addr_token(char *line)
472 {
473 static char *save_line = NULL;
474 static char *save_p = NULL;
475 int level;
476 char *s, *p;
477
478 if (line) {
479 /*
480 * First call
481 */
482 xfree(save_line);
483 save_p = save_line = strsave(line);
484 }
485
486 if (save_p == NULL)
487 return NULL;
488 if (!*save_p) {
489 save_p = NULL;
490 return NULL;
491 }
492
493 level = 0;
494 for (p = s = save_p; *p; p++) {
495 if (*p == '(')
496 level++;
497 if (*p == ')')
498 level--;
499 if (*p == ',' && level <= 0)
500 break;
501 }
502 if (*p)
503 *p++ = 0;
504 save_p = p;
505
506 return s;
507 }
508
509 /*
510 * Decodes the mimed headers and recodes them to @to charset
511 * (source charset is part of the mime encoding)
512 */
header_decode(RFCHeader * header,char * to,char * ch_fallback)513 RFCHeader *header_decode(RFCHeader *header, char *to, char *ch_fallback)
514 {
515 TextlistIterator iter;
516 Textline *line;
517 RFCHeader *decoded;
518
519 decoded = header_new(NULL);
520
521 tl_iterator_start(&iter, &header->headers);
522 line = tl_iterator_next(&iter);
523
524 for (; line; line = tl_iterator_next(&iter)) {
525
526 mime_header_dec(buffer, sizeof(buffer), line->line,
527 to, ch_fallback, header);
528 header_append(decoded, buffer);
529 }
530
531 return decoded;
532 }
533