1 /*
2 Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4
5 Additional Copyright (C) 2004-2005 Christopher Price,
6 Angel Carpintero, and other contributing authors.
7
8 Major part of this file is reused code from GNU Wget. It has been
9 merged and modified for use in the program Motion which is also
10 released under the terms of the GNU General Public License.
11
12 GNU Wget and Motion is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License as published
14 by the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 GNU Wget is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with Wget; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
25
26 #include "motion.h"
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #define MINVAL(x, y) ((x) < (y) ? (x) : (y))
32
33 /* This file contains the generic routines for work with headers.
34 Currently they are used only by HTTP in http.c, but they can be
35 used by anything that cares about RFC822-style headers.
36
37 Header is defined in RFC2068, as quoted below. Note that this
38 definition is not HTTP-specific -- it is virtually
39 indistinguishable from the one given in RFC822 or RFC1036.
40
41 message-header = field-name ":" [ field-value ] CRLF
42
43 field-name = token
44 field-value = *( field-content | LWS )
45
46 field-content = <the OCTETs making up the field-value
47 and consisting of either *TEXT or combinations
48 of token, tspecials, and quoted-string>
49
50 The public functions are header_get() and header_process(), which
51 see. */
52
53
54 /* Get a header from read-buffer RBUF and return it in *HDR.
55
56 As defined in RFC2068 and elsewhere, a header can be folded into
57 multiple lines if the continuation line begins with a space or
58 horizontal TAB. Also, this function will accept a header ending
59 with just LF instead of CRLF.
60
61 The header may be of arbitrary length; the function will allocate
62 as much memory as necessary for it to fit. It need not contain a
63 `:', thus you can use it to retrieve, say, HTTP status line.
64
65 All trailing whitespace is stripped from the header, and it is
66 zero-terminated.
67 */
header_get(netcam_context_ptr netcam,char ** hdr,enum header_get_flags flags)68 int header_get(netcam_context_ptr netcam, char **hdr, enum header_get_flags flags)
69 {
70 int i;
71 int bufsize = 80;
72
73 *hdr = mymalloc(bufsize);
74
75 for (i = 0; 1; i++) {
76 int res;
77 /* #### Use DO_REALLOC? */
78 if (i > bufsize - 1)
79 *hdr = (char *)myrealloc(*hdr, (bufsize <<= 1), "");
80
81 res = RBUF_READCHAR (netcam, *hdr + i);
82
83 if (res == 1) {
84 if ((*hdr)[i] == '\n') {
85 if (!((flags & HG_NO_CONTINUATIONS) || i == 0
86 || (i == 1 && (*hdr)[0] == '\r'))) {
87 char next;
88 /*
89 * If the header is non-empty, we need to check if
90 * it continues on to the other line. We do that by
91 * peeking at the next character.
92 */
93 res = rbuf_peek(netcam, &next);
94
95 if (res == 0) {
96 (*hdr)[i] = '\0';
97 return HG_EOF;
98 } else if (res == -1) {
99 (*hdr)[i] = '\0';
100 return HG_ERROR;
101 }
102 /* If the next character is HT or SP, just continue. */
103 if (next == '\t' || next == ' ')
104 continue;
105 }
106
107 /*
108 * Strip trailing whitespace. (*hdr)[i] is the newline;
109 * decrement I until it points to the last available
110 * whitespace.
111 */
112 while (i > 0 && isspace((*hdr)[i - 1]))
113 --i;
114
115 (*hdr)[i] = '\0';
116 break;
117 }
118 } else if (res == 0) {
119 (*hdr)[i] = '\0';
120 return HG_EOF;
121 } else {
122 (*hdr)[i] = '\0';
123 return HG_ERROR;
124 }
125 }
126
127 return HG_OK;
128 }
129
130 /**
131 * header_process
132 *
133 * Check whether HEADER begins with NAME and, if yes, skip the `:' and
134 * the whitespace, and call PROCFUN with the arguments of HEADER's
135 * contents (after the `:' and space) and ARG. Otherwise, return 0.
136 */
header_process(const char * header,const char * name,int (* procfun)(const char *,void *),void * arg)137 int header_process(const char *header, const char *name,
138 int (*procfun)(const char *, void *), void *arg)
139 {
140 /* Check whether HEADER matches NAME. */
141 while (*name && (tolower (*name) == tolower (*header)))
142 ++name, ++header;
143
144 if (*name || *header++ != ':')
145 return 0;
146
147 header += skip_lws (header);
148 return ((*procfun) (header, arg));
149 }
150
151 /* Helper functions for use with header_process(). */
152
153 /**
154 * header_extract_number
155 *
156 * Extract a long integer from HEADER and store it to CLOSURE. If an
157 * error is encountered, return 0, else 1.
158 */
header_extract_number(const char * header,void * closure)159 int header_extract_number(const char *header, void *closure)
160 {
161 const char *p = header;
162 long result;
163
164 for (result = 0; isdigit (*p); p++)
165 result = 10 * result + (*p - '0');
166
167 /* Failure if no number present. */
168 if (p == header)
169 return 0;
170
171 /* Skip trailing whitespace. */
172 p += skip_lws (p);
173
174 /* We return the value, even if a format error follows. */
175 *(long *)closure = result;
176
177 /* Indicate failure if trailing garbage is present. */
178 if (*p)
179 return 0;
180
181 return 1;
182 }
183
184 /**
185 * header_strdup
186 *
187 * Strdup HEADER, and place the pointer to CLOSURE.
188 */
header_strdup(const char * header,void * closure)189 int header_strdup(const char *header, void *closure)
190 {
191 *(char **)closure = mystrdup(header);
192 return 1;
193 }
194
195
196 /**
197 * skip_lws
198 * Skip LWS (linear white space), if present. Returns number of
199 * characters to skip.
200 */
skip_lws(const char * string)201 int skip_lws(const char *string)
202 {
203 const char *p = string;
204
205 while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
206 ++p;
207
208 return p - string;
209 }
210
211
212 /**
213 * motion_base64_encode
214 *
215 * Encode the string S of length LENGTH to base64 format and place it
216 * to STORE. STORE will be 0-terminated, and must point to a writable
217 * buffer of at least 1+BASE64_LENGTH(length) bytes.
218 */
motion_base64_encode(const char * s,char * store,int length)219 void motion_base64_encode(const char *s, char *store, int length)
220 {
221 /* Conversion table. */
222 static const char tbl[64] = {
223 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
224 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
225 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
226 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
227 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
228 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
229 'w', 'x', 'y', 'z', '0', '1', '2', '3',
230 '4', '5', '6', '7', '8', '9', '+', '/'
231 };
232
233 int i;
234 unsigned char *p = (unsigned char *)store;
235
236 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
237 for (i = 0; i < length; i += 3) {
238 *p++ = tbl[s[0] >> 2];
239 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
240 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
241 *p++ = tbl[s[2] & 0x3f];
242 s += 3;
243 }
244
245 /* Pad the result if necessary... */
246 if (i == length + 1)
247 *(p - 1) = '=';
248 else if (i == length + 2)
249 *(p - 1) = *(p - 2) = '=';
250
251 /* ...and zero-terminate it. */
252 *p = '\0';
253 }
254
255 /**
256 * strdupdelim
257 */
strdupdelim(const char * beg,const char * end)258 char *strdupdelim(const char *beg, const char *end)
259 {
260 char *res = mymalloc(end - beg + 1);
261 memcpy (res, beg, end - beg);
262
263 res[end - beg] = '\0';
264 return res;
265 }
266
267 /**
268 * http_process_type
269 */
http_process_type(const char * hdr,void * arg)270 int http_process_type(const char *hdr, void *arg)
271 {
272 char **result = (char **)arg;
273 /* Locate P on `;' or the terminating zero, whichever comes first. */
274 const char *p = strchr (hdr, ';');
275
276 if (!p)
277 p = hdr + strlen (hdr);
278
279 while (p > hdr && isspace (*(p - 1)))
280 --p;
281
282 *result = strdupdelim (hdr, p);
283 return 1;
284 }
285
286 /**
287 * rbuf_initialize
288 *
289 * This is a simple implementation of buffering IO-read functions.
290 */
rbuf_initialize(netcam_context_ptr netcam)291 void rbuf_initialize(netcam_context_ptr netcam)
292 {
293 netcam->response->buffer_pos = netcam->response->buffer;
294 netcam->response->buffer_left = 0;
295 }
296
rbuf_read_bufferful(netcam_context_ptr netcam)297 int rbuf_read_bufferful(netcam_context_ptr netcam)
298 {
299 return netcam_recv(netcam, netcam->response->buffer,
300 sizeof (netcam->response->buffer));
301 }
302
303 /**
304 * rbuf_peek
305 *
306 * Like rbuf_readchar(), only don't move the buffer position.
307 */
rbuf_peek(netcam_context_ptr netcam,char * store)308 int rbuf_peek(netcam_context_ptr netcam, char *store)
309 {
310 if (!netcam->response->buffer_left) {
311 int res;
312 rbuf_initialize(netcam);
313 res = netcam_recv (netcam, netcam->response->buffer,
314 sizeof (netcam->response->buffer));
315
316 if (res <= 0) {
317 *store = '\0';
318 return res;
319 }
320
321 netcam->response->buffer_left = res;
322 }
323
324 *store = *netcam->response->buffer_pos;
325 return 1;
326 }
327
328 /**
329 * rbuf_flush
330 *
331 * Flush RBUF's buffer to WHERE. Flush MAXSIZE bytes at most.
332 * Returns the number of bytes actually copied. If the buffer is
333 * empty, 0 is returned.
334 */
rbuf_flush(netcam_context_ptr netcam,char * where,int maxsize)335 int rbuf_flush(netcam_context_ptr netcam, char *where, int maxsize)
336 {
337 if (!netcam->response->buffer_left) {
338 return 0;
339 } else {
340 int howmuch = MINVAL ((int)netcam->response->buffer_left, maxsize);
341
342 if (where)
343 memcpy(where, netcam->response->buffer_pos, howmuch);
344
345 netcam->response->buffer_left -= howmuch;
346 netcam->response->buffer_pos += howmuch;
347 return howmuch;
348 }
349 }
350
351 /**
352 * http_result_code
353 *
354 * Get the HTTP result code
355 */
http_result_code(const char * header)356 int http_result_code(const char *header)
357 {
358 char *cptr;
359
360 /* Assure the header starts out right. */
361 if (strncmp(header, "HTTP", 4))
362 return -1;
363
364 /* Find the space following the HTTP/1.x */
365 if ((cptr = strchr(header+4, ' ')) == NULL)
366 return -1;
367
368 return atoi(cptr + 1);
369 }
370
371