OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / netflash / cgiparse.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <syslog.h>
5 #include <assert.h>
6 #include <signal.h>
7 #include <poll.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11
12 #include "kmp.h"
13 #include "cgiparse.h"
14
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"
19
20 #ifdef DEBUG_CGI
21 #define debug(args...) syslog(LOG_INFO, args)
22 #else
23 #define debug(args...)
24 #endif
25
26 #define MAX_HEADER_SIZE 128
27 #define MAX_NAME_SIZE  64
28 #define MAX_CONTENT_TYPE 54
29
30 #define OS_BUF_SIZE (4096 + MAX_HEADER_SIZE)
31
32 typedef struct {
33         int timeout;
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];
42 } output_section_t;
43
44 static int process_section(output_section_t *os);
45
46 static void set_nonblock(int fd)
47 {
48         int val;
49
50         val = fcntl(fd, F_GETFL);
51         if (val >= 0 && !(val & O_NONBLOCK))
52                 fcntl(fd, F_SETFL, val | O_NONBLOCK);
53 }
54
55 /*
56  * Reads data into the end of the buffer.
57  * Returns number of byes read.
58  */
59 static int os_read(output_section_t *os)
60 {
61         struct pollfd fds[1];
62         int ret;
63
64         fds[0].fd = 0;
65         fds[0].events = POLLIN;
66         fds[0].revents = 0;
67
68         do
69                 ret = poll(fds, 1, 30);
70         while (ret < 0 && errno == EINTR);
71         if (ret <= 0 || !(fds[0].revents & POLLIN)) {
72                 if (ret == 0)
73                         os->timeout = 1;
74                 return 0;
75         }
76
77         do
78                 ret = read(0, os->buf + os->len, sizeof(os->buf) - os->len);
79         while (ret < 0 && errno == EINTR);
80         if (ret <= 0)
81                 return 0;
82
83         os->len += ret;
84         os->pos += ret;
85         return ret;
86 }
87
88 /*
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.
93  */
94 static ssize_t os_getline(output_section_t *os)
95 {
96         size_t len = 0;
97         char *p;
98
99         for (;;) {
100                 p = memchr(os->buf + len, '\n', os->len - len);
101                 if (p)
102                         return p - os->buf + 1;
103                 len = os->len;
104                 if (os_read(os) <= 0)
105                         return 0;
106         }
107 }
108
109 /*
110  * Discards the specified number of byes from the front of the buffer.
111  */
112 static void os_discard(output_section_t *os, size_t len)
113 {
114         memmove(os->buf, os->buf + len, os->len - len);
115         os->len -= len;
116 }
117
118 /*
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.
123  */
124 static int getter_section(const char **text, void *cookie)
125 {
126         output_section_t *os = cookie;
127         size_t prevconsumed;
128
129         if (os->consumed < os->len)
130                 goto done;
131
132         if (os->len == sizeof(os->buf)) {
133                 if (os->consumed <= MAX_HEADER_SIZE)
134                         goto done;
135
136                 if (os->in_section) {
137                         os->writer(os->name, os->content_type, os->buf, os->consumed - MAX_HEADER_SIZE, os->pos - os->len);
138
139                 }
140                 os_discard(os, os->consumed - MAX_HEADER_SIZE);
141                 os->consumed = MAX_HEADER_SIZE;
142         }
143
144         if (os_read(os) <= 0)
145                 return 0;
146
147 done:
148         prevconsumed = os->consumed;
149         os->consumed = os->len;
150         *text = os->buf + prevconsumed;
151         return os->consumed - prevconsumed;
152 }
153
154 int cgi_extract_sections(output_buffer_function *writer)
155 {
156         int content_length;
157         const char *p;
158         char *boundary;
159         int boundary_length;
160         int match;
161         output_section_t os;
162
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);
167         }
168
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);
173         }
174
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);
179         }
180
181         /* Now search for boundary=XXX */
182         p = strstr(p, "boundary=");
183         if (!p) {
184                 syslog(LOG_WARNING, "cgi_filefetch bad or missing boundary specification");
185                 return(CGIPARSE_ERR_DATA);
186         }
187         p = strchr(p, '=') + 1;
188         debug("Got boundary=[%s]\n", p);
189
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
193          */
194
195         boundary_length = strlen(p) + 2;
196         boundary = alloca(boundary_length + 1);
197         sprintf(boundary, "--%s", p);
198
199         os.timeout = 0;
200         os.len = 0;
201         os.pos = 0;
202         os.consumed = 0;
203         os.in_section = 0;
204         os.writer = writer;
205
206         set_nonblock(0);
207
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);
211
212                 /* Flush all the bytes up until the match. */
213                 os.consumed = os.len - (os.pos - match);
214                 if (os.in_section) {
215                         /* We have been outputting this section. Back up by the boundary length
216                          * (plus 2 for the \r\n) and flush the buffer
217                          */
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,
222                                         os.pos - os.len);
223                 }
224                 os_discard(&os, os.consumed);
225                 os.consumed = 0;
226
227                 while (os.len < 2)
228                         if (os_read(&os) <= 0)
229                                 goto err;
230                 char ch1 = os.buf[0];
231                 char ch2 = os.buf[1];
232                 os_discard(&os, 2);
233
234                 if (ch1 == '\r' && ch2 == '\n') {
235                         /* we are at a boundary, so process this section */
236                         if (process_section(&os) <= 0)
237                                 goto err;
238                 }
239                 else if (ch1 == '-' && ch2 == '-') {
240                         debug("This is the last section\n");
241                         return CGIPARSE_ERR_NONE;
242                 }
243                 else {
244                         debug("Warning: Ignoring section with unknown terminator: '%c%c'\n", ch1, ch2);
245                 }
246                 os.pos = os.len;
247         }
248
249 err:
250         if (os.timeout) {
251                 return CGIPARSE_ERR_TIMEDOUT;
252         } else {
253                 return CGIPARSE_ERR_DATA;
254         }
255 }
256
257 /**
258  * Returns 1 if found a valid section or 0 if not.
259  *
260  * Also sets os->in_section, os->name, os->content_type.
261  */
262 static int process_section(output_section_t *os)
263 {
264         /* Need to read lines ending in \r\n, processing the headers
265          * Headers are terminated by a blank line
266          */
267         char *pt;
268         size_t len;
269
270         os->name[0] = 0;
271         os->content_type[0] = 0;
272         os->in_section = 0;
273
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");
279                         os_discard(os, len);
280                         return 1;
281                 }
282                 /* Strip off any \r\n */
283                 pt = strchr(os->buf, '\r');
284                 if (pt) {
285                         *pt = 0;
286                 }
287                 debug("HEADER: %s\n", os->buf);
288
289                 if (strncmp(os->buf, CONTENT_DISPOSITION, sizeof(CONTENT_DISPOSITION) - 1) == 0) {
290                         pt = strstr(os->buf, "name=\"");
291                         if (!pt) {
292                                 syslog(LOG_WARNING, "Warning: %s with no name\n", CONTENT_DISPOSITION);
293                         }
294                         else {
295                                 char *end;
296                                 pt += 6;
297                                 end = strchr(pt, '"');
298                                 if (end) {
299                                         *end = 0;
300                                 }
301                                 snprintf(os->name, sizeof(os->name), "%s", pt);
302                                 os->in_section = 1;
303                                 os->writer(os->name, os->content_type, 0, 0, 0);
304                         }
305                 }
306                 else if (strncmp(os->buf, CONTENT_TYPE, sizeof(CONTENT_TYPE) - 1) == 0) {
307                         pt = os->buf + sizeof(CONTENT_TYPE);
308
309                         snprintf(os->content_type, sizeof(os->content_type), "%s", pt);
310                 }
311                 /* Ignore other headers */
312                 os_discard(os, len);
313         }
314
315         return 0;
316 }