a2b8ae9a4049fa50f7df81311fff78a28a40b54e
[mmh] / sbr / m_getfld2.c
1 /*
2 ** m_getfld2 -- replacement for m_getfld()
3 **              read an RFC 822 message
4 */
5
6 #define _WITH_GETLINE
7 #define _POSIX_C_SOURCE 200809L
8
9 #include <h/mh.h>
10 #include <h/utils.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <stdbool.h>
14
15
16 enum threestate {
17         B_TRUE = 1,
18         B_FALSE = 0,
19         FAIL = -1,
20 };
21
22 /*
23 ** static prototypes
24 */
25 static enum threestate is_falted(FILE *);
26 static size_t copyname(char *, char *);
27 static bool is_separator(char *);
28
29
30 /*
31 ** FLD2:        We read a (complete) header field
32 ** BODY2:       We read a body line
33 ** LENERR2:     Line is too long (>998, as defined by RFC 822)
34 ** FMTERR2:     Header field invalid
35 ** IOERR2:      Failure to read
36 ** FILEEOF2:    We're at the end of the file
37 **
38 ** f->name is only filled in FLD2.
39 **
40 ** In FLD2, f->value contains the field's (complete) value only;
41 ** in BODY2, LENERR2 and FMTERR2 f->value contains the whole line;
42 ** in IOERR2 and FILEEOF2 f->value is not set.
43 */
44 enum state
45 m_getfld2(enum state s, struct field *f, FILE *msg)
46 {
47         char *tmpline = NULL;
48         size_t len = 0;
49         ssize_t nchars;
50         enum threestate falted;
51
52         switch (s) {
53         case FLD2:
54                 nchars = getline(&tmpline, &len, msg);
55                 if (nchars < 1) {
56                         if (feof(msg)) {
57                                 return FILEEOF2;
58                         } else {
59                                 return IOERR2;
60                         }
61                 }
62
63                 *f->name = '\0';
64                 f->namelen = 0;
65
66                 if (nchars >= NAMESZ) {
67                         if (f->value) {
68                                 free(f->value);
69                         }
70                         f->value = tmpline;
71                         f->valuelen = nchars;
72                         return LENERR2;
73                 }
74
75                 if (*(tmpline + nchars - 1) != '\n') {
76                         if (f->value) {
77                                 free(f->value);
78                         }
79                         f->value = tmpline;
80                         f->valuelen = nchars;
81                         f->alloclen = len;
82                         return FMTERR2;
83                 }
84
85                 if (is_separator(tmpline)) {
86                         /* header/body separator found */
87                         free(tmpline);
88                         return m_getfld2(BODY2, f, msg);
89                 }
90
91                 f->namelen = copyname(f->name, tmpline);
92                 if (f->namelen < 1) {
93                         if (f->value) {
94                                 free(f->value);
95                         }
96                         f->value = tmpline;
97                         f->valuelen = nchars;
98                         f->alloclen = len;
99                         return FMTERR2;
100                 }
101
102                 /* copy the field's value */
103                 if (f->alloclen <= nchars - f->namelen) {
104                         f->value = mh_xrealloc(f->value, f->alloclen + len);
105                         f->alloclen += len;
106                 }
107                 strcpy(f->value, tmpline + f->namelen + 1);
108                 f->valuelen = nchars - f->namelen - 1;
109
110                 while ((falted = is_falted(msg)) == B_TRUE) {
111                         nchars = getline(&tmpline, &len, msg);
112                         if (nchars <= 0) {
113                                 free(tmpline);
114                                 return IOERR2;
115                         }
116
117                         if (nchars >= NAMESZ) {
118                                 if (f->value) {
119                                         free(f->value);
120                                 }
121                                 f->value = tmpline;
122                                 f->valuelen = nchars;
123                                 return LENERR2;
124                         }
125
126                         if (*(tmpline + nchars - 1) != '\n') {
127                                 if (f->value) {
128                                         free(f->value);
129                                 }
130                                 f->value = tmpline;
131                                 f->valuelen = nchars;
132                                 f->alloclen = len;
133                                 return FMTERR2;
134                         }
135
136                         if (f->alloclen - f->valuelen <= nchars) {
137                                 f->value = mh_xrealloc(f->value,
138                                                 f->alloclen + len);
139                                 f->alloclen += len;
140                         }
141                         strcpy(f->value + f->valuelen, tmpline);
142                         f->valuelen += nchars;
143
144                 }
145
146                 if (falted == FAIL) {
147                         free(tmpline);
148                         return IOERR2;
149                 }
150
151                 free(tmpline);
152                 return FLD2;
153
154         case BODY2:
155                 nchars = getline(&tmpline, &len, msg);
156                 if (nchars < 1) {
157                         if (feof(msg)) {
158                                 return FILEEOF2;
159                         } else {
160                                 return IOERR2;
161                         }
162                 }
163
164                 *f->name = '\0';
165                 f->namelen = 0;
166
167                 if (nchars >= NAMESZ) {
168                         if (f->value) {
169                                 free(f->value);
170                         }
171                         f->value = tmpline;
172                         f->valuelen = nchars;
173                         return LENERR2;
174                 }
175                 if (f->value) {
176                         free(f->value);
177                 }
178                 f->value = tmpline;
179                 f->valuelen = nchars;
180                 f->alloclen = len;
181                 return BODY2;
182
183         default:
184                 /* give error states back as received */
185                 return s;
186         }
187 }
188
189 static enum threestate
190 is_falted(FILE *msg)
191 {
192         enum threestate ret;
193         int c;
194
195         if ((c = getc(msg)) < 0) {
196                 if (feof(msg)) {
197                         clearerr(msg);
198                         return B_FALSE;
199                 } else {
200                         return FAIL;
201                 }
202         }
203         if (isblank(c)) {
204                 ret = B_TRUE;
205         } else {
206                 ret = B_FALSE;
207         }
208         if (ungetc(c, msg) != c) {
209                 return FAIL;
210         }
211         return ret;
212 }
213
214 static size_t
215 copyname(char *dst, char *src)
216 {
217         size_t len;
218         char *cp, *sep;
219
220         if (!(sep = strchr(src, ':'))) {
221                 return 0;
222         }
223         /* whitespace is forbidden in name */
224         for (cp=src; cp<sep; cp++) {
225                 if (isspace(*cp)) {
226                         return 0;
227                 }
228         }
229
230         len = sep - src;
231         if (len >= NAMESZ) {
232                 return 0;
233         }
234
235         src[len] = '\0';
236         strcpy(dst, src);
237
238         return strlen(dst);
239 }
240
241 static bool
242 is_separator(char *line)
243 {
244         /*
245         ** In MH, lines that are consists of dashes only are
246         ** separators as well ... not so in RFC 822.
247         */
248         while (*line == '-') {
249                 line++;
250         }
251         if (strcmp("\n", line) == 0 || strcmp("\r\n", line) == 0 ) {
252                 return true;
253         }
254         return false;
255 }