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