]>
Commit | Line | Data |
---|---|---|
3840d6ba | 1 | /* |
063e1ac7 | 2 | * "$Id: http.c,v 1.39 1999/07/12 16:09:35 mike Exp $" |
3840d6ba | 3 | * |
fd8b1cf8 | 4 | * HTTP routines for the Common UNIX Printing System (CUPS) scheduler. |
5 | * | |
3a193f5e | 6 | * Copyright 1997-1999 by Easy Software Products, all rights reserved. |
fd8b1cf8 | 7 | * |
3a193f5e | 8 | * These statusd instructions, statements, and computer programs are the |
fd8b1cf8 | 9 | * property of Easy Software Products and are protected by Federal |
10 | * copyright law. Distribution and use rights are outlined in the file | |
11 | * "LICENSE.txt" which should have been included with this file. If this | |
12 | * file is missing or damaged please contact Easy Software Products | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
8784b6a6 | 17 | * 44141 Airport View Drive, Suite 204 |
fd8b1cf8 | 18 | * Hollywood, Maryland 20636-3111 USA |
19 | * | |
20 | * Voice: (301) 373-9603 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
3840d6ba | 23 | * |
24 | * Contents: | |
25 | * | |
50146867 | 26 | * httpInitialize() - Initialize the HTTP interface library and set the |
27 | * default HTTP proxy (if any). | |
28 | * httpClose() - Close an HTTP connection... | |
29 | * httpConnect() - Connect to a HTTP server. | |
30 | * httpReconnect() - Reconnect to a HTTP server... | |
31 | * httpSeparate() - Separate a Universal Resource Identifier into its | |
32 | * components. | |
33 | * httpSetField() - Set the value of an HTTP header. | |
34 | * httpDelete() - Send a DELETE request to the server. | |
35 | * httpGet() - Send a GET request to the server. | |
36 | * httpHead() - Send a HEAD request to the server. | |
37 | * httpOptions() - Send an OPTIONS request to the server. | |
38 | * httpPost() - Send a POST request to the server. | |
39 | * httpPut() - Send a PUT request to the server. | |
40 | * httpTrace() - Send an TRACE request to the server. | |
69ee1496 | 41 | * httpFlush() - Flush data from a HTTP connection. |
50146867 | 42 | * httpRead() - Read data from a HTTP connection. |
43 | * httpWrite() - Write data to a HTTP connection. | |
44 | * httpGets() - Get a line of text from a HTTP connection. | |
45 | * httpPrintf() - Print a formatted string to a HTTP connection. | |
46 | * httpStatus() - Return a short string describing a HTTP status code. | |
50146867 | 47 | * httpGetDateString() - Get a formatted date/time string from a time value. |
48 | * httpGetDateTime() - Get a time value from a formatted date/time string. | |
49 | * httpUpdate() - Update the current HTTP state for incoming data. | |
50 | * httpDecode64() - Base64-decode a string. | |
51 | * httpEncode64() - Base64-encode a string. | |
4a73831b | 52 | * httpGetLength() - Get the amount of data remaining from the |
0542e38e | 53 | * content-length or transfer-encoding fields. |
4a73831b | 54 | * http_field() - Return the field index for a field name. |
a8374e1e | 55 | * http_send() - Send a request with all fields and the trailing |
56 | * blank line. | |
3840d6ba | 57 | */ |
58 | ||
59 | /* | |
60 | * Include necessary headers... | |
61 | */ | |
62 | ||
3b960317 | 63 | #include <stdio.h> |
64 | #include <stdlib.h> | |
3840d6ba | 65 | #include <stdarg.h> |
3b960317 | 66 | #include <ctype.h> |
67 | #include "string.h" | |
3b960317 | 68 | #include <fcntl.h> |
992cf15a | 69 | |
3b960317 | 70 | #include "http.h" |
8a5aa628 | 71 | #include "ipp.h" |
4a73831b | 72 | #include "debug.h" |
f4cafe91 | 73 | |
5356dc5a | 74 | #if !defined(WIN32) && !defined(__EMX__) |
75 | # include <signal.h> | |
76 | #endif /* !WIN32 && !__EMX__ */ | |
77 | ||
42d48bd2 | 78 | /* |
79 | * Some operating systems have done away with the Fxxxx constants for | |
80 | * the fcntl() call; this works around that "feature"... | |
81 | */ | |
82 | ||
83 | #ifndef FNONBLK | |
84 | # define FNONBLK O_NONBLOCK | |
85 | #endif /* !FNONBLK */ | |
86 | ||
992cf15a | 87 | |
88 | /* | |
3a193f5e | 89 | * Local functions... |
992cf15a | 90 | */ |
91 | ||
063e1ac7 | 92 | static http_field_t http_field(const char *name); |
93 | static int http_send(http_t *http, http_state_t request, | |
94 | const char *uri); | |
3840d6ba | 95 | |
96 | ||
97 | /* | |
3a193f5e | 98 | * Local globals... |
3840d6ba | 99 | */ |
100 | ||
063e1ac7 | 101 | static const char *http_fields[] = |
3a193f5e | 102 | { |
3a193f5e | 103 | "Accept-Language", |
0542e38e | 104 | "Accept-Ranges", |
3a193f5e | 105 | "Authorization", |
3a193f5e | 106 | "Connection", |
3a193f5e | 107 | "Content-Encoding", |
108 | "Content-Language", | |
109 | "Content-Length", | |
110 | "Content-Location", | |
111 | "Content-MD5", | |
112 | "Content-Range", | |
113 | "Content-Type", | |
0d1f75a3 | 114 | "Content-Version", |
3a193f5e | 115 | "Date", |
3a193f5e | 116 | "Host", |
3a193f5e | 117 | "If-Modified-Since", |
3a193f5e | 118 | "If-Unmodified-since", |
0d1f75a3 | 119 | "Keep-Alive", |
3a193f5e | 120 | "Last-Modified", |
0d1f75a3 | 121 | "Link", |
3a193f5e | 122 | "Location", |
3a193f5e | 123 | "Range", |
124 | "Referer", | |
125 | "Retry-After", | |
3a193f5e | 126 | "Transfer-Encoding", |
127 | "Upgrade", | |
128 | "User-Agent", | |
3a193f5e | 129 | "WWW-Authenticate" |
130 | }; | |
063e1ac7 | 131 | static const char *days[7] = |
3a193f5e | 132 | { |
133 | "Sun", | |
134 | "Mon", | |
135 | "Tue", | |
136 | "Wed", | |
137 | "Thu", | |
138 | "Fri", | |
139 | "Sat" | |
140 | }; | |
063e1ac7 | 141 | static const char *months[12] = |
3a193f5e | 142 | { |
143 | "Jan", | |
144 | "Feb", | |
145 | "Mar", | |
146 | "Apr", | |
147 | "May", | |
148 | "Jun", | |
149 | "Jul", | |
150 | "Aug", | |
151 | "Sep", | |
152 | "Oct", | |
153 | "Nov", | |
154 | "Dec" | |
155 | }; | |
3840d6ba | 156 | |
157 | ||
9cba63f2 | 158 | /* |
159 | * 'httpInitialize()' - Initialize the HTTP interface library and set the | |
160 | * default HTTP proxy (if any). | |
161 | */ | |
162 | ||
163 | void | |
0542e38e | 164 | httpInitialize(void) |
9cba63f2 | 165 | { |
5356dc5a | 166 | #if defined(WIN32) || defined(__EMX__) |
50146867 | 167 | WSADATA winsockdata; /* WinSock data */ |
168 | static int initialized = 0;/* Has WinSock been initialized? */ | |
169 | ||
170 | ||
171 | if (!initialized) | |
172 | WSAStartup(MAKEWORD(1,1), &winsockdata); | |
ca7e65c8 | 173 | #elif defined(HAVE_SIGSET) |
174 | sigset(SIGPIPE, SIG_IGN); | |
175 | #elif defined(HAVE_SIGACTION) | |
2bffb563 | 176 | struct sigaction action; /* POSIX sigaction data */ |
177 | ||
178 | ||
179 | /* | |
180 | * Ignore SIGPIPE signals... | |
181 | */ | |
182 | ||
183 | memset(&action, 0, sizeof(action)); | |
184 | action.sa_handler = SIG_IGN; | |
185 | sigaction(SIGPIPE, &action, NULL); | |
ca7e65c8 | 186 | #else |
187 | signal(SIGPIPE, SIG_IGN); | |
5356dc5a | 188 | #endif /* WIN32 || __EMX__ */ |
50146867 | 189 | } |
190 | ||
191 | ||
192 | /* | |
193 | * 'httpClose()' - Close an HTTP connection... | |
194 | */ | |
195 | ||
196 | void | |
197 | httpClose(http_t *http) /* I - Connection to close */ | |
198 | { | |
199 | if (http == NULL) | |
200 | return; | |
201 | ||
202 | #ifdef WIN32 | |
203 | closesocket(http->fd); | |
204 | #else | |
205 | close(http->fd); | |
206 | #endif /* WIN32 */ | |
207 | ||
208 | free(http); | |
9cba63f2 | 209 | } |
210 | ||
211 | ||
3840d6ba | 212 | /* |
50146867 | 213 | * 'httpConnect()' - Connect to a HTTP server. |
3840d6ba | 214 | */ |
215 | ||
50146867 | 216 | http_t * /* O - New HTTP connection */ |
063e1ac7 | 217 | httpConnect(const char *host, /* I - Host to connect to */ |
218 | int port) /* I - Port number */ | |
3840d6ba | 219 | { |
50146867 | 220 | http_t *http; /* New HTTP connection */ |
221 | struct hostent *hostaddr; /* Host address data */ | |
222 | ||
223 | ||
0b83a6c8 | 224 | httpInitialize(); |
225 | ||
50146867 | 226 | /* |
227 | * Lookup the host... | |
228 | */ | |
229 | ||
0542e38e | 230 | if ((hostaddr = gethostbyname(host)) == NULL) |
50146867 | 231 | return (NULL); |
3840d6ba | 232 | |
3840d6ba | 233 | /* |
3a193f5e | 234 | * Allocate memory for the structure... |
3840d6ba | 235 | */ |
236 | ||
3a193f5e | 237 | http = calloc(sizeof(http_t), 1); |
238 | if (http == NULL) | |
239 | return (NULL); | |
3840d6ba | 240 | |
a8374e1e | 241 | http->version = HTTP_1_1; |
0542e38e | 242 | http->blocking = 1; |
3a193f5e | 243 | http->activity = time(NULL); |
3840d6ba | 244 | |
245 | /* | |
50146867 | 246 | * Copy the hostname and port and then "reconnect"... |
3840d6ba | 247 | */ |
248 | ||
50146867 | 249 | strcpy(http->hostname, host); |
0542e38e | 250 | memset((char *)&(http->hostaddr), 0, sizeof(http->hostaddr)); |
251 | memcpy((char *)&(http->hostaddr.sin_addr), hostaddr->h_addr, hostaddr->h_length); | |
50146867 | 252 | http->hostaddr.sin_family = hostaddr->h_addrtype; |
2456b740 | 253 | #ifdef WIN32 |
254 | http->hostaddr.sin_port = htons((u_short)port); | |
255 | #else | |
0542e38e | 256 | http->hostaddr.sin_port = htons(port); |
2456b740 | 257 | #endif /* WIN32 */ |
50146867 | 258 | if (httpReconnect(http)) |
3840d6ba | 259 | { |
3a193f5e | 260 | free(http); |
261 | return (NULL); | |
3840d6ba | 262 | } |
50146867 | 263 | else |
264 | return (http); | |
3a193f5e | 265 | } |
3840d6ba | 266 | |
3840d6ba | 267 | |
3a193f5e | 268 | /* |
50146867 | 269 | * 'httpReconnect()' - Reconnect to a HTTP server... |
3a193f5e | 270 | */ |
3840d6ba | 271 | |
50146867 | 272 | int /* O - 0 on success, non-zero on failure */ |
273 | httpReconnect(http_t *http) /* I - HTTP data */ | |
3a193f5e | 274 | { |
50146867 | 275 | int val; /* Socket option value */ |
276 | ||
3840d6ba | 277 | |
50146867 | 278 | /* |
279 | * Close any previously open socket... | |
280 | */ | |
281 | ||
282 | if (http->fd) | |
3a193f5e | 283 | #ifdef WIN32 |
50146867 | 284 | closesocket(http->fd); |
3a193f5e | 285 | #else |
50146867 | 286 | close(http->fd); |
3a193f5e | 287 | #endif /* WIN32 */ |
3840d6ba | 288 | |
50146867 | 289 | /* |
290 | * Create the socket and set options to allow reuse. | |
291 | */ | |
3840d6ba | 292 | |
900b10ea | 293 | if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
50146867 | 294 | return (-1); |
fd8b1cf8 | 295 | |
50146867 | 296 | #ifdef FD_CLOEXEC |
297 | fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting * | |
298 | * other processes... */ | |
299 | #endif /* FD_CLOEXEC */ | |
fd8b1cf8 | 300 | |
50146867 | 301 | val = 1; |
2456b740 | 302 | setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); |
3840d6ba | 303 | |
50146867 | 304 | #ifdef SO_REUSEPORT |
305 | val = 1; | |
306 | setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); | |
307 | #endif /* SO_REUSEPORT */ | |
3840d6ba | 308 | |
50146867 | 309 | /* |
310 | * Connect to the server... | |
311 | */ | |
312 | ||
313 | if (connect(http->fd, (struct sockaddr *)&(http->hostaddr), | |
314 | sizeof(http->hostaddr)) < 0) | |
315 | { | |
316 | #ifdef WIN32 | |
317 | closesocket(http->fd); | |
318 | #else | |
319 | close(http->fd); | |
320 | #endif | |
321 | ||
322 | return (-1); | |
e8fda7b9 | 323 | } |
50146867 | 324 | |
325 | return (0); | |
3a193f5e | 326 | } |
327 | ||
328 | ||
50146867 | 329 | /* |
330 | * 'httpSeparate()' - Separate a Universal Resource Identifier into its | |
331 | * components. | |
332 | */ | |
333 | ||
3a193f5e | 334 | void |
063e1ac7 | 335 | httpSeparate(const char *uri, /* I - Universal Resource Identifier */ |
336 | char *method, /* O - Method (http, https, etc.) */ | |
337 | char *username, /* O - Username */ | |
338 | char *host, /* O - Hostname */ | |
339 | int *port, /* O - Port number to use */ | |
340 | char *resource) /* O - Resource/filename */ | |
3a193f5e | 341 | { |
50146867 | 342 | char *ptr; /* Pointer into string... */ |
3a193f5e | 343 | |
344 | ||
50146867 | 345 | if (uri == NULL || method == NULL || username == NULL || host == NULL || |
4a73831b | 346 | port == NULL || resource == NULL) |
50146867 | 347 | return; |
3a193f5e | 348 | |
50146867 | 349 | /* |
350 | * Grab the method portion of the URI... | |
351 | */ | |
3a193f5e | 352 | |
caf07a5d | 353 | ptr = host; |
50146867 | 354 | while (*uri != ':' && *uri != '\0') |
d92f1fd8 | 355 | *ptr++ = *uri++; |
3a193f5e | 356 | |
50146867 | 357 | *ptr = '\0'; |
358 | if (*uri == ':') | |
359 | uri ++; | |
3a193f5e | 360 | |
50146867 | 361 | /* |
caf07a5d | 362 | * If the method contains a period or slash, then it's probably |
363 | * hostname/filename... | |
50146867 | 364 | */ |
3a193f5e | 365 | |
d92f1fd8 | 366 | if (strchr(host, '.') != NULL || strchr(host, '/') != NULL || *uri == '\0') |
50146867 | 367 | { |
caf07a5d | 368 | if ((ptr = strchr(host, '/')) != NULL) |
369 | { | |
370 | strcpy(resource, ptr); | |
371 | *ptr = '\0'; | |
372 | } | |
373 | else | |
374 | resource[0] = '\0'; | |
375 | ||
376 | if (isdigit(*uri)) | |
377 | { | |
378 | /* | |
379 | * OK, we have "hostname:port[/resource]"... | |
380 | */ | |
381 | ||
063e1ac7 | 382 | *port = strtol(uri, (char **)&uri, 10); |
caf07a5d | 383 | |
384 | if (*uri == '/') | |
385 | strcpy(resource, uri); | |
386 | } | |
387 | else | |
388 | *port = 0; | |
389 | ||
390 | strcpy(method, "http"); | |
50146867 | 391 | username[0] = '\0'; |
50146867 | 392 | return; |
393 | } | |
caf07a5d | 394 | else |
395 | strcpy(method, host); | |
3a193f5e | 396 | |
50146867 | 397 | /* |
0b83a6c8 | 398 | * If the method starts with less than 2 slashes then it is a local resource... |
50146867 | 399 | */ |
3a193f5e | 400 | |
0b83a6c8 | 401 | if (strncmp(uri, "//", 2) != 0) |
50146867 | 402 | { |
403 | strcpy(resource, uri); | |
404 | username[0] = '\0'; | |
405 | host[0] = '\0'; | |
406 | *port = 0; | |
407 | return; | |
408 | } | |
3a193f5e | 409 | |
50146867 | 410 | /* |
411 | * Grab the hostname... | |
412 | */ | |
3a193f5e | 413 | |
50146867 | 414 | while (*uri == '/') |
415 | uri ++; | |
3a193f5e | 416 | |
50146867 | 417 | ptr = host; |
418 | while (*uri != ':' && *uri != '@' && *uri != '/' && *uri != '\0') | |
419 | *ptr ++ = *uri ++; | |
3a193f5e | 420 | |
50146867 | 421 | *ptr = '\0'; |
3a193f5e | 422 | |
50146867 | 423 | if (*uri == '@') |
424 | { | |
425 | /* | |
426 | * Got a username... | |
427 | */ | |
3a193f5e | 428 | |
50146867 | 429 | strcpy(username, host); |
430 | ||
431 | ptr = host; | |
432 | while (*uri != ':' && *uri != '/' && *uri != '\0') | |
433 | *ptr ++ = *uri ++; | |
3a193f5e | 434 | |
50146867 | 435 | *ptr = '\0'; |
436 | } | |
437 | else | |
438 | username[0] = '\0'; | |
3a193f5e | 439 | |
50146867 | 440 | if (*uri == '\0') |
441 | { | |
442 | /* | |
443 | * Hostname but no port or path... | |
444 | */ | |
3a193f5e | 445 | |
50146867 | 446 | *port = 0; |
447 | resource[0] = '/'; | |
448 | resource[1] = '\0'; | |
449 | return; | |
450 | } | |
451 | else if (*uri == ':') | |
452 | { | |
453 | /* | |
454 | * Parse port number... | |
455 | */ | |
3a193f5e | 456 | |
50146867 | 457 | *port = 0; |
458 | uri ++; | |
459 | while (isdigit(*uri)) | |
460 | { | |
461 | *port = (*port * 10) + *uri - '0'; | |
462 | uri ++; | |
463 | } | |
464 | } | |
465 | else | |
466 | { | |
467 | /* | |
468 | * Figure out the default port number based on the method... | |
469 | */ | |
3a193f5e | 470 | |
50146867 | 471 | if (strcasecmp(method, "http") == 0) |
472 | *port = 80; | |
473 | else if (strcasecmp(method, "https") == 0) | |
474 | *port = 443; | |
475 | else if (strcasecmp(method, "ipp") == 0) /* Not registered yet... */ | |
a9338e0a | 476 | *port = ippPort(); |
56e64c1f | 477 | else if (strcasecmp(method, "socket") == 0) /* Not registered yet... */ |
478 | *port = 9100; | |
50146867 | 479 | else |
480 | *port = 0; | |
481 | } | |
3a193f5e | 482 | |
50146867 | 483 | /* |
484 | * The remaining portion is the resource string... | |
485 | */ | |
3a193f5e | 486 | |
50146867 | 487 | strcpy(resource, uri); |
3a193f5e | 488 | } |
489 | ||
490 | ||
50146867 | 491 | /* |
492 | * 'httpSetField()' - Set the value of an HTTP header. | |
493 | */ | |
3a193f5e | 494 | |
50146867 | 495 | void |
496 | httpSetField(http_t *http, /* I - HTTP data */ | |
497 | http_field_t field, /* I - Field index */ | |
063e1ac7 | 498 | const char *value) /* I - Value */ |
3a193f5e | 499 | { |
50146867 | 500 | strncpy(http->fields[field], value, HTTP_MAX_VALUE - 1); |
501 | http->fields[field][HTTP_MAX_VALUE - 1] = '\0'; | |
3a193f5e | 502 | } |
503 | ||
504 | ||
50146867 | 505 | /* |
506 | * 'httpDelete()' - Send a DELETE request to the server. | |
507 | */ | |
508 | ||
509 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 510 | httpDelete(http_t *http, /* I - HTTP data */ |
511 | const char *uri) /* I - URI to delete */ | |
3a193f5e | 512 | { |
a8374e1e | 513 | return (http_send(http, HTTP_DELETE, uri)); |
3a193f5e | 514 | } |
515 | ||
516 | ||
50146867 | 517 | /* |
518 | * 'httpGet()' - Send a GET request to the server. | |
519 | */ | |
520 | ||
521 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 522 | httpGet(http_t *http, /* I - HTTP data */ |
523 | const char *uri) /* I - URI to get */ | |
3a193f5e | 524 | { |
a8374e1e | 525 | return (http_send(http, HTTP_GET, uri)); |
3a193f5e | 526 | } |
527 | ||
528 | ||
50146867 | 529 | /* |
530 | * 'httpHead()' - Send a HEAD request to the server. | |
531 | */ | |
532 | ||
533 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 534 | httpHead(http_t *http, /* I - HTTP data */ |
535 | const char *uri) /* I - URI for head */ | |
3a193f5e | 536 | { |
a8374e1e | 537 | return (http_send(http, HTTP_HEAD, uri)); |
3a193f5e | 538 | } |
539 | ||
540 | ||
50146867 | 541 | /* |
542 | * 'httpOptions()' - Send an OPTIONS request to the server. | |
543 | */ | |
544 | ||
545 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 546 | httpOptions(http_t *http, /* I - HTTP data */ |
547 | const char *uri) /* I - URI for options */ | |
3a193f5e | 548 | { |
a8374e1e | 549 | return (http_send(http, HTTP_OPTIONS, uri)); |
3a193f5e | 550 | } |
551 | ||
552 | ||
50146867 | 553 | /* |
554 | * 'httpPost()' - Send a POST request to the server. | |
555 | */ | |
556 | ||
557 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 558 | httpPost(http_t *http, /* I - HTTP data */ |
559 | const char *uri) /* I - URI for post */ | |
3a193f5e | 560 | { |
4a73831b | 561 | httpGetLength(http); |
0542e38e | 562 | |
a8374e1e | 563 | return (http_send(http, HTTP_POST, uri)); |
3a193f5e | 564 | } |
565 | ||
566 | ||
567 | /* | |
50146867 | 568 | * 'httpPut()' - Send a PUT request to the server. |
3a193f5e | 569 | */ |
570 | ||
50146867 | 571 | int /* O - Status of call (0 = success) */ |
063e1ac7 | 572 | httpPut(http_t *http, /* I - HTTP data */ |
573 | const char *uri) /* I - URI to put */ | |
3a193f5e | 574 | { |
4a73831b | 575 | httpGetLength(http); |
0542e38e | 576 | |
a8374e1e | 577 | return (http_send(http, HTTP_PUT, uri)); |
3840d6ba | 578 | } |
579 | ||
580 | ||
581 | /* | |
50146867 | 582 | * 'httpTrace()' - Send an TRACE request to the server. |
3840d6ba | 583 | */ |
584 | ||
50146867 | 585 | int /* O - Status of call (0 = success) */ |
063e1ac7 | 586 | httpTrace(http_t *http, /* I - HTTP data */ |
587 | const char *uri) /* I - URI for trace */ | |
3840d6ba | 588 | { |
a8374e1e | 589 | return (http_send(http, HTTP_TRACE, uri)); |
3840d6ba | 590 | } |
591 | ||
592 | ||
69ee1496 | 593 | /* |
594 | * 'httpFlush()' - Flush data from a HTTP connection. | |
595 | */ | |
596 | ||
597 | void | |
598 | httpFlush(http_t *http) /* I - HTTP data */ | |
599 | { | |
600 | char buffer[8192]; /* Junk buffer */ | |
601 | ||
602 | ||
603 | while (httpRead(http, buffer, sizeof(buffer)) > 0); | |
604 | } | |
605 | ||
606 | ||
3840d6ba | 607 | /* |
50146867 | 608 | * 'httpRead()' - Read data from a HTTP connection. |
3840d6ba | 609 | */ |
610 | ||
50146867 | 611 | int /* O - Number of bytes read */ |
612 | httpRead(http_t *http, /* I - HTTP data */ | |
613 | char *buffer, /* I - Buffer for data */ | |
614 | int length) /* I - Maximum number of bytes */ | |
3840d6ba | 615 | { |
2456b740 | 616 | int bytes; /* Bytes read */ |
50146867 | 617 | char len[32]; /* Length string */ |
3840d6ba | 618 | |
619 | ||
5081f753 | 620 | DEBUG_printf(("httpRead(%08x, %08x, %d)\n", http, buffer, length)); |
4a73831b | 621 | |
50146867 | 622 | if (http == NULL || buffer == NULL) |
623 | return (-1); | |
3840d6ba | 624 | |
50146867 | 625 | http->activity = time(NULL); |
3840d6ba | 626 | |
50146867 | 627 | if (length <= 0) |
628 | return (0); | |
fd8b1cf8 | 629 | |
0542e38e | 630 | if (http->data_encoding == HTTP_ENCODE_CHUNKED && |
631 | http->data_remaining <= 0 && | |
50146867 | 632 | (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV || |
633 | http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV)) | |
634 | { | |
635 | if (httpGets(len, sizeof(len), http) == NULL) | |
636 | return (0); | |
3840d6ba | 637 | |
0542e38e | 638 | http->data_remaining = strtol(len, NULL, 16); |
50146867 | 639 | } |
3840d6ba | 640 | |
4a73831b | 641 | DEBUG_printf(("httpRead: data_remaining = %d\n", http->data_remaining)); |
642 | ||
50146867 | 643 | if (http->data_remaining == 0) |
3840d6ba | 644 | { |
50146867 | 645 | /* |
646 | * A zero-length chunk ends a transfer; unless we are reading POST | |
647 | * data, go idle... | |
648 | */ | |
f736c0e3 | 649 | |
50146867 | 650 | if (http->state == HTTP_POST_RECV) |
651 | http->state ++; | |
652 | else | |
653 | http->state = HTTP_WAITING; | |
3840d6ba | 654 | |
50146867 | 655 | return (0); |
656 | } | |
0542e38e | 657 | else if (length > http->data_remaining) |
658 | length = http->data_remaining; | |
50146867 | 659 | |
0542e38e | 660 | if (http->used > 0) |
50146867 | 661 | { |
0542e38e | 662 | if (length > http->used) |
663 | length = http->used; | |
3840d6ba | 664 | |
0542e38e | 665 | bytes = length; |
666 | ||
4a73831b | 667 | DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes)); |
668 | ||
0542e38e | 669 | memcpy(buffer, http->buffer, length); |
670 | http->used -= length; | |
671 | ||
672 | if (http->used > 0) | |
673 | memcpy(http->buffer, http->buffer + length, http->used); | |
50146867 | 674 | } |
0542e38e | 675 | else |
4a73831b | 676 | { |
677 | DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length)); | |
0542e38e | 678 | bytes = recv(http->fd, buffer, length, 0); |
4a73831b | 679 | DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes)); |
680 | } | |
3840d6ba | 681 | |
0542e38e | 682 | if (bytes > 0) |
683 | http->data_remaining -= bytes; | |
684 | ||
4a73831b | 685 | if (http->data_remaining == 0 && http->data_encoding != HTTP_ENCODE_CHUNKED) |
686 | { | |
687 | if (http->state == HTTP_POST_RECV) | |
688 | http->state ++; | |
689 | else | |
690 | http->state = HTTP_WAITING; | |
691 | } | |
692 | ||
0542e38e | 693 | return (bytes); |
3840d6ba | 694 | } |
695 | ||
696 | ||
697 | /* | |
50146867 | 698 | * 'httpWrite()' - Write data to a HTTP connection. |
3840d6ba | 699 | */ |
50146867 | 700 | |
701 | int /* O - Number of bytes written */ | |
063e1ac7 | 702 | httpWrite(http_t *http, /* I - HTTP data */ |
703 | const char *buffer, /* I - Buffer for data */ | |
704 | int length) /* I - Number of bytes to write */ | |
3840d6ba | 705 | { |
50146867 | 706 | int tbytes, /* Total bytes sent */ |
707 | bytes; /* Bytes sent */ | |
708 | char len[32]; /* Length string */ | |
3840d6ba | 709 | |
710 | ||
50146867 | 711 | if (http == NULL || buffer == NULL) |
712 | return (-1); | |
3840d6ba | 713 | |
50146867 | 714 | http->activity = time(NULL); |
715 | ||
9ae9d67c | 716 | if (http->data_encoding == HTTP_ENCODE_CHUNKED && |
50146867 | 717 | (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV || |
718 | http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV)) | |
3840d6ba | 719 | { |
0542e38e | 720 | sprintf(len, "%x\r\n", length); |
50146867 | 721 | if (send(http->fd, len, strlen(len), 0) < 3) |
722 | return (-1); | |
723 | } | |
3840d6ba | 724 | |
50146867 | 725 | if (length == 0) |
726 | { | |
727 | /* | |
728 | * A zero-length chunk ends a transfer; unless we are sending POST | |
729 | * data, go idle... | |
730 | */ | |
3840d6ba | 731 | |
50146867 | 732 | if (http->state == HTTP_POST_RECV) |
733 | http->state ++; | |
734 | else | |
735 | http->state = HTTP_WAITING; | |
3840d6ba | 736 | |
50146867 | 737 | return (0); |
738 | } | |
3840d6ba | 739 | |
50146867 | 740 | tbytes = 0; |
4a73831b | 741 | |
50146867 | 742 | while (length > 0) |
743 | { | |
744 | bytes = send(http->fd, buffer, length, 0); | |
745 | if (bytes < 0) | |
4a73831b | 746 | { |
747 | DEBUG_puts("httpWrite: error writing data...\n"); | |
50146867 | 748 | return (-1); |
4a73831b | 749 | } |
3840d6ba | 750 | |
50146867 | 751 | buffer += bytes; |
752 | tbytes += bytes; | |
753 | length -= bytes; | |
69ee1496 | 754 | if (http->data_encoding == HTTP_ENCODE_LENGTH) |
755 | http->data_remaining -= bytes; | |
756 | } | |
757 | ||
758 | if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH) | |
759 | { | |
760 | /* | |
761 | * Finished with the transfer; unless we are sending POST data, go idle... | |
762 | */ | |
763 | ||
764 | if (http->state == HTTP_POST_RECV) | |
765 | http->state ++; | |
766 | else | |
767 | http->state = HTTP_WAITING; | |
50146867 | 768 | } |
769 | ||
4a73831b | 770 | DEBUG_printf(("httpWrite: wrote %d bytes...\n", tbytes)); |
771 | ||
50146867 | 772 | return (tbytes); |
773 | } | |
774 | ||
775 | ||
776 | /* | |
777 | * 'httpGets()' - Get a line of text from a HTTP connection. | |
778 | */ | |
779 | ||
780 | char * /* O - Line or NULL */ | |
781 | httpGets(char *line, /* I - Line to read into */ | |
782 | int length, /* I - Max length of buffer */ | |
783 | http_t *http) /* I - HTTP data */ | |
784 | { | |
785 | char *lineptr, /* Pointer into line */ | |
786 | *bufptr, /* Pointer into input buffer */ | |
787 | *bufend; /* Pointer to end of buffer */ | |
788 | int bytes; /* Number of bytes read */ | |
789 | ||
790 | ||
4a73831b | 791 | DEBUG_printf(("httpGets(%08x, %d, %08x)\n", line, length, http)); |
792 | ||
50146867 | 793 | if (http == NULL || line == NULL) |
794 | return (NULL); | |
795 | ||
796 | /* | |
797 | * Pre-scan the buffer and see if there is a newline in there... | |
798 | */ | |
799 | ||
0542e38e | 800 | do |
50146867 | 801 | { |
0542e38e | 802 | bufptr = http->buffer; |
803 | bufend = http->buffer + http->used; | |
50146867 | 804 | |
0542e38e | 805 | while (bufptr < bufend) |
806 | if (*bufptr == 0x0a) | |
807 | break; | |
808 | else | |
809 | bufptr ++; | |
50146867 | 810 | |
0542e38e | 811 | if (bufptr >= bufend) |
50146867 | 812 | { |
813 | /* | |
0542e38e | 814 | * No newline; see if there is more data to be read... |
50146867 | 815 | */ |
816 | ||
0542e38e | 817 | if ((bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0)) < 1) |
818 | { | |
819 | /* | |
820 | * Nope, can't get a line this time... | |
821 | */ | |
822 | ||
823 | return (NULL); | |
824 | } | |
825 | else | |
826 | { | |
827 | /* | |
828 | * Yup, update the amount used and the end pointer... | |
829 | */ | |
830 | ||
831 | http->used += bytes; | |
832 | bufend += bytes; | |
833 | } | |
50146867 | 834 | } |
835 | } | |
0542e38e | 836 | while (bufptr >= bufend); |
50146867 | 837 | |
838 | http->activity = time(NULL); | |
839 | ||
840 | /* | |
841 | * Read a line from the buffer... | |
842 | */ | |
843 | ||
844 | lineptr = line; | |
845 | bufptr = http->buffer; | |
846 | bytes = 0; | |
847 | ||
848 | while (bufptr < bufend && bytes < length) | |
849 | { | |
850 | bytes ++; | |
851 | ||
852 | if (*bufptr == 0x0a) | |
853 | { | |
854 | bufptr ++; | |
855 | *lineptr = '\0'; | |
856 | ||
857 | http->used -= bytes; | |
858 | if (http->used > 0) | |
859 | memcpy(http->buffer, bufptr, http->used); | |
860 | ||
861 | return (line); | |
862 | } | |
863 | else if (*bufptr == 0x0d) | |
864 | bufptr ++; | |
865 | else | |
866 | *lineptr++ = *bufptr++; | |
867 | } | |
868 | ||
869 | return (NULL); | |
870 | } | |
871 | ||
872 | ||
873 | /* | |
874 | * 'httpPrintf()' - Print a formatted string to a HTTP connection. | |
875 | */ | |
876 | ||
877 | int /* O - Number of bytes written */ | |
878 | httpPrintf(http_t *http, /* I - HTTP data */ | |
879 | const char *format, /* I - printf-style format string */ | |
880 | ...) /* I - Additional args as needed */ | |
881 | { | |
882 | int bytes; /* Number of bytes to write */ | |
883 | char buf[HTTP_MAX_BUFFER]; /* Buffer for formatted string */ | |
884 | va_list ap; /* Variable argument pointer */ | |
885 | ||
886 | ||
887 | va_start(ap, format); | |
888 | bytes = vsprintf(buf, format, ap); | |
889 | va_end(ap); | |
890 | ||
5356dc5a | 891 | DEBUG_printf(("httpPrintf: %s", buf)); |
892 | ||
9ae9d67c | 893 | return (send(http->fd, buf, bytes, 0)); |
50146867 | 894 | } |
895 | ||
896 | ||
897 | /* | |
898 | * 'httpStatus()' - Return a short string describing a HTTP status code. | |
899 | */ | |
900 | ||
063e1ac7 | 901 | const char * /* O - String or NULL */ |
50146867 | 902 | httpStatus(http_status_t status) /* I - HTTP status code */ |
903 | { | |
904 | switch (status) | |
905 | { | |
906 | case HTTP_OK : | |
907 | return ("OK"); | |
908 | case HTTP_CREATED : | |
909 | return ("Created"); | |
910 | case HTTP_ACCEPTED : | |
911 | return ("Accepted"); | |
912 | case HTTP_NO_CONTENT : | |
913 | return ("No Content"); | |
914 | case HTTP_NOT_MODIFIED : | |
915 | return ("Not Modified"); | |
916 | case HTTP_BAD_REQUEST : | |
917 | return ("Bad Request"); | |
918 | case HTTP_UNAUTHORIZED : | |
919 | return ("Unauthorized"); | |
920 | case HTTP_FORBIDDEN : | |
921 | return ("Forbidden"); | |
922 | case HTTP_NOT_FOUND : | |
923 | return ("Not Found"); | |
924 | case HTTP_REQUEST_TOO_LARGE : | |
925 | return ("Request Entity Too Large"); | |
926 | case HTTP_URI_TOO_LONG : | |
927 | return ("URI Too Long"); | |
928 | case HTTP_NOT_IMPLEMENTED : | |
929 | return ("Not Implemented"); | |
930 | case HTTP_NOT_SUPPORTED : | |
931 | return ("Not Supported"); | |
932 | default : | |
933 | return ("Unknown"); | |
934 | } | |
935 | } | |
936 | ||
937 | ||
50146867 | 938 | /* |
939 | * 'httpGetDateString()' - Get a formatted date/time string from a time value. | |
940 | */ | |
941 | ||
063e1ac7 | 942 | const char * /* O - Date/time string */ |
50146867 | 943 | httpGetDateString(time_t t) /* I - UNIX time */ |
944 | { | |
945 | struct tm *tdate; | |
946 | static char datetime[256]; | |
947 | ||
948 | ||
949 | tdate = gmtime(&t); | |
a8374e1e | 950 | sprintf(datetime, "%s, %02d %s %d %02d:%02d:%02d GMT", |
951 | days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon], | |
952 | tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec); | |
50146867 | 953 | |
954 | return (datetime); | |
955 | } | |
956 | ||
957 | ||
958 | /* | |
959 | * 'httpGetDateTime()' - Get a time value from a formatted date/time string. | |
960 | */ | |
961 | ||
962 | time_t /* O - UNIX time */ | |
063e1ac7 | 963 | httpGetDateTime(const char *s) /* I - Date/time string */ |
50146867 | 964 | { |
a8374e1e | 965 | int i; /* Looping var */ |
966 | struct tm tdate; /* Time/date structure */ | |
967 | char mon[16]; /* Abbreviated month name */ | |
968 | int day, year; /* Day of month and year */ | |
969 | int hour, min, sec; /* Time */ | |
970 | ||
971 | ||
f4cafe91 | 972 | if (sscanf(s, "%*s%d%s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6) |
a8374e1e | 973 | return (0); |
974 | ||
975 | for (i = 0; i < 12; i ++) | |
976 | if (strcasecmp(mon, months[i]) == 0) | |
977 | break; | |
978 | ||
979 | if (i >= 12) | |
980 | return (0); | |
981 | ||
982 | tdate.tm_mon = i; | |
983 | tdate.tm_mday = day; | |
984 | tdate.tm_year = year - 1900; | |
985 | tdate.tm_hour = hour; | |
986 | tdate.tm_min = min; | |
987 | tdate.tm_sec = sec; | |
988 | tdate.tm_isdst = 0; | |
989 | ||
990 | return (mktime(&tdate)); | |
50146867 | 991 | } |
992 | ||
993 | ||
994 | /* | |
995 | * 'httpUpdate()' - Update the current HTTP state for incoming data. | |
996 | */ | |
997 | ||
a8374e1e | 998 | http_status_t /* O - HTTP status */ |
50146867 | 999 | httpUpdate(http_t *http) /* I - HTTP data */ |
1000 | { | |
a8374e1e | 1001 | char line[1024], /* Line from connection... */ |
1002 | *value; /* Pointer to value on line */ | |
1003 | http_field_t field; /* Field index */ | |
50146867 | 1004 | int major, minor; /* HTTP version numbers */ |
50146867 | 1005 | http_status_t status; /* Authorization status */ |
50146867 | 1006 | |
50146867 | 1007 | |
4a73831b | 1008 | DEBUG_printf(("httpUpdate(%08x)\n", http)); |
1009 | ||
a8374e1e | 1010 | /* |
1011 | * If we haven't issued any commands, then there is nothing to "update"... | |
1012 | */ | |
3840d6ba | 1013 | |
a8374e1e | 1014 | if (http->state == HTTP_WAITING) |
1015 | return (HTTP_CONTINUE); | |
3840d6ba | 1016 | |
1017 | /* | |
a8374e1e | 1018 | * Grab all of the lines we can from the connection... |
3840d6ba | 1019 | */ |
1020 | ||
a8374e1e | 1021 | while (httpGets(line, sizeof(line), http) != NULL) |
fd8b1cf8 | 1022 | { |
f4cafe91 | 1023 | DEBUG_puts(line); |
9ae9d67c | 1024 | |
a8374e1e | 1025 | if (line[0] == '\0') |
fd8b1cf8 | 1026 | { |
a8374e1e | 1027 | /* |
1028 | * Blank line means the start of the data section (if any). Return | |
1029 | * the result code, too... | |
11b9b0d7 | 1030 | * |
1031 | * If we get status 100 (HTTP_CONTINUE), then we *don't* change states. | |
1032 | * Instead, we just return HTTP_CONTINUE to the caller and keep on | |
1033 | * tryin'... | |
a8374e1e | 1034 | */ |
1035 | ||
11b9b0d7 | 1036 | if (http->status == HTTP_CONTINUE) |
1037 | return (http->status); | |
1038 | ||
5356dc5a | 1039 | httpGetLength(http); |
0542e38e | 1040 | |
1041 | switch (http->state) | |
1042 | { | |
1043 | case HTTP_GET : | |
1044 | case HTTP_POST : | |
1045 | case HTTP_POST_RECV : | |
1046 | case HTTP_PUT : | |
1047 | http->state ++; | |
1048 | break; | |
1049 | ||
1050 | default : | |
1051 | http->state = HTTP_WAITING; | |
1052 | break; | |
1053 | } | |
a8374e1e | 1054 | |
1055 | return (http->status); | |
fd8b1cf8 | 1056 | } |
a8374e1e | 1057 | else if (strncmp(line, "HTTP/", 5) == 0) |
fd8b1cf8 | 1058 | { |
1059 | /* | |
a8374e1e | 1060 | * Got the beginning of a response... |
fd8b1cf8 | 1061 | */ |
1062 | ||
a8374e1e | 1063 | if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3) |
1064 | return (HTTP_ERROR); | |
1065 | ||
1066 | http->version = (http_version_t)(major * 100 + minor); | |
1067 | http->status = status; | |
fd8b1cf8 | 1068 | } |
a8374e1e | 1069 | else if ((value = strchr(line, ':')) != NULL) |
3840d6ba | 1070 | { |
a8374e1e | 1071 | /* |
1072 | * Got a value... | |
1073 | */ | |
3a193f5e | 1074 | |
a8374e1e | 1075 | *value++ = '\0'; |
1076 | while (isspace(*value)) | |
1077 | value ++; | |
1078 | ||
0d1f75a3 | 1079 | /* |
1080 | * Be tolerants of servers that send unknown attribute fields... | |
1081 | */ | |
1082 | ||
a8374e1e | 1083 | if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN) |
f4cafe91 | 1084 | { |
4a73831b | 1085 | DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line)); |
0d1f75a3 | 1086 | continue; |
f4cafe91 | 1087 | } |
a8374e1e | 1088 | |
1089 | httpSetField(http, field, value); | |
3840d6ba | 1090 | } |
a8374e1e | 1091 | else |
1092 | return (HTTP_ERROR); | |
fd8b1cf8 | 1093 | } |
3840d6ba | 1094 | |
1095 | /* | |
a8374e1e | 1096 | * If we haven't already returned, then there is nothing new... |
3840d6ba | 1097 | */ |
1098 | ||
a8374e1e | 1099 | return (HTTP_CONTINUE); |
3840d6ba | 1100 | } |
1101 | ||
1102 | ||
3a193f5e | 1103 | /* |
50146867 | 1104 | * 'httpDecode64()' - Base64-decode a string. |
3840d6ba | 1105 | */ |
1106 | ||
50146867 | 1107 | char * /* O - Decoded string */ |
063e1ac7 | 1108 | httpDecode64(char *out, /* I - String to write to */ |
1109 | const char *in) /* I - String to read from */ | |
3840d6ba | 1110 | { |
50146867 | 1111 | int pos, /* Bit position */ |
1112 | base64; /* Value of this character */ | |
1113 | char *outptr; /* Output pointer */ | |
3840d6ba | 1114 | |
3840d6ba | 1115 | |
50146867 | 1116 | for (outptr = out, pos = 0; *in != '\0'; in ++) |
fd8b1cf8 | 1117 | { |
50146867 | 1118 | /* |
1119 | * Decode this character into a number from 0 to 63... | |
1120 | */ | |
f736c0e3 | 1121 | |
50146867 | 1122 | if (*in >= 'A' && *in <= 'Z') |
1123 | base64 = *in - 'A'; | |
1124 | else if (*in >= 'a' && *in <= 'z') | |
1125 | base64 = *in - 'a' + 26; | |
1126 | else if (*in >= '0' && *in <= '9') | |
1127 | base64 = *in - '0' + 52; | |
1128 | else if (*in == '+') | |
1129 | base64 = 62; | |
1130 | else if (*in == '/') | |
1131 | base64 = 63; | |
1132 | else if (*in == '=') | |
1133 | break; | |
1134 | else | |
1135 | continue; | |
3840d6ba | 1136 | |
50146867 | 1137 | /* |
1138 | * Store the result in the appropriate chars... | |
1139 | */ | |
992cf15a | 1140 | |
1141 | switch (pos) | |
1142 | { | |
1143 | case 0 : | |
50146867 | 1144 | *outptr = base64 << 2; |
1145 | pos ++; | |
992cf15a | 1146 | break; |
50146867 | 1147 | case 1 : |
1148 | *outptr++ |= (base64 >> 4) & 3; | |
1149 | *outptr = (base64 << 4) & 255; | |
1150 | pos ++; | |
1151 | break; | |
1152 | case 2 : | |
1153 | *outptr++ |= (base64 >> 2) & 15; | |
1154 | *outptr = (base64 << 6) & 255; | |
1155 | pos ++; | |
1156 | break; | |
1157 | case 3 : | |
1158 | *outptr++ |= base64; | |
1159 | pos = 0; | |
992cf15a | 1160 | break; |
992cf15a | 1161 | } |
1162 | } | |
1163 | ||
50146867 | 1164 | *outptr = '\0'; |
3840d6ba | 1165 | |
1166 | /* | |
50146867 | 1167 | * Return the decoded string... |
3840d6ba | 1168 | */ |
1169 | ||
50146867 | 1170 | return (out); |
3840d6ba | 1171 | } |
1172 | ||
1173 | ||
f736c0e3 | 1174 | /* |
50146867 | 1175 | * 'httpEncode64()' - Base64-encode a string. |
f736c0e3 | 1176 | */ |
1177 | ||
50146867 | 1178 | char * /* O - Encoded string */ |
063e1ac7 | 1179 | httpEncode64(char *out, /* I - String to write to */ |
1180 | const char *in) /* I - String to read from */ | |
f736c0e3 | 1181 | { |
50146867 | 1182 | char *outptr; /* Output pointer */ |
1183 | static char base64[] = /* Base64 characters... */ | |
1184 | { | |
1185 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
1186 | "abcdefghijklmnopqrstuvwxyz" | |
1187 | "0123456789" | |
1188 | "+/" | |
1189 | }; | |
f736c0e3 | 1190 | |
f736c0e3 | 1191 | |
50146867 | 1192 | for (outptr = out; *in != '\0'; in ++) |
f736c0e3 | 1193 | { |
1194 | /* | |
50146867 | 1195 | * Encode the up to 3 characters as 4 Base64 numbers... |
f736c0e3 | 1196 | */ |
1197 | ||
50146867 | 1198 | *outptr ++ = base64[in[0] >> 2]; |
1199 | *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63]; | |
f736c0e3 | 1200 | |
50146867 | 1201 | in ++; |
1202 | if (*in == '\0') | |
1203 | break; | |
f736c0e3 | 1204 | |
50146867 | 1205 | *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63]; |
f736c0e3 | 1206 | |
50146867 | 1207 | in ++; |
1208 | if (*in == '\0') | |
1209 | break; | |
f736c0e3 | 1210 | |
50146867 | 1211 | *outptr ++ = base64[in[0] & 63]; |
f736c0e3 | 1212 | } |
f736c0e3 | 1213 | |
50146867 | 1214 | *outptr ++ = '='; |
1215 | *outptr = '\0'; | |
f736c0e3 | 1216 | |
50146867 | 1217 | /* |
1218 | * Return the encoded string... | |
1219 | */ | |
f736c0e3 | 1220 | |
50146867 | 1221 | return (out); |
f736c0e3 | 1222 | } |
1223 | ||
1224 | ||
3a193f5e | 1225 | /* |
4a73831b | 1226 | * 'httpGetLength()' - Get the amount of data remaining from the |
1227 | * content-length or transfer-encoding fields. | |
0542e38e | 1228 | */ |
1229 | ||
063e1ac7 | 1230 | int /* O - Content length */ |
4a73831b | 1231 | httpGetLength(http_t *http) /* I - HTTP data */ |
0542e38e | 1232 | { |
1233 | if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0) | |
1234 | { | |
1235 | http->data_encoding = HTTP_ENCODE_CHUNKED; | |
1236 | http->data_remaining = 0; | |
1237 | } | |
1238 | else | |
1239 | { | |
1240 | http->data_encoding = HTTP_ENCODE_LENGTH; | |
1241 | ||
1242 | /* | |
1243 | * The following is a hack for HTTP servers that don't send a | |
1244 | * content-length or transfer-encoding field... | |
1245 | * | |
1246 | * If there is no content-length then the connection must close | |
1247 | * after the transfer is complete... | |
1248 | */ | |
1249 | ||
1250 | if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0') | |
1251 | http->data_remaining = 2147483647; | |
1252 | else | |
1253 | http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]); | |
1254 | } | |
4a73831b | 1255 | |
1256 | return (http->data_remaining); | |
1257 | } | |
1258 | ||
1259 | ||
1260 | /* | |
1261 | * 'http_field()' - Return the field index for a field name. | |
1262 | */ | |
1263 | ||
1264 | static http_field_t /* O - Field index */ | |
063e1ac7 | 1265 | http_field(const char *name) /* I - String name */ |
4a73831b | 1266 | { |
1267 | int i; /* Looping var */ | |
1268 | ||
1269 | ||
1270 | for (i = 0; i < HTTP_FIELD_MAX; i ++) | |
1271 | if (strcasecmp(name, http_fields[i]) == 0) | |
1272 | return ((http_field_t)i); | |
1273 | ||
1274 | return (HTTP_FIELD_UNKNOWN); | |
0542e38e | 1275 | } |
1276 | ||
1277 | ||
a8374e1e | 1278 | /* |
1279 | * 'http_send()' - Send a request with all fields and the trailing blank line. | |
1280 | */ | |
1281 | ||
1282 | static int /* O - 0 on success, non-zero on error */ | |
1283 | http_send(http_t *http, /* I - HTTP data */ | |
1284 | http_state_t request, /* I - Request code */ | |
063e1ac7 | 1285 | const char *uri) /* I - URI */ |
a8374e1e | 1286 | { |
1287 | int i; /* Looping var */ | |
1288 | char *ptr, /* Pointer in buffer */ | |
1289 | buf[1024]; /* Encoded URI buffer */ | |
063e1ac7 | 1290 | static const char *codes[] = /* Request code strings */ |
a8374e1e | 1291 | { |
1292 | NULL, | |
1293 | "OPTIONS", | |
1294 | "GET", | |
1295 | NULL, | |
1296 | "HEAD", | |
1297 | "POST", | |
1298 | NULL, | |
1299 | NULL, | |
1300 | "PUT", | |
1301 | NULL, | |
1302 | "DELETE", | |
1303 | "TRACE", | |
1304 | "CLOSE" | |
1305 | }; | |
063e1ac7 | 1306 | static const char *hex = "0123456789ABCDEF"; |
a8374e1e | 1307 | /* Hex digits */ |
1308 | ||
1309 | ||
1310 | if (http == NULL || uri == NULL) | |
1311 | return (-1); | |
1312 | ||
1313 | /* | |
1314 | * Encode the URI as needed... | |
1315 | */ | |
1316 | ||
1317 | for (ptr = buf; *uri != '\0'; uri ++) | |
1318 | if (*uri <= ' ' || *uri >= 127) | |
1319 | { | |
1320 | *ptr ++ = '%'; | |
1321 | *ptr ++ = hex[(*uri >> 4) & 15]; | |
1322 | *ptr ++ = hex[*uri & 15]; | |
1323 | } | |
1324 | else | |
1325 | *ptr ++ = *uri; | |
1326 | ||
1327 | *ptr = '\0'; | |
1328 | ||
1329 | /* | |
1330 | * Send the request header... | |
1331 | */ | |
1332 | ||
1333 | http->state = request; | |
5356dc5a | 1334 | if (request == HTTP_POST || request == HTTP_PUT) |
1335 | http->state ++; | |
1336 | ||
a8374e1e | 1337 | if (httpPrintf(http, "%s %s HTTP/1.1\n", codes[request], buf) < 1) |
1338 | { | |
1339 | /* | |
1340 | * Might have lost connection; try to reconnect... | |
1341 | */ | |
1342 | ||
1343 | if (httpReconnect(http)) | |
1344 | return (-1); | |
1345 | ||
1346 | /* | |
1347 | * OK, we've reconnected, send the request again... | |
1348 | */ | |
1349 | ||
1350 | if (httpPrintf(http, "%s %s HTTP/%d.%d\n", codes[request], buf, | |
1351 | http->version / 100, http->version % 100) < 1) | |
1352 | return (-1); | |
1353 | } | |
1354 | ||
1355 | for (i = 0; i < HTTP_FIELD_MAX; i ++) | |
1356 | if (http->fields[i][0] != '\0') | |
4a73831b | 1357 | { |
1358 | DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i])); | |
1359 | ||
a8374e1e | 1360 | if (httpPrintf(http, "%s: %s\n", http_fields[i], http->fields[i]) < 1) |
1361 | return (-1); | |
4a73831b | 1362 | } |
a8374e1e | 1363 | |
1364 | if (httpPrintf(http, "\n") < 1) | |
1365 | return (-1); | |
1366 | ||
1367 | httpClearFields(http); | |
1368 | ||
a8374e1e | 1369 | return (0); |
1370 | } | |
1371 | ||
1372 | ||
3840d6ba | 1373 | /* |
063e1ac7 | 1374 | * End of "$Id: http.c,v 1.39 1999/07/12 16:09:35 mike Exp $". |
3840d6ba | 1375 | */ |