]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http.c
Added more error checking code to httpGets() - now check for two EPIPE
[thirdparty/cups.git] / cups / http.c
1 /*
2 * "$Id: http.c,v 1.43 1999/08/16 15:38:18 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 return (-1);
333
334 #ifdef FD_CLOEXEC
335 fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
336 * other processes... */
337 #endif /* FD_CLOEXEC */
338
339 val = 1;
340 setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
341
342 #ifdef SO_REUSEPORT
343 val = 1;
344 setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
345 #endif /* SO_REUSEPORT */
346
347 /*
348 * Connect to the server...
349 */
350
351 if (connect(http->fd, (struct sockaddr *)&(http->hostaddr),
352 sizeof(http->hostaddr)) < 0)
353 {
354 #ifdef WIN32
355 closesocket(http->fd);
356 #else
357 close(http->fd);
358 #endif
359
360 return (-1);
361 }
362
363 return (0);
364 }
365
366
367 /*
368 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
369 * components.
370 */
371
372 void
373 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
374 char *method, /* O - Method (http, https, etc.) */
375 char *username, /* O - Username */
376 char *host, /* O - Hostname */
377 int *port, /* O - Port number to use */
378 char *resource) /* O - Resource/filename */
379 {
380 char *ptr; /* Pointer into string... */
381
382
383 if (uri == NULL || method == NULL || username == NULL || host == NULL ||
384 port == NULL || resource == NULL)
385 return;
386
387 /*
388 * Grab the method portion of the URI...
389 */
390
391 ptr = host;
392 while (*uri != ':' && *uri != '\0')
393 *ptr++ = *uri++;
394
395 *ptr = '\0';
396 if (*uri == ':')
397 uri ++;
398
399 /*
400 * If the method contains a period or slash, then it's probably
401 * hostname/filename...
402 */
403
404 if (strchr(host, '.') != NULL || strchr(host, '/') != NULL || *uri == '\0')
405 {
406 if ((ptr = strchr(host, '/')) != NULL)
407 {
408 strcpy(resource, ptr);
409 *ptr = '\0';
410 }
411 else
412 resource[0] = '\0';
413
414 if (isdigit(*uri))
415 {
416 /*
417 * OK, we have "hostname:port[/resource]"...
418 */
419
420 *port = strtol(uri, (char **)&uri, 10);
421
422 if (*uri == '/')
423 strcpy(resource, uri);
424 }
425 else
426 *port = 0;
427
428 strcpy(method, "http");
429 username[0] = '\0';
430 return;
431 }
432 else
433 strcpy(method, host);
434
435 /*
436 * If the method starts with less than 2 slashes then it is a local resource...
437 */
438
439 if (strncmp(uri, "//", 2) != 0)
440 {
441 strcpy(resource, uri);
442 username[0] = '\0';
443 host[0] = '\0';
444 *port = 0;
445 return;
446 }
447
448 /*
449 * Grab the hostname...
450 */
451
452 while (*uri == '/')
453 uri ++;
454
455 ptr = host;
456 while (*uri != ':' && *uri != '@' && *uri != '/' && *uri != '\0')
457 *ptr ++ = *uri ++;
458
459 *ptr = '\0';
460
461 if (*uri == '@')
462 {
463 /*
464 * Got a username...
465 */
466
467 strcpy(username, host);
468
469 ptr = host;
470 while (*uri != ':' && *uri != '/' && *uri != '\0')
471 *ptr ++ = *uri ++;
472
473 *ptr = '\0';
474 }
475 else
476 username[0] = '\0';
477
478 if (*uri == '\0')
479 {
480 /*
481 * Hostname but no port or path...
482 */
483
484 *port = 0;
485 resource[0] = '/';
486 resource[1] = '\0';
487 return;
488 }
489 else if (*uri == ':')
490 {
491 /*
492 * Parse port number...
493 */
494
495 *port = 0;
496 uri ++;
497 while (isdigit(*uri))
498 {
499 *port = (*port * 10) + *uri - '0';
500 uri ++;
501 }
502 }
503 else
504 {
505 /*
506 * Figure out the default port number based on the method...
507 */
508
509 if (strcasecmp(method, "http") == 0)
510 *port = 80;
511 else if (strcasecmp(method, "https") == 0)
512 *port = 443;
513 else if (strcasecmp(method, "ipp") == 0) /* Not registered yet... */
514 *port = ippPort();
515 else if (strcasecmp(method, "socket") == 0) /* Not registered yet... */
516 *port = 9100;
517 else
518 *port = 0;
519 }
520
521 /*
522 * The remaining portion is the resource string...
523 */
524
525 strcpy(resource, uri);
526 }
527
528
529 /*
530 * 'httpSetField()' - Set the value of an HTTP header.
531 */
532
533 void
534 httpSetField(http_t *http, /* I - HTTP data */
535 http_field_t field, /* I - Field index */
536 const char *value) /* I - Value */
537 {
538 strncpy(http->fields[field], value, HTTP_MAX_VALUE - 1);
539 http->fields[field][HTTP_MAX_VALUE - 1] = '\0';
540 }
541
542
543 /*
544 * 'httpDelete()' - Send a DELETE request to the server.
545 */
546
547 int /* O - Status of call (0 = success) */
548 httpDelete(http_t *http, /* I - HTTP data */
549 const char *uri) /* I - URI to delete */
550 {
551 return (http_send(http, HTTP_DELETE, uri));
552 }
553
554
555 /*
556 * 'httpGet()' - Send a GET request to the server.
557 */
558
559 int /* O - Status of call (0 = success) */
560 httpGet(http_t *http, /* I - HTTP data */
561 const char *uri) /* I - URI to get */
562 {
563 return (http_send(http, HTTP_GET, uri));
564 }
565
566
567 /*
568 * 'httpHead()' - Send a HEAD request to the server.
569 */
570
571 int /* O - Status of call (0 = success) */
572 httpHead(http_t *http, /* I - HTTP data */
573 const char *uri) /* I - URI for head */
574 {
575 return (http_send(http, HTTP_HEAD, uri));
576 }
577
578
579 /*
580 * 'httpOptions()' - Send an OPTIONS request to the server.
581 */
582
583 int /* O - Status of call (0 = success) */
584 httpOptions(http_t *http, /* I - HTTP data */
585 const char *uri) /* I - URI for options */
586 {
587 return (http_send(http, HTTP_OPTIONS, uri));
588 }
589
590
591 /*
592 * 'httpPost()' - Send a POST request to the server.
593 */
594
595 int /* O - Status of call (0 = success) */
596 httpPost(http_t *http, /* I - HTTP data */
597 const char *uri) /* I - URI for post */
598 {
599 httpGetLength(http);
600
601 return (http_send(http, HTTP_POST, uri));
602 }
603
604
605 /*
606 * 'httpPut()' - Send a PUT request to the server.
607 */
608
609 int /* O - Status of call (0 = success) */
610 httpPut(http_t *http, /* I - HTTP data */
611 const char *uri) /* I - URI to put */
612 {
613 httpGetLength(http);
614
615 return (http_send(http, HTTP_PUT, uri));
616 }
617
618
619 /*
620 * 'httpTrace()' - Send an TRACE request to the server.
621 */
622
623 int /* O - Status of call (0 = success) */
624 httpTrace(http_t *http, /* I - HTTP data */
625 const char *uri) /* I - URI for trace */
626 {
627 return (http_send(http, HTTP_TRACE, uri));
628 }
629
630
631 /*
632 * 'httpFlush()' - Flush data from a HTTP connection.
633 */
634
635 void
636 httpFlush(http_t *http) /* I - HTTP data */
637 {
638 char buffer[8192]; /* Junk buffer */
639
640
641 while (httpRead(http, buffer, sizeof(buffer)) > 0);
642 }
643
644
645 /*
646 * 'httpRead()' - Read data from a HTTP connection.
647 */
648
649 int /* O - Number of bytes read */
650 httpRead(http_t *http, /* I - HTTP data */
651 char *buffer, /* I - Buffer for data */
652 int length) /* I - Maximum number of bytes */
653 {
654 int bytes; /* Bytes read */
655 char len[32]; /* Length string */
656
657
658 DEBUG_printf(("httpRead(%08x, %08x, %d)\n", http, buffer, length));
659
660 if (http == NULL || buffer == NULL)
661 return (-1);
662
663 http->activity = time(NULL);
664
665 if (length <= 0)
666 return (0);
667
668 if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
669 http->data_remaining <= 0 &&
670 (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV ||
671 http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV))
672 {
673 if (httpGets(len, sizeof(len), http) == NULL)
674 return (0);
675
676 http->data_remaining = strtol(len, NULL, 16);
677 }
678
679 DEBUG_printf(("httpRead: data_remaining = %d\n", http->data_remaining));
680
681 if (http->data_remaining == 0)
682 {
683 /*
684 * A zero-length chunk ends a transfer; unless we are reading POST
685 * data, go idle...
686 */
687
688 if (http->state == HTTP_POST_RECV)
689 http->state ++;
690 else
691 http->state = HTTP_WAITING;
692
693 return (0);
694 }
695 else if (length > http->data_remaining)
696 length = http->data_remaining;
697
698 if (http->used > 0)
699 {
700 if (length > http->used)
701 length = http->used;
702
703 bytes = length;
704
705 DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes));
706
707 memcpy(buffer, http->buffer, length);
708 http->used -= length;
709
710 if (http->used > 0)
711 memcpy(http->buffer, http->buffer + length, http->used);
712 }
713 else
714 {
715 DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
716 bytes = recv(http->fd, buffer, length, 0);
717 DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
718 }
719
720 if (bytes > 0)
721 http->data_remaining -= bytes;
722
723 if (http->data_remaining == 0 && http->data_encoding != HTTP_ENCODE_CHUNKED)
724 {
725 if (http->state == HTTP_POST_RECV)
726 http->state ++;
727 else
728 http->state = HTTP_WAITING;
729 }
730
731 return (bytes);
732 }
733
734
735 /*
736 * 'httpWrite()' - Write data to a HTTP connection.
737 */
738
739 int /* O - Number of bytes written */
740 httpWrite(http_t *http, /* I - HTTP data */
741 const char *buffer, /* I - Buffer for data */
742 int length) /* I - Number of bytes to write */
743 {
744 int tbytes, /* Total bytes sent */
745 bytes; /* Bytes sent */
746 char len[32]; /* Length string */
747
748
749 if (http == NULL || buffer == NULL)
750 return (-1);
751
752 http->activity = time(NULL);
753
754 if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
755 (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV ||
756 http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV))
757 {
758 sprintf(len, "%x\r\n", length);
759 if (send(http->fd, len, strlen(len), 0) < 3)
760 return (-1);
761 }
762
763 if (length == 0)
764 {
765 /*
766 * A zero-length chunk ends a transfer; unless we are sending POST
767 * data, go idle...
768 */
769
770 if (http->state == HTTP_POST_RECV)
771 http->state ++;
772 else
773 http->state = HTTP_WAITING;
774
775 return (0);
776 }
777
778 tbytes = 0;
779
780 while (length > 0)
781 {
782 bytes = send(http->fd, buffer, length, 0);
783 if (bytes < 0)
784 {
785 DEBUG_puts("httpWrite: error writing data...\n");
786
787 return (-1);
788 }
789
790 buffer += bytes;
791 tbytes += bytes;
792 length -= bytes;
793 if (http->data_encoding == HTTP_ENCODE_LENGTH)
794 http->data_remaining -= bytes;
795 }
796
797 if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
798 {
799 /*
800 * Finished with the transfer; unless we are sending POST data, go idle...
801 */
802
803 if (http->state == HTTP_POST_RECV)
804 http->state ++;
805 else
806 http->state = HTTP_WAITING;
807 }
808
809 DEBUG_printf(("httpWrite: wrote %d bytes...\n", tbytes));
810
811 return (tbytes);
812 }
813
814
815 /*
816 * 'httpGets()' - Get a line of text from a HTTP connection.
817 */
818
819 char * /* O - Line or NULL */
820 httpGets(char *line, /* I - Line to read into */
821 int length, /* I - Max length of buffer */
822 http_t *http) /* I - HTTP data */
823 {
824 char *lineptr, /* Pointer into line */
825 *bufptr, /* Pointer into input buffer */
826 *bufend; /* Pointer to end of buffer */
827 int bytes; /* Number of bytes read */
828 int lasterror; /* Last error received */
829
830
831 DEBUG_printf(("httpGets(%08x, %d, %08x)\n", line, length, http));
832
833 if (http == NULL || line == NULL)
834 return (NULL);
835
836 /*
837 * Pre-scan the buffer and see if there is a newline in there...
838 */
839
840 lasterror = 0;
841
842 do
843 {
844 bufptr = http->buffer;
845 bufend = http->buffer + http->used;
846
847 while (bufptr < bufend)
848 if (*bufptr == 0x0a)
849 break;
850 else
851 bufptr ++;
852
853 if (bufptr >= bufend)
854 {
855 /*
856 * No newline; see if there is more data to be read...
857 */
858
859 if ((bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0)) < 0)
860 {
861 /*
862 * Nope, can't get a line this time...
863 */
864
865 if (errno == EAGAIN || errno == EINTR)
866 {
867 lasterror = errno;
868 continue;
869 }
870
871 if (errno == EPIPE && lasterror != EPIPE)
872 {
873 lasterror = errno;
874 continue;
875 }
876
877 DEBUG_printf(("httpGets(): recv() error %d!\n", errno));
878
879 return (NULL);
880 }
881 else if (bytes == 0)
882 return (NULL);
883
884 /*
885 * Yup, update the amount used and the end pointer...
886 */
887
888 http->used += bytes;
889 bufend += bytes;
890 }
891 }
892 while (bufptr >= bufend);
893
894 http->activity = time(NULL);
895
896 /*
897 * Read a line from the buffer...
898 */
899
900 lineptr = line;
901 bufptr = http->buffer;
902 bytes = 0;
903
904 while (bufptr < bufend && bytes < length)
905 {
906 bytes ++;
907
908 if (*bufptr == 0x0a)
909 {
910 bufptr ++;
911 *lineptr = '\0';
912
913 http->used -= bytes;
914 if (http->used > 0)
915 memcpy(http->buffer, bufptr, http->used);
916
917 DEBUG_printf(("httpGets(): Returning \"%s\"\n", line));
918 return (line);
919 }
920 else if (*bufptr == 0x0d)
921 bufptr ++;
922 else
923 *lineptr++ = *bufptr++;
924 }
925
926 DEBUG_puts("httpGets(): No new line available!");
927
928 return (NULL);
929 }
930
931
932 /*
933 * 'httpPrintf()' - Print a formatted string to a HTTP connection.
934 */
935
936 int /* O - Number of bytes written */
937 httpPrintf(http_t *http, /* I - HTTP data */
938 const char *format, /* I - printf-style format string */
939 ...) /* I - Additional args as needed */
940 {
941 int bytes; /* Number of bytes to write */
942 char buf[HTTP_MAX_BUFFER]; /* Buffer for formatted string */
943 va_list ap; /* Variable argument pointer */
944
945
946 va_start(ap, format);
947 bytes = vsprintf(buf, format, ap);
948 va_end(ap);
949
950 DEBUG_printf(("httpPrintf: %s", buf));
951
952 return (send(http->fd, buf, bytes, 0));
953 }
954
955
956 /*
957 * 'httpStatus()' - Return a short string describing a HTTP status code.
958 */
959
960 const char * /* O - String or NULL */
961 httpStatus(http_status_t status) /* I - HTTP status code */
962 {
963 switch (status)
964 {
965 case HTTP_OK :
966 return ("OK");
967 case HTTP_CREATED :
968 return ("Created");
969 case HTTP_ACCEPTED :
970 return ("Accepted");
971 case HTTP_NO_CONTENT :
972 return ("No Content");
973 case HTTP_NOT_MODIFIED :
974 return ("Not Modified");
975 case HTTP_BAD_REQUEST :
976 return ("Bad Request");
977 case HTTP_UNAUTHORIZED :
978 return ("Unauthorized");
979 case HTTP_FORBIDDEN :
980 return ("Forbidden");
981 case HTTP_NOT_FOUND :
982 return ("Not Found");
983 case HTTP_REQUEST_TOO_LARGE :
984 return ("Request Entity Too Large");
985 case HTTP_URI_TOO_LONG :
986 return ("URI Too Long");
987 case HTTP_NOT_IMPLEMENTED :
988 return ("Not Implemented");
989 case HTTP_NOT_SUPPORTED :
990 return ("Not Supported");
991 default :
992 return ("Unknown");
993 }
994 }
995
996
997 /*
998 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
999 */
1000
1001 const char * /* O - Date/time string */
1002 httpGetDateString(time_t t) /* I - UNIX time */
1003 {
1004 struct tm *tdate;
1005 static char datetime[256];
1006
1007
1008 tdate = gmtime(&t);
1009 sprintf(datetime, "%s, %02d %s %d %02d:%02d:%02d GMT",
1010 days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon],
1011 tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
1012
1013 return (datetime);
1014 }
1015
1016
1017 /*
1018 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
1019 */
1020
1021 time_t /* O - UNIX time */
1022 httpGetDateTime(const char *s) /* I - Date/time string */
1023 {
1024 int i; /* Looping var */
1025 struct tm tdate; /* Time/date structure */
1026 char mon[16]; /* Abbreviated month name */
1027 int day, year; /* Day of month and year */
1028 int hour, min, sec; /* Time */
1029
1030
1031 if (sscanf(s, "%*s%d%s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
1032 return (0);
1033
1034 for (i = 0; i < 12; i ++)
1035 if (strcasecmp(mon, months[i]) == 0)
1036 break;
1037
1038 if (i >= 12)
1039 return (0);
1040
1041 tdate.tm_mon = i;
1042 tdate.tm_mday = day;
1043 tdate.tm_year = year - 1900;
1044 tdate.tm_hour = hour;
1045 tdate.tm_min = min;
1046 tdate.tm_sec = sec;
1047 tdate.tm_isdst = 0;
1048
1049 return (mktime(&tdate));
1050 }
1051
1052
1053 /*
1054 * 'httpUpdate()' - Update the current HTTP state for incoming data.
1055 */
1056
1057 http_status_t /* O - HTTP status */
1058 httpUpdate(http_t *http) /* I - HTTP data */
1059 {
1060 char line[1024], /* Line from connection... */
1061 *value; /* Pointer to value on line */
1062 http_field_t field; /* Field index */
1063 int major, minor; /* HTTP version numbers */
1064 http_status_t status; /* Authorization status */
1065
1066
1067 DEBUG_printf(("httpUpdate(%08x)\n", http));
1068
1069 /*
1070 * If we haven't issued any commands, then there is nothing to "update"...
1071 */
1072
1073 if (http->state == HTTP_WAITING)
1074 return (HTTP_CONTINUE);
1075
1076 /*
1077 * Grab all of the lines we can from the connection...
1078 */
1079
1080 while (httpGets(line, sizeof(line), http) != NULL)
1081 {
1082 DEBUG_puts(line);
1083
1084 if (line[0] == '\0')
1085 {
1086 /*
1087 * Blank line means the start of the data section (if any). Return
1088 * the result code, too...
1089 *
1090 * If we get status 100 (HTTP_CONTINUE), then we *don't* change states.
1091 * Instead, we just return HTTP_CONTINUE to the caller and keep on
1092 * tryin'...
1093 */
1094
1095 if (http->status == HTTP_CONTINUE)
1096 return (http->status);
1097
1098 httpGetLength(http);
1099
1100 switch (http->state)
1101 {
1102 case HTTP_GET :
1103 case HTTP_POST :
1104 case HTTP_POST_RECV :
1105 case HTTP_PUT :
1106 http->state ++;
1107 break;
1108
1109 default :
1110 http->state = HTTP_WAITING;
1111 break;
1112 }
1113
1114 return (http->status);
1115 }
1116 else if (strncmp(line, "HTTP/", 5) == 0)
1117 {
1118 /*
1119 * Got the beginning of a response...
1120 */
1121
1122 if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3)
1123 return (HTTP_ERROR);
1124
1125 http->version = (http_version_t)(major * 100 + minor);
1126 http->status = status;
1127 }
1128 else if ((value = strchr(line, ':')) != NULL)
1129 {
1130 /*
1131 * Got a value...
1132 */
1133
1134 *value++ = '\0';
1135 while (isspace(*value))
1136 value ++;
1137
1138 /*
1139 * Be tolerants of servers that send unknown attribute fields...
1140 */
1141
1142 if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN)
1143 {
1144 DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line));
1145 continue;
1146 }
1147
1148 httpSetField(http, field, value);
1149 }
1150 else
1151 return (HTTP_ERROR);
1152 }
1153
1154 /*
1155 * See if there was an error...
1156 */
1157
1158 if (errno)
1159 return (HTTP_ERROR);
1160
1161 /*
1162 * If we haven't already returned, then there is nothing new...
1163 */
1164
1165 return (HTTP_CONTINUE);
1166 }
1167
1168
1169 /*
1170 * 'httpDecode64()' - Base64-decode a string.
1171 */
1172
1173 char * /* O - Decoded string */
1174 httpDecode64(char *out, /* I - String to write to */
1175 const char *in) /* I - String to read from */
1176 {
1177 int pos, /* Bit position */
1178 base64; /* Value of this character */
1179 char *outptr; /* Output pointer */
1180
1181
1182 for (outptr = out, pos = 0; *in != '\0'; in ++)
1183 {
1184 /*
1185 * Decode this character into a number from 0 to 63...
1186 */
1187
1188 if (*in >= 'A' && *in <= 'Z')
1189 base64 = *in - 'A';
1190 else if (*in >= 'a' && *in <= 'z')
1191 base64 = *in - 'a' + 26;
1192 else if (*in >= '0' && *in <= '9')
1193 base64 = *in - '0' + 52;
1194 else if (*in == '+')
1195 base64 = 62;
1196 else if (*in == '/')
1197 base64 = 63;
1198 else if (*in == '=')
1199 break;
1200 else
1201 continue;
1202
1203 /*
1204 * Store the result in the appropriate chars...
1205 */
1206
1207 switch (pos)
1208 {
1209 case 0 :
1210 *outptr = base64 << 2;
1211 pos ++;
1212 break;
1213 case 1 :
1214 *outptr++ |= (base64 >> 4) & 3;
1215 *outptr = (base64 << 4) & 255;
1216 pos ++;
1217 break;
1218 case 2 :
1219 *outptr++ |= (base64 >> 2) & 15;
1220 *outptr = (base64 << 6) & 255;
1221 pos ++;
1222 break;
1223 case 3 :
1224 *outptr++ |= base64;
1225 pos = 0;
1226 break;
1227 }
1228 }
1229
1230 *outptr = '\0';
1231
1232 /*
1233 * Return the decoded string...
1234 */
1235
1236 return (out);
1237 }
1238
1239
1240 /*
1241 * 'httpEncode64()' - Base64-encode a string.
1242 */
1243
1244 char * /* O - Encoded string */
1245 httpEncode64(char *out, /* I - String to write to */
1246 const char *in) /* I - String to read from */
1247 {
1248 char *outptr; /* Output pointer */
1249 static char base64[] = /* Base64 characters... */
1250 {
1251 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1252 "abcdefghijklmnopqrstuvwxyz"
1253 "0123456789"
1254 "+/"
1255 };
1256
1257
1258 for (outptr = out; *in != '\0'; in ++)
1259 {
1260 /*
1261 * Encode the up to 3 characters as 4 Base64 numbers...
1262 */
1263
1264 *outptr ++ = base64[in[0] >> 2];
1265 *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63];
1266
1267 in ++;
1268 if (*in == '\0')
1269 break;
1270
1271 *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63];
1272
1273 in ++;
1274 if (*in == '\0')
1275 break;
1276
1277 *outptr ++ = base64[in[0] & 63];
1278 }
1279
1280 *outptr ++ = '=';
1281 *outptr = '\0';
1282
1283 /*
1284 * Return the encoded string...
1285 */
1286
1287 return (out);
1288 }
1289
1290
1291 /*
1292 * 'httpGetLength()' - Get the amount of data remaining from the
1293 * content-length or transfer-encoding fields.
1294 */
1295
1296 int /* O - Content length */
1297 httpGetLength(http_t *http) /* I - HTTP data */
1298 {
1299 if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
1300 {
1301 http->data_encoding = HTTP_ENCODE_CHUNKED;
1302 http->data_remaining = 0;
1303 }
1304 else
1305 {
1306 http->data_encoding = HTTP_ENCODE_LENGTH;
1307
1308 /*
1309 * The following is a hack for HTTP servers that don't send a
1310 * content-length or transfer-encoding field...
1311 *
1312 * If there is no content-length then the connection must close
1313 * after the transfer is complete...
1314 */
1315
1316 if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0')
1317 http->data_remaining = 2147483647;
1318 else
1319 http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]);
1320 }
1321
1322 return (http->data_remaining);
1323 }
1324
1325
1326 /*
1327 * 'http_field()' - Return the field index for a field name.
1328 */
1329
1330 static http_field_t /* O - Field index */
1331 http_field(const char *name) /* I - String name */
1332 {
1333 int i; /* Looping var */
1334
1335
1336 for (i = 0; i < HTTP_FIELD_MAX; i ++)
1337 if (strcasecmp(name, http_fields[i]) == 0)
1338 return ((http_field_t)i);
1339
1340 return (HTTP_FIELD_UNKNOWN);
1341 }
1342
1343
1344 /*
1345 * 'http_send()' - Send a request with all fields and the trailing blank line.
1346 */
1347
1348 static int /* O - 0 on success, non-zero on error */
1349 http_send(http_t *http, /* I - HTTP data */
1350 http_state_t request, /* I - Request code */
1351 const char *uri) /* I - URI */
1352 {
1353 int i; /* Looping var */
1354 char *ptr, /* Pointer in buffer */
1355 buf[1024]; /* Encoded URI buffer */
1356 static const char *codes[] = /* Request code strings */
1357 {
1358 NULL,
1359 "OPTIONS",
1360 "GET",
1361 NULL,
1362 "HEAD",
1363 "POST",
1364 NULL,
1365 NULL,
1366 "PUT",
1367 NULL,
1368 "DELETE",
1369 "TRACE",
1370 "CLOSE"
1371 };
1372 static const char *hex = "0123456789ABCDEF";
1373 /* Hex digits */
1374
1375
1376 if (http == NULL || uri == NULL)
1377 return (-1);
1378
1379 /*
1380 * Encode the URI as needed...
1381 */
1382
1383 for (ptr = buf; *uri != '\0'; uri ++)
1384 if (*uri <= ' ' || *uri >= 127)
1385 {
1386 *ptr ++ = '%';
1387 *ptr ++ = hex[(*uri >> 4) & 15];
1388 *ptr ++ = hex[*uri & 15];
1389 }
1390 else
1391 *ptr ++ = *uri;
1392
1393 *ptr = '\0';
1394
1395 /*
1396 * Send the request header...
1397 */
1398
1399 http->state = request;
1400 if (request == HTTP_POST || request == HTTP_PUT)
1401 http->state ++;
1402
1403 http->status = HTTP_CONTINUE;
1404
1405 if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
1406 {
1407 /*
1408 * Might have lost connection; try to reconnect...
1409 */
1410
1411 if (httpReconnect(http))
1412 return (-1);
1413
1414 /*
1415 * OK, we've reconnected, send the request again...
1416 */
1417
1418 if (httpPrintf(http, "%s %s HTTP/%d.%d\r\n", codes[request], buf,
1419 http->version / 100, http->version % 100) < 1)
1420 return (-1);
1421 }
1422
1423 for (i = 0; i < HTTP_FIELD_MAX; i ++)
1424 if (http->fields[i][0] != '\0')
1425 {
1426 DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i]));
1427
1428 if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1)
1429 return (-1);
1430 }
1431
1432 if (httpPrintf(http, "\r\n") < 1)
1433 return (-1);
1434
1435 httpClearFields(http);
1436
1437 return (0);
1438 }
1439
1440
1441 /*
1442 * End of "$Id: http.c,v 1.43 1999/08/16 15:38:18 mike Exp $".
1443 */