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