1 /*
2  * Read MIME body-part, stopping on boundaries.
3  */
4 /* (C) Copyright 1994 by Carnegie Mellon University
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software
8  * and its documentation for any purpose is hereby granted without
9  * fee, provided that the above copyright notice appear in all copies
10  * and that both that copyright notice and this permission notice
11  * appear in supporting documentation, and that the name of Carnegie
12  * Mellon University not be used in advertising or publicity
13  * pertaining to distribution of the software without specific,
14  * written prior permission.  Carnegie Mellon University makes no
15  * representations about the suitability of this software for any
16  * purpose.  It is provided "as is" without express or implied
17  * warranty.
18  *
19  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
20  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
22  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
24  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
25  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26  * SOFTWARE.
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "part.h"
34 #include "xmalloc.h"
35 
36 #define BUFSIZE 1024 /* must be > PART_MAX_BOUNDARY_LEN */
37 #define GROWBOUNDARY 20
38 
39 static int pendingboundary(struct part *part);
40 
41 /*
42  * Create, initialize, and return a new struct part pointer
43  * for the input file 'infile'.
44  */
part_init(FILE * infile)45 struct part *part_init(FILE *infile)
46 {
47     static struct part zeropart;
48     struct part *newpart;
49 
50     newpart = (struct part *)xmalloc(sizeof(struct part));
51     *newpart = zeropart;
52     newpart->infile = infile;
53     newpart->buf = (unsigned char *)xmalloc(BUFSIZE);
54     newpart->buf_alloc = BUFSIZE;
55 
56     return newpart;
57 }
58 
59 /*
60  * Close and free 'part'.
61  */
part_close(struct part * part)62 void part_close(struct part *part)
63 {
64     fclose(part->infile);
65     if (part->buf) free(part->buf);
66     if (part->boundary) free(part->boundary);
67 }
68 
69 /*
70  * Return the multipart depth of 'part'.  Top-level is '0'.
71  */
72 int
part_depth(struct part * part)73 part_depth(struct part *part)
74 {
75     return part->boundary_num;
76 }
77 
78 /*
79  * Add to 'part' the multipart boundary 'boundary'.
80  */
part_addboundary(struct part * part,char * boundary)81 void part_addboundary(struct part *part, char *boundary)
82 {
83     /* Grow boundary array if necessary */
84     if (part->boundary_num == part->boundary_alloc) {
85 	part->boundary_alloc += GROWBOUNDARY;
86 	part->boundary = (char (*)[PART_MAX_BOUNDARY_LEN+1])
87 	    xrealloc((char *)part->boundary,
88 		     part->boundary_alloc * (PART_MAX_BOUNDARY_LEN+1));
89 	part->boundary_length = (int *)
90 	    xrealloc((char *)part->boundary_length,
91 		     part->boundary_alloc * sizeof(int));
92     }
93 
94     strncpy(part->boundary[part->boundary_num], boundary,
95 	    PART_MAX_BOUNDARY_LEN);
96     part->boundary[part->boundary_num][PART_MAX_BOUNDARY_LEN] = '\0';
97     part->boundary_length[part->boundary_num] =
98 	strlen(part->boundary[part->boundary_num]);
99     part->boundary_num++;
100     if (part->boundary_seen+1 == part->boundary_num) {
101 	part->boundary_seen++;
102     }
103 }
104 
105 /*
106  * Private function that is only called from the part_getc() macro.
107  *
108  * Fills the input buffer for 'part' if necessary.  Returns the next
109  * input character or EOF if at a boundary or end of file.
110  */
111 int
part_fill(struct part * part)112 part_fill(struct part *part)
113 {
114     /* part_getc() decremented this before calling us, put it back */
115     part->cnt++;
116 
117     /* Return EOF if we saw a boundary */
118     if (part->boundary_seen < part->boundary_num) return EOF;
119 
120     /* Fill buffer if it is empty */
121     if (part->cnt == 0) {
122 	part->ptr = part->buf;
123 	part->cnt = fread(part->buf, 1, part->buf_alloc, part->infile);
124 	if (part->cnt == 0) {
125 	    part->boundary_seen = 0;
126 	    return EOF;
127 	}
128     }
129 
130     /* If there is a newline, see if it is followed by a boundary */
131     if (part->ptr[0] == '\n' && pendingboundary(part)) {
132 	return EOF;
133     }
134 
135     part->cnt--;
136     return *part->ptr++;
137 }
138 
139 /*
140  * Read a line into the array 's', of size 'n', from 'part'.
141  * Reads until 'n'-1 characters are read, a newline is read, or
142  * an EOF is encountered.  The array is then nul-terminated and returned.
143  * If the first character read is an EOF, then a null pointer is instead
144  * returned.
145  */
146 char *
part_gets(char * s,int n,struct part * part)147 part_gets(char *s, int n, struct part *part)
148 {
149     int c;
150     char *p = s;
151 
152     if (n == 0) return 0;
153     n--;
154     while (n-- && (c = part_getc(part)) != EOF) {
155 	*p++ = c;
156 	if (c == '\n') break;
157     }
158     if (p == s) return 0;
159     *p++ = '\0';
160     return s;
161 }
162 
163 /*
164  * Push back the string 's' into the input buffer of 'part'.
165  * Leaves room in the input buffer to push back an additional single
166  * character using the prot_ungetc() macro.
167  */
part_ungets(char * s,struct part * part)168 void part_ungets(char *s, struct part *part)
169 {
170     int len = strlen(s);
171     int i;
172 
173     /* Grow buffer if necessary */
174     if (part->cnt + len + 1 > part->buf_alloc) {
175 	i = part->ptr - part->buf;
176 	part->buf_alloc = part->cnt + len + 1;
177 	part->buf = (unsigned char *)
178 	    xrealloc((char *)part->buf, part->buf_alloc);
179 	part->ptr = part->buf + i;
180     }
181 
182     /* Move current data down to make room for new data if necessary */
183     if (len + 1 > part->ptr - part->buf) {
184 	for (i = part->cnt-1; i >= 0; i--) {
185 	    part->buf[len+1+i] = part->ptr[i];
186 	}
187 	part->ptr = part->buf + len + 1;
188     }
189 
190     /* Copy in the new data */
191     part->ptr -= len;
192     part->cnt += len;
193     for (i = 0; i < len; i++) {
194 	part->ptr[i] = s[i];
195     }
196 }
197 
198 /*
199  * Reset the saw-boundary state of 'part' and set up to read next
200  * body-part Returns nonzero iff the pending boundary was a final
201  * boundary of the current multipart.
202  */
203 int
part_readboundary(struct part * part)204 part_readboundary(struct part *part)
205 {
206     int c;
207     int sawfinal = 0;
208 
209     if (part->boundary_seen < part->boundary_num-1) {
210 	/* We saw an enclosing boundary.  Signal end of multipart, but
211 	 * don't skip over the boundary.
212 	 */
213 	part->boundary_num--;
214 	return 1;
215     }
216 
217     /* Deal with EOF on input stream */
218     if (part->cnt == 0) return 1;
219 
220     /* Skip over delimiter, reset the "saw boundary" state */
221     part->ptr += part->boundary_length[part->boundary_seen] + 3;
222     part->cnt -= part->boundary_length[part->boundary_seen] + 3;
223     part->boundary_seen = part->boundary_num;
224 
225     /* Check for two dashes, which indicate a final delimiter */
226     c = part_getc(part);
227     if (c == '-') {
228 	c = part_getc(part);
229 	if (c == '-') {
230 	    sawfinal = 1;
231 	    part->boundary_num--;
232 	}
233     }
234 
235     /* Eat rest of the boundary line */
236     while (c != '\n' && c != EOF) {
237 	c = part_getc(part);
238     }
239 
240     return sawfinal;
241 }
242 
243 
244 /*
245  * Return nonzero and set the saw-boundary state iff 'part'
246  * is positioned at a boundary.
247  */
248 static int
pendingboundary(struct part * part)249 pendingboundary(struct part *part)
250 {
251     int bufleft;
252     int i;
253 
254     /* Fill buffer if we don't have enough to do our look ahead */
255     if (part->cnt < 3 ||
256 	(part->cnt < PART_MAX_BOUNDARY_LEN+3 && part->ptr[1] == '-' &&
257 	 part->ptr[2] == '-')) {
258 
259 	bufleft = part->buf_alloc - part->cnt - (part->ptr - part->buf);
260 
261 	/* If not enough room, move everything to beginning of buffer */
262 	if (part->ptr!=part->buf && bufleft + part->cnt < PART_MAX_BOUNDARY_LEN+3) {
263 	    for (i = 0; i < part->cnt; i++) {
264 		part->buf[i] = part->ptr[i];
265 	    }
266 	    part->ptr = part->buf;
267 	    bufleft = part->buf_alloc - part->cnt;
268 	}
269 
270 	/* Read in more data */
271 	part->cnt += fread(part->ptr+part->cnt, 1, bufleft, part->infile);
272     }
273 
274     /* If no "--", it's not a boundary */
275     if (part->cnt < 3 || part->ptr[1] != '-' || part->ptr[2] != '-') {
276 	return 0;
277     }
278 
279     for (i = 0; i < part->boundary_num; i++) {
280 	if (part->cnt - 3 >= part->boundary_length[i] &&
281 	    !strncmp((char *)part->ptr+3, part->boundary[i],
282 		     part->boundary_length[i])) {
283 	    break;
284 	}
285     }
286 
287     if (i == part->boundary_num) return 0;
288 
289     /* Saw boundary, index 'i' */
290     part->boundary_seen = i;
291     return 1;
292 }
293