check for crlf in m_getfld2
[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                 f->crlf = (nchars > 2 && tmpline[nchars-2] == '\r');
66                 if (nchars > NAMESZ+1 || (!f->crlf && 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 || ret == LENERR2) && (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+1 || (!f->crlf && 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                 f->crlf = (nchars > 2 && tmpline[nchars-2] == '\r');
150                 free(f->value);
151                 f->value = tmpline;
152                 f->valuelen = nchars;
153                 f->alloclen = len;
154                 return ret;
155
156         default:
157                 /* give error states back as received */
158                 return s;
159         }
160 }
161
162 static enum threestate
163 is_falted(FILE *msg)
164 {
165         enum threestate ret;
166         int c;
167
168         if ((c = getc(msg)) < 0) {
169                 if (feof(msg)) {
170                         clearerr(msg);
171                         return B_FALSE;
172                 } else {
173                         return FAIL;
174                 }
175         }
176         if (isblank(c)) {
177                 ret = B_TRUE;
178         } else {
179                 ret = B_FALSE;
180         }
181         if (ungetc(c, msg) != c) {
182                 return FAIL;
183         }
184         return ret;
185 }
186
187 static size_t
188 copyname(char *dst, char *src)
189 {
190         size_t len;
191         char *cp, *sep;
192
193         if (!(sep = strchr(src, ':'))) {
194                 return 0;
195         }
196         /* whitespace is forbidden in name */
197         for (cp=src; cp<sep; cp++) {
198                 if (isspace(*cp)) {
199                         return 0;
200                 }
201         }
202
203         len = sep - src;
204         if (len >= NAMESZ - 1) {
205                 return 0;
206         }
207
208         src[len] = '\0';
209         strcpy(dst, src);
210
211         return strlen(dst);
212 }
213
214 static boolean
215 is_separator(char *line)
216 {
217         /*
218         ** In MH, lines that are consists of dashes only are
219         ** separators as well ... not so in RFC 822.
220         */
221         while (*line == '-') {
222                 line++;
223         }
224         if (strcmp("\n", line) == 0 || strcmp("\r\n", line) == 0 ) {
225                 return TRUE;
226         }
227         return FALSE;
228 }