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