]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/http.c
Fixed all constant arrays to use "const" modifier.
[thirdparty/cups.git] / cups / http.c
CommitLineData
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 92static http_field_t http_field(const char *name);
93static 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 101static 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 131static const char *days[7] =
3a193f5e 132 {
133 "Sun",
134 "Mon",
135 "Tue",
136 "Wed",
137 "Thu",
138 "Fri",
139 "Sat"
140 };
063e1ac7 141static 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
163void
0542e38e 164httpInitialize(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
196void
197httpClose(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 216http_t * /* O - New HTTP connection */
063e1ac7 217httpConnect(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 272int /* O - 0 on success, non-zero on failure */
273httpReconnect(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 334void
063e1ac7 335httpSeparate(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 495void
496httpSetField(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
509int /* O - Status of call (0 = success) */
063e1ac7 510httpDelete(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
521int /* O - Status of call (0 = success) */
063e1ac7 522httpGet(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
533int /* O - Status of call (0 = success) */
063e1ac7 534httpHead(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
545int /* O - Status of call (0 = success) */
063e1ac7 546httpOptions(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
557int /* O - Status of call (0 = success) */
063e1ac7 558httpPost(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 571int /* O - Status of call (0 = success) */
063e1ac7 572httpPut(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 585int /* O - Status of call (0 = success) */
063e1ac7 586httpTrace(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
597void
598httpFlush(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 611int /* O - Number of bytes read */
612httpRead(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
701int /* O - Number of bytes written */
063e1ac7 702httpWrite(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
780char * /* O - Line or NULL */
781httpGets(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
877int /* O - Number of bytes written */
878httpPrintf(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 901const char * /* O - String or NULL */
50146867 902httpStatus(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 942const char * /* O - Date/time string */
50146867 943httpGetDateString(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
962time_t /* O - UNIX time */
063e1ac7 963httpGetDateTime(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 998http_status_t /* O - HTTP status */
50146867 999httpUpdate(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 1107char * /* O - Decoded string */
063e1ac7 1108httpDecode64(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 1178char * /* O - Encoded string */
063e1ac7 1179httpEncode64(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 1230int /* O - Content length */
4a73831b 1231httpGetLength(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
1264static http_field_t /* O - Field index */
063e1ac7 1265http_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
1282static int /* O - 0 on success, non-zero on error */
1283http_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 */