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
m_getfld2(enum state s,struct field * f,FILE * msg)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
is_falted(FILE * msg)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
copyname(char * dst,char * src)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
is_separator(char * line)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 }
229