cd1c6bea09186849db1942434c796ffe0b99ec33
[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 = B_FALSE;
51         enum state ret = s;
52
53         switch (s) {
54         case FLD2:
55                 nchars = getline(&tmpline, &len, msg);
56                 if (nchars < 1) {
57                         free(f->value);
58                         *f = (struct field) { "\0", 0, NULL, 0, 0 };
59                         if (feof(msg)) {
60                                 return FILEEOF2;
61                         } else {
62                                 return IOERR2;
63                         }
64                 }
65
66                 if (nchars >= NAMESZ) {
67                         ret = LENERR2;
68                 }
69
70                 if (*(tmpline + nchars - 1) != '\n') {
71                         ret = FMTERR2;
72                 }
73
74                 if (ret == FLD2 && is_separator(tmpline)) {
75                         /* header/body separator found */
76                         free(tmpline);
77                         return m_getfld2(BODY2, f, msg);
78                 }
79
80                 f->namelen = copyname(f->name, tmpline);
81                 if (f->namelen < 1) {
82                         *f->name = '\0';
83                         f->namelen = 0;
84                         ret = FMTERR2;
85                 }
86
87                 /* copy the field's value */
88                 if (f->alloclen <= nchars - f->namelen) {
89                         f->value = mh_xrealloc(f->value, f->alloclen + len);
90                         f->alloclen += len;
91                 }
92                 if (f->namelen != 0) {
93                         strcpy(f->value, tmpline + f->namelen + 1);
94                         f->valuelen = nchars - f->namelen - 1;
95                 } else {
96                         strcpy(f->value, tmpline);
97                         f->valuelen = nchars;
98                 }
99
100                 while (ret == FLD2 && (falted = is_falted(msg)) == B_TRUE) {
101                         nchars = getline(&tmpline, &len, msg);
102                         if (nchars <= 0) {
103                                 free(tmpline);
104                                 return IOERR2;
105                         }
106
107                         if (nchars >= NAMESZ) {
108                                 ret = LENERR2;
109                         }
110
111                         if (*(tmpline + nchars - 1) != '\n') {
112                                 ret = FMTERR2;
113                         }
114
115                         if (f->alloclen - f->valuelen <= nchars) {
116                                 f->value = mh_xrealloc(f->value,
117                                                 f->alloclen + len);
118                                 f->alloclen += len;
119                         }
120                         strcpy(f->value + f->valuelen, tmpline);
121                         f->valuelen += nchars;
122
123                 }
124
125                 if (falted == FAIL) {
126                         ret = IOERR2;
127                 }
128
129                 free(tmpline);
130                 return ret;
131
132         case BODY2:
133                 *f->name = '\0';
134                 f->namelen = 0;
135
136                 nchars = getline(&tmpline, &len, msg);
137                 if (nchars < 1) {
138                         free(f->value);
139                         f->value = NULL;
140                         f->valuelen = 0;
141                         f->alloclen = 0;
142                         if (feof(msg)) {
143                                 return FILEEOF2;
144                         } else {
145                                 return IOERR2;
146                         }
147                 }
148
149                 if (nchars >= NAMESZ) {
150                         ret = LENERR2;
151                 }
152
153                 free(f->value);
154                 f->value = tmpline;
155                 f->valuelen = nchars;
156                 f->alloclen = len;
157                 return ret;
158
159         default:
160                 /* give error states back as received */
161                 return s;
162         }
163 }
164
165 static enum threestate
166 is_falted(FILE *msg)
167 {
168         enum threestate ret;
169         int c;
170
171         if ((c = getc(msg)) < 0) {
172                 if (feof(msg)) {
173                         clearerr(msg);
174                         return B_FALSE;
175                 } else {
176                         return FAIL;
177                 }
178         }
179         if (isblank(c)) {
180                 ret = B_TRUE;
181         } else {
182                 ret = B_FALSE;
183         }
184         if (ungetc(c, msg) != c) {
185                 return FAIL;
186         }
187         return ret;
188 }
189
190 static size_t
191 copyname(char *dst, char *src)
192 {
193         size_t len;
194         char *cp, *sep;
195
196         if (!(sep = strchr(src, ':'))) {
197                 return 0;
198         }
199         /* whitespace is forbidden in name */
200         for (cp=src; cp<sep; cp++) {
201                 if (isspace(*cp)) {
202                         return 0;
203                 }
204         }
205
206         len = sep - src;
207         if (len >= NAMESZ) {
208                 return 0;
209         }
210
211         src[len] = '\0';
212         strcpy(dst, src);
213
214         return strlen(dst);
215 }
216
217 static bool
218 is_separator(char *line)
219 {
220         /*
221         ** In MH, lines that are consists of dashes only are
222         ** separators as well ... not so in RFC 822.
223         */
224         while (*line == '-') {
225                 line++;
226         }
227         if (strcmp("\n", line) == 0 || strcmp("\r\n", line) == 0 ) {
228                 return true;
229         }
230         return false;
231 }