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