de38435c01267e1a328721834f5d98d3c2a81321
[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         nchars = getline(&tmpline, &len, msg);
52         if (nchars < 1) {
53                 if (feof(msg)) {
54                         return FILEEOF2;
55                 } else {
56                         return IOERR2;
57                 }
58         }
59
60         *f->name = '\0';
61         f->namelen = 0;
62
63         if (nchars >= NAMESZ) {
64                 if (f->value) {
65                         free(f->value);
66                 }
67                 f->value = tmpline;
68                 f->valuelen = nchars;
69                 return LENERR2;
70         }
71
72         switch (s) {
73         case FLD2:
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                 if (f->value) {
155                         free(f->value);
156                 }
157                 f->value = tmpline;
158                 f->valuelen = nchars;
159                 f->alloclen = len;
160                 return BODY2;
161
162         default:
163                 /* give error states back as received */
164                 return s;
165         }
166 }
167
168 static enum threestate
169 is_falted(FILE *msg)
170 {
171         enum threestate ret;
172         int c;
173
174         if ((c = getc(msg)) < 0) {
175                 if (feof(msg)) {
176                         clearerr(msg);
177                         return B_FALSE;
178                 } else {
179                         return FAIL;
180                 }
181         }
182         if (isblank(c)) {
183                 ret = B_TRUE;
184         } else {
185                 ret = B_FALSE;
186         }
187         if (ungetc(c, msg) != c) {
188                 return FAIL;
189         }
190         return ret;
191 }
192
193 static size_t
194 copyname(char *dst, char *src)
195 {
196         size_t len;
197         char *cp, *sep;
198
199         if (!(sep = strchr(src, ':'))) {
200                 return 0;
201         }
202         /* whitespace is forbidden in name */
203         for (cp=src; cp<sep; cp++) {
204                 if (isspace(*cp)) {
205                         return 0;
206                 }
207         }
208
209         len = sep - src;
210         if (len >= NAMESZ) {
211                 return 0;
212         }
213
214         src[len] = '\0';
215         strcpy(dst, src);
216
217         return strlen(dst);
218 }
219
220 static int
221 is_separator(char *line)
222 {
223         /*
224         ** In MH, lines that are consists of dashes only are
225         ** separators as well ... not so in RFC 822.
226         */
227         while (*line == '-') {
228                 line++;
229         }
230         if (strcmp("\n", line)==0) {
231                 return 1;
232         }
233         return 0;
234 }