]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http.c
Reverted snprintf's to using just sizeof(); a quick test program
[thirdparty/cups.git] / cups / http.c
1 /*
2 * "$Id: http.c,v 1.68 2000/11/03 14:13:26 mike Exp $"
3 *
4 * HTTP routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2000 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 if (host == NULL)
263 return (NULL);
264
265 httpInitialize();
266
267 /*
268 * Lookup the host...
269 */
270
271 if ((hostaddr = gethostbyname(host)) == NULL)
272 return (NULL);
273
274 /*
275 * Allocate memory for the structure...
276 */
277
278 http = calloc(sizeof(http_t), 1);
279 if (http == NULL)
280 return (NULL);
281
282 http->version = HTTP_1_1;
283 http->blocking = 1;
284 http->activity = time(NULL);
285
286 /*
287 * Copy the hostname and port and then "reconnect"...
288 */
289
290 strncpy(http->hostname, host, sizeof(http->hostname) - 1);
291 memcpy((char *)&(http->hostaddr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
292 http->hostaddr.sin_family = hostaddr->h_addrtype;
293 #ifdef WIN32
294 http->hostaddr.sin_port = htons((u_short)port);
295 #else
296 http->hostaddr.sin_port = htons(port);
297 #endif /* WIN32 */
298 if (httpReconnect(http))
299 {
300 free(http);
301 return (NULL);
302 }
303 else
304 return (http);
305 }
306
307
308 /*
309 * 'httpReconnect()' - Reconnect to a HTTP server...
310 */
311
312 int /* O - 0 on success, non-zero on failure */
313 httpReconnect(http_t *http) /* I - HTTP data */
314 {
315 int val; /* Socket option value */
316
317
318 /*
319 * Close any previously open socket...
320 */
321
322 if (http->fd)
323 #ifdef WIN32
324 closesocket(http->fd);
325 #else
326 close(http->fd);
327 #endif /* WIN32 */
328
329 /*
330 * Create the socket and set options to allow reuse.
331 */
332
333 if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
334 {
335 #if defined(WIN32) || defined(__EMX__)
336 http->error = WSAGetLastError();
337 #else
338 http->error = errno;
339 #endif /* WIN32 || __EMX__ */
340 http->status = HTTP_ERROR;
341 return (-1);
342 }
343
344 #ifdef FD_CLOEXEC
345 fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
346 * other processes... */
347 #endif /* FD_CLOEXEC */
348
349 val = 1;
350 setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
351
352 #ifdef SO_REUSEPORT
353 val = 1;
354 setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
355 #endif /* SO_REUSEPORT */
356
357 /*
358 * Connect to the server...
359 */
360
361 if (connect(http->fd, (struct sockaddr *)&(http->hostaddr),
362 sizeof(http->hostaddr)) < 0)
363 {
364 #if defined(WIN32) || defined(__EMX__)
365 http->error = WSAGetLastError();
366 #else
367 http->error = errno;
368 #endif /* WIN32 || __EMX__ */
369 http->status = HTTP_ERROR;
370
371 #ifdef WIN32
372 closesocket(http->fd);
373 #else
374 close(http->fd);
375 #endif
376
377 return (-1);
378 }
379
380 http->error = 0;
381 http->status = HTTP_CONTINUE;
382
383 return (0);
384 }
385
386
387 /*
388 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
389 * components.
390 */
391
392 void
393 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
394 char *method, /* O - Method [32] (http, https, etc.) */
395 char *username, /* O - Username [32] */
396 char *host, /* O - Hostname [32] */
397 int *port, /* O - Port number to use */
398 char *resource) /* O - Resource/filename [1024] */
399 {
400 char *ptr; /* Pointer into string... */
401 const char *atsign, /* @ sign */
402 *slash; /* Separator */
403
404
405 if (uri == NULL || method == NULL || username == NULL || host == NULL ||
406 port == NULL || resource == NULL)
407 return;
408
409 /*
410 * Grab the method portion of the URI...
411 */
412
413 if (strncmp(uri, "//", 2) == 0)
414 {
415 /*
416 * Workaround for HP IPP client bug...
417 */
418
419 strcpy(method, "ipp");
420 }
421 else
422 {
423 /*
424 * Standard URI with method...
425 */
426
427 ptr = host;
428 while (*uri != ':' && *uri != '\0')
429 *ptr++ = *uri++;
430
431 *ptr = '\0';
432 if (*uri == ':')
433 uri ++;
434
435 /*
436 * If the method contains a period or slash, then it's probably
437 * hostname/filename...
438 */
439
440 if (strchr(host, '.') != NULL || strchr(host, '/') != NULL || *uri == '\0')
441 {
442 if ((ptr = strchr(host, '/')) != NULL)
443 {
444 strncpy(resource, ptr, HTTP_MAX_URI - 1);
445 resource[HTTP_MAX_URI - 1] = '\0';
446 *ptr = '\0';
447 }
448 else
449 resource[0] = '\0';
450
451 if (isdigit(*uri))
452 {
453 /*
454 * OK, we have "hostname:port[/resource]"...
455 */
456
457 *port = strtol(uri, (char **)&uri, 10);
458
459 if (*uri == '/')
460 {
461 strncpy(resource, uri, HTTP_MAX_URI - 1);
462 resource[HTTP_MAX_URI - 1] = '\0';
463 }
464 }
465 else
466 *port = 631;
467
468 strcpy(method, "http");
469 username[0] = '\0';
470 return;
471 }
472 else
473 {
474 strncpy(method, host, 31);
475 method[31] = '\0';
476 }
477 }
478
479 /*
480 * If the method starts with less than 2 slashes then it is a local resource...
481 */
482
483 if (strncmp(uri, "//", 2) != 0)
484 {
485 strncpy(resource, uri, 1023);
486 resource[1023] = '\0';
487
488 username[0] = '\0';
489 host[0] = '\0';
490 *port = 0;
491 return;
492 }
493
494 /*
495 * Grab the usenname, if any...
496 */
497
498 while (*uri == '/')
499 uri ++;
500
501 if ((slash = strchr(uri, '/')) == NULL)
502 slash = uri + strlen(uri);
503
504 if ((atsign = strchr(uri, '@')) != NULL && atsign < slash)
505 {
506 /*
507 * Got a username:password combo...
508 */
509
510 for (ptr = username;
511 uri < atsign && ptr < (username + sizeof(username) - 1);
512 *ptr++ = *uri++);
513
514 *ptr = '\0';
515
516 uri = atsign + 1;
517 }
518 else
519 username[0] = '\0';
520
521 /*
522 * Grab the hostname...
523 */
524
525 ptr = host;
526 while (*uri != ':' && *uri != '/' && *uri != '\0')
527 *ptr ++ = *uri ++;
528
529 *ptr = '\0';
530
531 if (*uri != ':')
532 {
533 if (strcasecmp(method, "http") == 0)
534 *port = 80;
535 else if (strcasecmp(method, "https") == 0)
536 *port = 443;
537 else if (strcasecmp(method, "ipp") == 0)
538 *port = ippPort();
539 else if (strcasecmp(method, "socket") == 0) /* Not registered yet... */
540 *port = 9100;
541 else
542 *port = 0;
543 }
544 else
545 {
546 /*
547 * Parse port number...
548 */
549
550 *port = 0;
551 uri ++;
552 while (isdigit(*uri))
553 {
554 *port = (*port * 10) + *uri - '0';
555 uri ++;
556 }
557 }
558
559 if (*uri == '\0')
560 {
561 /*
562 * Hostname but no port or path...
563 */
564
565 resource[0] = '/';
566 resource[1] = '\0';
567 return;
568 }
569
570 /*
571 * The remaining portion is the resource string...
572 */
573
574 strncpy(resource, uri, HTTP_MAX_URI - 1);
575 resource[HTTP_MAX_URI - 1] = '\0';
576 }
577
578
579 /*
580 * 'httpGetSubField()' - Get a sub-field value.
581 */
582
583 char * /* O - Value or NULL */
584 httpGetSubField(http_t *http, /* I - HTTP data */
585 http_field_t field, /* I - Field index */
586 const char *name, /* I - Name of sub-field */
587 char *value) /* O - Value string */
588 {
589 const char *fptr; /* Pointer into field */
590 char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
591 *ptr; /* Pointer into string buffer */
592
593
594 if (http == NULL ||
595 field < HTTP_FIELD_ACCEPT_LANGUAGE ||
596 field > HTTP_FIELD_WWW_AUTHENTICATE ||
597 name == NULL || value == NULL)
598 return (NULL);
599
600 for (fptr = http->fields[field]; *fptr;)
601 {
602 /*
603 * Skip leading whitespace...
604 */
605
606 while (isspace(*fptr));
607 fptr ++;
608
609 if (*fptr == ',')
610 {
611 fptr ++;
612 continue;
613 }
614
615 /*
616 * Get the sub-field name...
617 */
618
619 for (ptr = temp;
620 *fptr && *fptr != '=' && !isspace(*fptr) && ptr < (temp + sizeof(temp) - 1);
621 *ptr++ = *fptr++);
622
623 *ptr = '\0';
624
625 /*
626 * Skip trailing chars up to the '='...
627 */
628
629 while (*fptr && *fptr != '=')
630 fptr ++;
631
632 if (!*fptr)
633 break;
634
635 /*
636 * Skip = and leading whitespace...
637 */
638
639 fptr ++;
640
641 while (isspace(*fptr));
642 fptr ++;
643
644 if (*fptr == '\"')
645 {
646 /*
647 * Read quoted string...
648 */
649
650 for (ptr = value, fptr ++;
651 *fptr && *fptr != '\"' && ptr < (value + HTTP_MAX_VALUE - 1);
652 *ptr++ = *fptr++);
653
654 *ptr = '\0';
655
656 while (*fptr && *fptr != '\"')
657 fptr ++;
658
659 if (*fptr)
660 fptr ++;
661 }
662 else
663 {
664 /*
665 * Read unquoted string...
666 */
667
668 for (ptr = value;
669 *fptr && !isspace(*fptr) && *fptr != ',' && ptr < (value + HTTP_MAX_VALUE - 1);
670 *ptr++ = *fptr++);
671
672 *ptr = '\0';
673
674 while (*fptr && !isspace(*fptr) && *fptr != ',')
675 fptr ++;
676 }
677
678 /*
679 * See if this is the one...
680 */
681
682 if (strcmp(name, temp) == 0)
683 return (value);
684 }
685
686 value[0] = '\0';
687
688 return (NULL);
689 }
690
691
692 /*
693 * 'httpSetField()' - Set the value of an HTTP header.
694 */
695
696 void
697 httpSetField(http_t *http, /* I - HTTP data */
698 http_field_t field, /* I - Field index */
699 const char *value) /* I - Value */
700 {
701 if (http == NULL ||
702 field < HTTP_FIELD_ACCEPT_LANGUAGE ||
703 field > HTTP_FIELD_WWW_AUTHENTICATE ||
704 value == NULL)
705 return;
706
707 strncpy(http->fields[field], value, HTTP_MAX_VALUE - 1);
708 http->fields[field][HTTP_MAX_VALUE - 1] = '\0';
709 }
710
711
712 /*
713 * 'httpDelete()' - Send a DELETE request to the server.
714 */
715
716 int /* O - Status of call (0 = success) */
717 httpDelete(http_t *http, /* I - HTTP data */
718 const char *uri) /* I - URI to delete */
719 {
720 return (http_send(http, HTTP_DELETE, uri));
721 }
722
723
724 /*
725 * 'httpGet()' - Send a GET request to the server.
726 */
727
728 int /* O - Status of call (0 = success) */
729 httpGet(http_t *http, /* I - HTTP data */
730 const char *uri) /* I - URI to get */
731 {
732 return (http_send(http, HTTP_GET, uri));
733 }
734
735
736 /*
737 * 'httpHead()' - Send a HEAD request to the server.
738 */
739
740 int /* O - Status of call (0 = success) */
741 httpHead(http_t *http, /* I - HTTP data */
742 const char *uri) /* I - URI for head */
743 {
744 return (http_send(http, HTTP_HEAD, uri));
745 }
746
747
748 /*
749 * 'httpOptions()' - Send an OPTIONS request to the server.
750 */
751
752 int /* O - Status of call (0 = success) */
753 httpOptions(http_t *http, /* I - HTTP data */
754 const char *uri) /* I - URI for options */
755 {
756 return (http_send(http, HTTP_OPTIONS, uri));
757 }
758
759
760 /*
761 * 'httpPost()' - Send a POST request to the server.
762 */
763
764 int /* O - Status of call (0 = success) */
765 httpPost(http_t *http, /* I - HTTP data */
766 const char *uri) /* I - URI for post */
767 {
768 httpGetLength(http);
769
770 return (http_send(http, HTTP_POST, uri));
771 }
772
773
774 /*
775 * 'httpPut()' - Send a PUT request to the server.
776 */
777
778 int /* O - Status of call (0 = success) */
779 httpPut(http_t *http, /* I - HTTP data */
780 const char *uri) /* I - URI to put */
781 {
782 httpGetLength(http);
783
784 return (http_send(http, HTTP_PUT, uri));
785 }
786
787
788 /*
789 * 'httpTrace()' - Send an TRACE request to the server.
790 */
791
792 int /* O - Status of call (0 = success) */
793 httpTrace(http_t *http, /* I - HTTP data */
794 const char *uri) /* I - URI for trace */
795 {
796 return (http_send(http, HTTP_TRACE, uri));
797 }
798
799
800 /*
801 * 'httpFlush()' - Flush data from a HTTP connection.
802 */
803
804 void
805 httpFlush(http_t *http) /* I - HTTP data */
806 {
807 char buffer[8192]; /* Junk buffer */
808
809
810 while (httpRead(http, buffer, sizeof(buffer)) > 0);
811 }
812
813
814 /*
815 * 'httpRead()' - Read data from a HTTP connection.
816 */
817
818 int /* O - Number of bytes read */
819 httpRead(http_t *http, /* I - HTTP data */
820 char *buffer, /* I - Buffer for data */
821 int length) /* I - Maximum number of bytes */
822 {
823 int bytes; /* Bytes read */
824 char len[32]; /* Length string */
825
826
827 DEBUG_printf(("httpRead(%08x, %08x, %d)\n", http, buffer, length));
828
829 if (http == NULL || buffer == NULL)
830 return (-1);
831
832 http->activity = time(NULL);
833
834 if (length <= 0)
835 return (0);
836
837 if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
838 http->data_remaining <= 0)
839 {
840 DEBUG_puts("httpRead: Getting chunk length...");
841
842 if (httpGets(len, sizeof(len), http) == NULL)
843 {
844 DEBUG_puts("httpRead: Could not get length!");
845 return (0);
846 }
847
848 http->data_remaining = strtol(len, NULL, 16);
849 }
850
851 DEBUG_printf(("httpRead: data_remaining = %d\n", http->data_remaining));
852
853 if (http->data_remaining == 0)
854 {
855 /*
856 * A zero-length chunk ends a transfer; unless we are reading POST
857 * data, go idle...
858 */
859
860 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
861 httpGets(len, sizeof(len), http);
862
863 if (http->state == HTTP_POST_RECV)
864 http->state ++;
865 else
866 http->state = HTTP_WAITING;
867
868 return (0);
869 }
870 else if (length > http->data_remaining)
871 length = http->data_remaining;
872
873 if (http->used > 0)
874 {
875 if (length > http->used)
876 length = http->used;
877
878 bytes = length;
879
880 DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes));
881
882 memcpy(buffer, http->buffer, length);
883 http->used -= length;
884
885 if (http->used > 0)
886 memcpy(http->buffer, http->buffer + length, http->used);
887 }
888 else
889 {
890 DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
891 bytes = recv(http->fd, buffer, length, 0);
892 DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
893 }
894
895 if (bytes > 0)
896 http->data_remaining -= bytes;
897 else if (bytes < 0)
898 #if defined(WIN32) || defined(__EMX__)
899 http->error = WSAGetLastError();
900 #else
901 http->error = errno;
902 #endif /* WIN32 || __EMX__ */
903
904 if (http->data_remaining == 0)
905 {
906 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
907 httpGets(len, sizeof(len), http);
908
909 if (http->data_encoding != HTTP_ENCODE_CHUNKED)
910 {
911 if (http->state == HTTP_POST_RECV)
912 http->state ++;
913 else
914 http->state = HTTP_WAITING;
915 }
916 }
917
918 return (bytes);
919 }
920
921
922 /*
923 * 'httpWrite()' - Write data to a HTTP connection.
924 */
925
926 int /* O - Number of bytes written */
927 httpWrite(http_t *http, /* I - HTTP data */
928 const char *buffer, /* I - Buffer for data */
929 int length) /* I - Number of bytes to write */
930 {
931 int tbytes, /* Total bytes sent */
932 bytes; /* Bytes sent */
933
934
935 if (http == NULL || buffer == NULL)
936 return (-1);
937
938 http->activity = time(NULL);
939
940 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
941 {
942 if (httpPrintf(http, "%x\r\n", length) < 0)
943 return (-1);
944
945 if (length == 0)
946 {
947 /*
948 * A zero-length chunk ends a transfer; unless we are sending POST
949 * data, go idle...
950 */
951
952 DEBUG_puts("httpWrite: changing states...");
953
954 if (http->state == HTTP_POST_RECV)
955 http->state ++;
956 else
957 http->state = HTTP_WAITING;
958
959 if (httpPrintf(http, "\r\n") < 0)
960 return (-1);
961
962 return (0);
963 }
964 }
965
966 tbytes = 0;
967
968 while (length > 0)
969 {
970 bytes = send(http->fd, buffer, length, 0);
971 if (bytes < 0)
972 {
973 DEBUG_puts("httpWrite: error writing data...\n");
974
975 return (-1);
976 }
977
978 buffer += bytes;
979 tbytes += bytes;
980 length -= bytes;
981 if (http->data_encoding == HTTP_ENCODE_LENGTH)
982 http->data_remaining -= bytes;
983 }
984
985 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
986 if (httpPrintf(http, "\r\n") < 0)
987 return (-1);
988
989 if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
990 {
991 /*
992 * Finished with the transfer; unless we are sending POST data, go idle...
993 */
994
995 DEBUG_puts("httpWrite: changing states...");
996
997 if (http->state == HTTP_POST_RECV)
998 http->state ++;
999 else
1000 http->state = HTTP_WAITING;
1001 }
1002
1003 DEBUG_printf(("httpWrite: wrote %d bytes...\n", tbytes));
1004
1005 return (tbytes);
1006 }
1007
1008
1009 /*
1010 * 'httpGets()' - Get a line of text from a HTTP connection.
1011 */
1012
1013 char * /* O - Line or NULL */
1014 httpGets(char *line, /* I - Line to read into */
1015 int length, /* I - Max length of buffer */
1016 http_t *http) /* I - HTTP data */
1017 {
1018 char *lineptr, /* Pointer into line */
1019 *bufptr, /* Pointer into input buffer */
1020 *bufend; /* Pointer to end of buffer */
1021 int bytes; /* Number of bytes read */
1022
1023
1024 DEBUG_printf(("httpGets(%08x, %d, %08x)\n", line, length, http));
1025
1026 if (http == NULL || line == NULL)
1027 return (NULL);
1028
1029 /*
1030 * Pre-scan the buffer and see if there is a newline in there...
1031 */
1032
1033 #if defined(WIN32) || defined(__EMX__)
1034 WSASetLastError(0);
1035 #else
1036 errno = 0;
1037 #endif /* WIN32 || __EMX__ */
1038
1039 do
1040 {
1041 bufptr = http->buffer;
1042 bufend = http->buffer + http->used;
1043
1044 while (bufptr < bufend)
1045 if (*bufptr == 0x0a)
1046 break;
1047 else
1048 bufptr ++;
1049
1050 if (bufptr >= bufend && http->used < HTTP_MAX_BUFFER)
1051 {
1052 /*
1053 * No newline; see if there is more data to be read...
1054 */
1055
1056 if ((bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0)) < 0)
1057 {
1058 /*
1059 * Nope, can't get a line this time...
1060 */
1061
1062 #if defined(WIN32) || defined(__EMX__)
1063 if (WSAGetLastError() != http->error)
1064 {
1065 http->error = WSAGetLastError();
1066 continue;
1067 }
1068
1069 DEBUG_printf(("httpGets(): recv() error %d!\n", WSAGetLastError()));
1070 #else
1071 if (errno != http->error)
1072 {
1073 http->error = errno;
1074 continue;
1075 }
1076
1077 DEBUG_printf(("httpGets(): recv() error %d!\n", errno));
1078 #endif /* WIN32 || __EMX__ */
1079
1080 return (NULL);
1081 }
1082 else if (bytes == 0)
1083 {
1084 if (http->blocking)
1085 http->error = EPIPE;
1086
1087 return (NULL);
1088 }
1089
1090 /*
1091 * Yup, update the amount used and the end pointer...
1092 */
1093
1094 http->used += bytes;
1095 bufend += bytes;
1096 }
1097 }
1098 while (bufptr >= bufend && http->used < HTTP_MAX_BUFFER);
1099
1100 http->activity = time(NULL);
1101
1102 /*
1103 * Read a line from the buffer...
1104 */
1105
1106 lineptr = line;
1107 bufptr = http->buffer;
1108 bytes = 0;
1109
1110 while (bufptr < bufend && bytes < length)
1111 {
1112 bytes ++;
1113
1114 if (*bufptr == 0x0a)
1115 {
1116 bufptr ++;
1117 *lineptr = '\0';
1118
1119 http->used -= bytes;
1120 if (http->used > 0)
1121 memcpy(http->buffer, bufptr, http->used);
1122
1123 DEBUG_printf(("httpGets(): Returning \"%s\"\n", line));
1124 return (line);
1125 }
1126 else if (*bufptr == 0x0d)
1127 bufptr ++;
1128 else
1129 *lineptr++ = *bufptr++;
1130 }
1131
1132 DEBUG_puts("httpGets(): No new line available!");
1133
1134 return (NULL);
1135 }
1136
1137
1138 /*
1139 * 'httpPrintf()' - Print a formatted string to a HTTP connection.
1140 */
1141
1142 int /* O - Number of bytes written */
1143 httpPrintf(http_t *http, /* I - HTTP data */
1144 const char *format, /* I - printf-style format string */
1145 ...) /* I - Additional args as needed */
1146 {
1147 int bytes, /* Number of bytes to write */
1148 nbytes, /* Number of bytes written */
1149 tbytes; /* Number of bytes all together */
1150 char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */
1151 *bufptr; /* Pointer into buffer */
1152 va_list ap; /* Variable argument pointer */
1153
1154
1155 va_start(ap, format);
1156 bytes = vsnprintf(buf, sizeof(buf), format, ap);
1157 va_end(ap);
1158
1159 DEBUG_printf(("httpPrintf: %s", buf));
1160
1161 for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes)
1162 if ((nbytes = send(http->fd, bufptr, bytes - tbytes, 0)) < 0)
1163 return (-1);
1164
1165 return (bytes);
1166 }
1167
1168
1169 /*
1170 * 'httpStatus()' - Return a short string describing a HTTP status code.
1171 */
1172
1173 const char * /* O - String or NULL */
1174 httpStatus(http_status_t status) /* I - HTTP status code */
1175 {
1176 switch (status)
1177 {
1178 case HTTP_OK :
1179 return ("OK");
1180 case HTTP_CREATED :
1181 return ("Created");
1182 case HTTP_ACCEPTED :
1183 return ("Accepted");
1184 case HTTP_NO_CONTENT :
1185 return ("No Content");
1186 case HTTP_NOT_MODIFIED :
1187 return ("Not Modified");
1188 case HTTP_BAD_REQUEST :
1189 return ("Bad Request");
1190 case HTTP_UNAUTHORIZED :
1191 return ("Unauthorized");
1192 case HTTP_FORBIDDEN :
1193 return ("Forbidden");
1194 case HTTP_NOT_FOUND :
1195 return ("Not Found");
1196 case HTTP_REQUEST_TOO_LARGE :
1197 return ("Request Entity Too Large");
1198 case HTTP_URI_TOO_LONG :
1199 return ("URI Too Long");
1200 case HTTP_NOT_IMPLEMENTED :
1201 return ("Not Implemented");
1202 case HTTP_NOT_SUPPORTED :
1203 return ("Not Supported");
1204 default :
1205 return ("Unknown");
1206 }
1207 }
1208
1209
1210 /*
1211 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
1212 */
1213
1214 const char * /* O - Date/time string */
1215 httpGetDateString(time_t t) /* I - UNIX time */
1216 {
1217 struct tm *tdate;
1218 static char datetime[256];
1219
1220
1221 tdate = gmtime(&t);
1222 sprintf(datetime, "%s, %02d %s %d %02d:%02d:%02d GMT",
1223 days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon],
1224 tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
1225
1226 return (datetime);
1227 }
1228
1229
1230 /*
1231 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
1232 */
1233
1234 time_t /* O - UNIX time */
1235 httpGetDateTime(const char *s) /* I - Date/time string */
1236 {
1237 int i; /* Looping var */
1238 struct tm tdate; /* Time/date structure */
1239 char mon[16]; /* Abbreviated month name */
1240 int day, year; /* Day of month and year */
1241 int hour, min, sec; /* Time */
1242
1243
1244 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
1245 return (0);
1246
1247 for (i = 0; i < 12; i ++)
1248 if (strcasecmp(mon, months[i]) == 0)
1249 break;
1250
1251 if (i >= 12)
1252 return (0);
1253
1254 tdate.tm_mon = i;
1255 tdate.tm_mday = day;
1256 tdate.tm_year = year - 1900;
1257 tdate.tm_hour = hour;
1258 tdate.tm_min = min;
1259 tdate.tm_sec = sec;
1260 tdate.tm_isdst = 0;
1261
1262 return (mktime(&tdate));
1263 }
1264
1265
1266 /*
1267 * 'httpUpdate()' - Update the current HTTP state for incoming data.
1268 */
1269
1270 http_status_t /* O - HTTP status */
1271 httpUpdate(http_t *http) /* I - HTTP data */
1272 {
1273 char line[1024], /* Line from connection... */
1274 *value; /* Pointer to value on line */
1275 http_field_t field; /* Field index */
1276 int major, minor; /* HTTP version numbers */
1277 http_status_t status; /* Authorization status */
1278
1279
1280 DEBUG_printf(("httpUpdate(%08x)\n", http));
1281
1282 /*
1283 * If we haven't issued any commands, then there is nothing to "update"...
1284 */
1285
1286 if (http->state == HTTP_WAITING)
1287 return (HTTP_CONTINUE);
1288
1289 /*
1290 * Grab all of the lines we can from the connection...
1291 */
1292
1293 while (httpGets(line, sizeof(line), http) != NULL)
1294 {
1295 DEBUG_puts(line);
1296
1297 if (line[0] == '\0')
1298 {
1299 /*
1300 * Blank line means the start of the data section (if any). Return
1301 * the result code, too...
1302 *
1303 * If we get status 100 (HTTP_CONTINUE), then we *don't* change states.
1304 * Instead, we just return HTTP_CONTINUE to the caller and keep on
1305 * tryin'...
1306 */
1307
1308 if (http->status == HTTP_CONTINUE)
1309 return (http->status);
1310
1311 httpGetLength(http);
1312
1313 switch (http->state)
1314 {
1315 case HTTP_GET :
1316 case HTTP_POST :
1317 case HTTP_POST_RECV :
1318 case HTTP_PUT :
1319 http->state ++;
1320 break;
1321
1322 default :
1323 http->state = HTTP_WAITING;
1324 break;
1325 }
1326
1327 return (http->status);
1328 }
1329 else if (strncmp(line, "HTTP/", 5) == 0)
1330 {
1331 /*
1332 * Got the beginning of a response...
1333 */
1334
1335 if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, (int *)&status) != 3)
1336 return (HTTP_ERROR);
1337
1338 http->version = (http_version_t)(major * 100 + minor);
1339 http->status = status;
1340 }
1341 else if ((value = strchr(line, ':')) != NULL)
1342 {
1343 /*
1344 * Got a value...
1345 */
1346
1347 *value++ = '\0';
1348 while (isspace(*value))
1349 value ++;
1350
1351 /*
1352 * Be tolerants of servers that send unknown attribute fields...
1353 */
1354
1355 if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN)
1356 {
1357 DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line));
1358 continue;
1359 }
1360
1361 httpSetField(http, field, value);
1362 }
1363 else
1364 {
1365 http->status = HTTP_ERROR;
1366 return (HTTP_ERROR);
1367 }
1368 }
1369
1370 /*
1371 * See if there was an error...
1372 */
1373
1374 if (http->error)
1375 {
1376 http->status = HTTP_ERROR;
1377 return (HTTP_ERROR);
1378 }
1379
1380 /*
1381 * If we haven't already returned, then there is nothing new...
1382 */
1383
1384 return (HTTP_CONTINUE);
1385 }
1386
1387
1388 /*
1389 * 'httpDecode64()' - Base64-decode a string.
1390 */
1391
1392 char * /* O - Decoded string */
1393 httpDecode64(char *out, /* I - String to write to */
1394 const char *in) /* I - String to read from */
1395 {
1396 int pos, /* Bit position */
1397 base64; /* Value of this character */
1398 char *outptr; /* Output pointer */
1399
1400
1401 for (outptr = out, pos = 0; *in != '\0'; in ++)
1402 {
1403 /*
1404 * Decode this character into a number from 0 to 63...
1405 */
1406
1407 if (*in >= 'A' && *in <= 'Z')
1408 base64 = *in - 'A';
1409 else if (*in >= 'a' && *in <= 'z')
1410 base64 = *in - 'a' + 26;
1411 else if (*in >= '0' && *in <= '9')
1412 base64 = *in - '0' + 52;
1413 else if (*in == '+')
1414 base64 = 62;
1415 else if (*in == '/')
1416 base64 = 63;
1417 else if (*in == '=')
1418 break;
1419 else
1420 continue;
1421
1422 /*
1423 * Store the result in the appropriate chars...
1424 */
1425
1426 switch (pos)
1427 {
1428 case 0 :
1429 *outptr = base64 << 2;
1430 pos ++;
1431 break;
1432 case 1 :
1433 *outptr++ |= (base64 >> 4) & 3;
1434 *outptr = (base64 << 4) & 255;
1435 pos ++;
1436 break;
1437 case 2 :
1438 *outptr++ |= (base64 >> 2) & 15;
1439 *outptr = (base64 << 6) & 255;
1440 pos ++;
1441 break;
1442 case 3 :
1443 *outptr++ |= base64;
1444 pos = 0;
1445 break;
1446 }
1447 }
1448
1449 *outptr = '\0';
1450
1451 /*
1452 * Return the decoded string...
1453 */
1454
1455 return (out);
1456 }
1457
1458
1459 /*
1460 * 'httpEncode64()' - Base64-encode a string.
1461 */
1462
1463 char * /* O - Encoded string */
1464 httpEncode64(char *out, /* I - String to write to */
1465 const char *in) /* I - String to read from */
1466 {
1467 char *outptr; /* Output pointer */
1468 static char base64[] = /* Base64 characters... */
1469 {
1470 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1471 "abcdefghijklmnopqrstuvwxyz"
1472 "0123456789"
1473 "+/"
1474 };
1475
1476
1477 for (outptr = out; *in != '\0'; in ++)
1478 {
1479 /*
1480 * Encode the up to 3 characters as 4 Base64 numbers...
1481 */
1482
1483 *outptr ++ = base64[in[0] >> 2];
1484 *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63];
1485
1486 in ++;
1487 if (*in == '\0')
1488 {
1489 *outptr ++ = '=';
1490 break;
1491 }
1492
1493 *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63];
1494
1495 in ++;
1496 if (*in == '\0')
1497 break;
1498
1499 *outptr ++ = base64[in[0] & 63];
1500 }
1501
1502 *outptr ++ = '=';
1503 *outptr = '\0';
1504
1505 /*
1506 * Return the encoded string...
1507 */
1508
1509 return (out);
1510 }
1511
1512
1513 /*
1514 * 'httpGetLength()' - Get the amount of data remaining from the
1515 * content-length or transfer-encoding fields.
1516 */
1517
1518 int /* O - Content length */
1519 httpGetLength(http_t *http) /* I - HTTP data */
1520 {
1521 DEBUG_printf(("httpGetLength(%08x)\n", http));
1522
1523 if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
1524 {
1525 DEBUG_puts("httpGetLength: chunked request!");
1526
1527 http->data_encoding = HTTP_ENCODE_CHUNKED;
1528 http->data_remaining = 0;
1529 }
1530 else
1531 {
1532 http->data_encoding = HTTP_ENCODE_LENGTH;
1533
1534 /*
1535 * The following is a hack for HTTP servers that don't send a
1536 * content-length or transfer-encoding field...
1537 *
1538 * If there is no content-length then the connection must close
1539 * after the transfer is complete...
1540 */
1541
1542 if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0')
1543 http->data_remaining = 2147483647;
1544 else
1545 http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]);
1546
1547 DEBUG_printf(("httpGetLength: content_length = %d\n", http->data_remaining));
1548 }
1549
1550 return (http->data_remaining);
1551 }
1552
1553
1554 /*
1555 * 'http_field()' - Return the field index for a field name.
1556 */
1557
1558 static http_field_t /* O - Field index */
1559 http_field(const char *name) /* I - String name */
1560 {
1561 int i; /* Looping var */
1562
1563
1564 for (i = 0; i < HTTP_FIELD_MAX; i ++)
1565 if (strcasecmp(name, http_fields[i]) == 0)
1566 return ((http_field_t)i);
1567
1568 return (HTTP_FIELD_UNKNOWN);
1569 }
1570
1571
1572 /*
1573 * 'http_send()' - Send a request with all fields and the trailing blank line.
1574 */
1575
1576 static int /* O - 0 on success, non-zero on error */
1577 http_send(http_t *http, /* I - HTTP data */
1578 http_state_t request, /* I - Request code */
1579 const char *uri) /* I - URI */
1580 {
1581 int i; /* Looping var */
1582 char *ptr, /* Pointer in buffer */
1583 buf[1024]; /* Encoded URI buffer */
1584 static const char *codes[] = /* Request code strings */
1585 {
1586 NULL,
1587 "OPTIONS",
1588 "GET",
1589 NULL,
1590 "HEAD",
1591 "POST",
1592 NULL,
1593 NULL,
1594 "PUT",
1595 NULL,
1596 "DELETE",
1597 "TRACE",
1598 "CLOSE"
1599 };
1600 static const char *hex = "0123456789ABCDEF";
1601 /* Hex digits */
1602
1603
1604 if (http == NULL || uri == NULL)
1605 return (-1);
1606
1607 /*
1608 * Encode the URI as needed...
1609 */
1610
1611 for (ptr = buf; *uri != '\0'; uri ++)
1612 if (*uri <= ' ' || *uri >= 127)
1613 {
1614 *ptr ++ = '%';
1615 *ptr ++ = hex[(*uri >> 4) & 15];
1616 *ptr ++ = hex[*uri & 15];
1617 }
1618 else
1619 *ptr ++ = *uri;
1620
1621 *ptr = '\0';
1622
1623 /*
1624 * See if we had an error the last time around; if so, reconnect...
1625 */
1626
1627 if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST)
1628 httpReconnect(http);
1629
1630 /*
1631 * Send the request header...
1632 */
1633
1634 http->state = request;
1635 if (request == HTTP_POST || request == HTTP_PUT)
1636 http->state ++;
1637
1638 http->status = HTTP_CONTINUE;
1639
1640 if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
1641 {
1642 http->status = HTTP_ERROR;
1643 return (-1);
1644 }
1645
1646 for (i = 0; i < HTTP_FIELD_MAX; i ++)
1647 if (http->fields[i][0] != '\0')
1648 {
1649 DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i]));
1650
1651 if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1)
1652 {
1653 http->status = HTTP_ERROR;
1654 return (-1);
1655 }
1656 }
1657
1658 if (httpPrintf(http, "\r\n") < 1)
1659 {
1660 http->status = HTTP_ERROR;
1661 return (-1);
1662 }
1663
1664 httpClearFields(http);
1665
1666 return (0);
1667 }
1668
1669
1670 /*
1671 * End of "$Id: http.c,v 1.68 2000/11/03 14:13:26 mike Exp $".
1672 */