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