15 #define MULTIPART_FORM_DATA "multipart/form-data;"
16 #define CONTENT_DISPOSITION "Content-Disposition:"
17 #define CONTENT_TYPE "Content-Type:"
18 #define OCTET_STREAM "application/octet-stream"
21 #define debug(args...) syslog(LOG_INFO, args)
23 #define debug(args...)
26 #define MAX_HEADER_SIZE 128
27 #define MAX_NAME_SIZE 64
28 #define MAX_CONTENT_TYPE 54
30 #define OS_BUF_SIZE (4096 + MAX_HEADER_SIZE)
34 char buf[OS_BUF_SIZE];
35 size_t len; /* length of data in buf */
36 off_t pos; /* offset within section data of last valid byte in buf */
37 size_t consumed; /* length of data used by matcher */
38 int in_section; /* sending current section to writer */
39 output_buffer_function *writer;
40 char name[MAX_NAME_SIZE];
41 char content_type[MAX_CONTENT_TYPE];
44 static int process_section(output_section_t *os);
46 static void set_nonblock(int fd)
50 val = fcntl(fd, F_GETFL);
51 if (val >= 0 && !(val & O_NONBLOCK))
52 fcntl(fd, F_SETFL, val | O_NONBLOCK);
56 * Reads data into the end of the buffer.
57 * Returns number of byes read.
59 static int os_read(output_section_t *os)
65 fds[0].events = POLLIN;
69 ret = poll(fds, 1, 30);
70 while (ret < 0 && errno == EINTR);
71 if (ret <= 0 || !(fds[0].revents & POLLIN)) {
78 ret = read(0, os->buf + os->len, sizeof(os->buf) - os->len);
79 while (ret < 0 && errno == EINTR);
89 * Read into buffer until a newline is found.
90 * Note: searches any existing data in the buffer, so discard
91 * any previously processed data before calling.
92 * Returns offset of newline, or 0 if buffer is full before found.
94 static ssize_t os_getline(output_section_t *os)
100 p = memchr(os->buf + len, '\n', os->len - len);
102 return p - os->buf + 1;
104 if (os_read(os) <= 0)
110 * Discards the specified number of byes from the front of the buffer.
112 static void os_discard(output_section_t *os, size_t len)
114 memmove(os->buf, os->buf + len, os->len - len);
119 * Simple wrapper around os_read that passes any previously processed
120 * data to os->writer and then discards it to make room in
121 * the buffer, while ensuring at least MAX_HEADER_SIZE bytes remain.
122 * Returns the number of new data bytes in the buffer.
124 static int getter_section(const char **text, void *cookie)
126 output_section_t *os = cookie;
129 if (os->consumed < os->len)
132 if (os->len == sizeof(os->buf)) {
133 if (os->consumed <= MAX_HEADER_SIZE)
136 if (os->in_section) {
137 os->writer(os->name, os->content_type, os->buf, os->consumed - MAX_HEADER_SIZE, os->pos - os->len);
140 os_discard(os, os->consumed - MAX_HEADER_SIZE);
141 os->consumed = MAX_HEADER_SIZE;
144 if (os_read(os) <= 0)
148 prevconsumed = os->consumed;
149 os->consumed = os->len;
150 *text = os->buf + prevconsumed;
151 return os->consumed - prevconsumed;
154 int cgi_extract_sections(output_buffer_function *writer)
163 p = getenv("REQUEST_METHOD");
164 if (!p || strcmp(p, "POST") != 0) {
165 syslog(LOG_WARNING, "cgi_filefetch not POST");
166 return(CGIPARSE_ERR_FORMAT);
169 p = getenv("CONTENT_LENGTH");
170 if (!p || ((content_length = atoi(p)) == 0)) {
171 syslog(LOG_WARNING, "cgi_filefetch bad content length");
172 return(CGIPARSE_ERR_DATA);
175 p = getenv("CONTENT_TYPE");
176 if (!p || strncmp(p, MULTIPART_FORM_DATA, sizeof(MULTIPART_FORM_DATA) - 1) != 0) {
177 syslog(LOG_WARNING, "cgi_filefetch not type: %s", MULTIPART_FORM_DATA);
178 return(CGIPARSE_ERR_DATA);
181 /* Now search for boundary=XXX */
182 p = strstr(p, "boundary=");
184 syslog(LOG_WARNING, "cgi_filefetch bad or missing boundary specification");
185 return(CGIPARSE_ERR_DATA);
187 p = strchr(p, '=') + 1;
188 debug("Got boundary=[%s]\n", p);
190 /* Now search for --<boundary>
191 * Note that we don't search for \r\n--<boundary> since
192 * sometimes?? the first \r\n is missing
195 boundary_length = strlen(p) + 2;
196 boundary = alloca(boundary_length + 1);
197 sprintf(boundary, "--%s", p);
208 /* Now iterate through each item separated by the boundary */
209 while ((match = KMP(boundary, boundary_length, getter_section, &os)) >= 0) {
210 debug("Found match at %d\n", match - boundary_length);
212 /* Flush all the bytes up until the match. */
213 os.consumed = os.len - (os.pos - match);
215 /* We have been outputting this section. Back up by the boundary length
216 * (plus 2 for the \r\n) and flush the buffer
218 debug("reached end of section, match=%d, os.len=%d, os.pos=%d, boundary_length=%d\n", match, (int)os.len, (int)os.pos, boundary_length);
219 assert(os.consumed >= boundary_length + 2);
220 os.writer(os.name, os.content_type, os.buf,
221 os.consumed - boundary_length - 2,
224 os_discard(&os, os.consumed);
228 if (os_read(&os) <= 0)
230 char ch1 = os.buf[0];
231 char ch2 = os.buf[1];
234 if (ch1 == '\r' && ch2 == '\n') {
235 /* we are at a boundary, so process this section */
236 if (process_section(&os) <= 0)
239 else if (ch1 == '-' && ch2 == '-') {
240 debug("This is the last section\n");
241 return CGIPARSE_ERR_NONE;
244 debug("Warning: Ignoring section with unknown terminator: '%c%c'\n", ch1, ch2);
251 return CGIPARSE_ERR_TIMEDOUT;
253 return CGIPARSE_ERR_DATA;
258 * Returns 1 if found a valid section or 0 if not.
260 * Also sets os->in_section, os->name, os->content_type.
262 static int process_section(output_section_t *os)
264 /* Need to read lines ending in \r\n, processing the headers
265 * Headers are terminated by a blank line
271 os->content_type[0] = 0;
274 while ((len = os_getline(os)) > 0) {
275 os->buf[len - 1] = '\0';
276 if (os->buf[0] == '\r') {
277 /* Reached end of headers */
278 debug("End of headers\n");
282 /* Strip off any \r\n */
283 pt = strchr(os->buf, '\r');
287 debug("HEADER: %s\n", os->buf);
289 if (strncmp(os->buf, CONTENT_DISPOSITION, sizeof(CONTENT_DISPOSITION) - 1) == 0) {
290 pt = strstr(os->buf, "name=\"");
292 syslog(LOG_WARNING, "Warning: %s with no name\n", CONTENT_DISPOSITION);
297 end = strchr(pt, '"');
301 snprintf(os->name, sizeof(os->name), "%s", pt);
303 os->writer(os->name, os->content_type, 0, 0, 0);
306 else if (strncmp(os->buf, CONTENT_TYPE, sizeof(CONTENT_TYPE) - 1) == 0) {
307 pt = os->buf + sizeof(CONTENT_TYPE);
309 snprintf(os->content_type, sizeof(os->content_type), "%s", pt);
311 /* Ignore other headers */