]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / cups / http.c
1 /*
2 * "$Id: http.c,v 1.82.2.54 2004/07/02 04:08:59 mike Exp $"
3 *
4 * HTTP routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2004 by Easy Software Products, all rights reserved.
7 *
8 * These coded 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-3142 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * httpInitialize() - Initialize the HTTP interface library and set the
29 * default HTTP proxy (if any).
30 * httpCheck() - Check to see if there is a pending response from
31 * the server.
32 * httpClearCookie() - Clear the cookie value(s).
33 * httpClose() - Close an HTTP connection...
34 * httpConnect() - Connect to a HTTP server.
35 * httpConnectEncrypt() - Connect to a HTTP server using encryption.
36 * httpEncryption() - Set the required encryption on the link.
37 * httpReconnect() - Reconnect to a HTTP server...
38 * httpGetSubField() - Get a sub-field value.
39 * httpSetField() - Set the value of an HTTP header.
40 * httpDelete() - Send a DELETE request to the server.
41 * httpGet() - Send a GET request to the server.
42 * httpHead() - Send a HEAD request to the server.
43 * httpOptions() - Send an OPTIONS request to the server.
44 * httpPost() - Send a POST request to the server.
45 * httpPut() - Send a PUT request to the server.
46 * httpTrace() - Send an TRACE request to the server.
47 * httpFlush() - Flush data from a HTTP connection.
48 * httpRead() - Read data from a HTTP connection.
49 * httpSetCookie() - Set the cookie value(s)...
50 * httpWait() - Wait for data available on a connection.
51 * httpWrite() - Write data to a HTTP connection.
52 * httpGets() - Get a line of text from a HTTP connection.
53 * httpPrintf() - Print a formatted string to a HTTP connection.
54 * httpGetDateString() - Get a formatted date/time string from a time value.
55 * httpGetDateTime() - Get a time value from a formatted date/time string.
56 * httpUpdate() - Update the current HTTP state for incoming data.
57 * httpDecode64() - Base64-decode a string.
58 * httpDecode64_2() - Base64-decode a string.
59 * httpEncode64() - Base64-encode a string.
60 * httpEncode64_2() - Base64-encode a string.
61 * httpGetLength() - Get the amount of data remaining from the
62 * content-length or transfer-encoding fields.
63 * http_field() - Return the field index for a field name.
64 * http_send() - Send a request with all fields and the trailing
65 * blank line.
66 * http_wait() - Wait for data available on a connection.
67 * http_upgrade() - Force upgrade to TLS encryption.
68 * http_setup_ssl() - Set up SSL/TLS on a connection.
69 * http_shutdown_ssl() - Shut down SSL/TLS on a connection.
70 * http_read_ssl() - Read from a SSL/TLS connection.
71 * http_write_ssl() - Write to a SSL/TLS connection.
72 * CDSAReadFunc() - Read function for CDSA decryption code.
73 * CDSAWriteFunc() - Write function for CDSA encryption code.
74 */
75
76 /*
77 * Include necessary headers...
78 */
79
80 #include "http-private.h"
81
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <stdarg.h>
85 #include <ctype.h>
86 #include "string.h"
87 #include <fcntl.h>
88 #include <errno.h>
89
90 #include "http.h"
91 #include "debug.h"
92
93 #ifndef WIN32
94 # include <signal.h>
95 # include <sys/time.h>
96 # include <sys/resource.h>
97 #endif /* !WIN32 */
98
99
100 /*
101 * Some operating systems have done away with the Fxxxx constants for
102 * the fcntl() call; this works around that "feature"...
103 */
104
105 #ifndef FNONBLK
106 # define FNONBLK O_NONBLOCK
107 #endif /* !FNONBLK */
108
109
110 /*
111 * Local functions...
112 */
113
114 static http_field_t http_field(const char *name);
115 static int http_send(http_t *http, http_state_t request,
116 const char *uri);
117 static int http_wait(http_t *http, int msec);
118 #ifdef HAVE_SSL
119 static int http_upgrade(http_t *http);
120 static int http_setup_ssl(http_t *http);
121 static void http_shutdown_ssl(http_t *http);
122 static int http_read_ssl(http_t *http, char *buf, int len);
123 static int http_write_ssl(http_t *http, const char *buf, int len);
124 # ifdef HAVE_CDSASSL
125 static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, size_t *dataLength);
126 static OSStatus CDSAWriteFunc(SSLConnectionRef connection, const void *data, size_t *dataLength);
127 # endif /* HAVE_CDSASSL */
128 #endif /* HAVE_SSL */
129
130
131 /*
132 * Local globals...
133 */
134
135 static const char * const http_fields[] =
136 {
137 "Accept-Language",
138 "Accept-Ranges",
139 "Authorization",
140 "Connection",
141 "Content-Encoding",
142 "Content-Language",
143 "Content-Length",
144 "Content-Location",
145 "Content-MD5",
146 "Content-Range",
147 "Content-Type",
148 "Content-Version",
149 "Date",
150 "Host",
151 "If-Modified-Since",
152 "If-Unmodified-since",
153 "Keep-Alive",
154 "Last-Modified",
155 "Link",
156 "Location",
157 "Range",
158 "Referer",
159 "Retry-After",
160 "Transfer-Encoding",
161 "Upgrade",
162 "User-Agent",
163 "WWW-Authenticate"
164 };
165 static const char * const days[7] =
166 {
167 "Sun",
168 "Mon",
169 "Tue",
170 "Wed",
171 "Thu",
172 "Fri",
173 "Sat"
174 };
175 static const char * const months[12] =
176 {
177 "Jan",
178 "Feb",
179 "Mar",
180 "Apr",
181 "May",
182 "Jun",
183 "Jul",
184 "Aug",
185 "Sep",
186 "Oct",
187 "Nov",
188 "Dec"
189 };
190
191
192 /*
193 * 'httpInitialize()' - Initialize the HTTP interface library and set the
194 * default HTTP proxy (if any).
195 */
196
197 void
198 httpInitialize(void)
199 {
200 #ifdef HAVE_LIBSSL
201 # ifndef WIN32
202 struct timeval curtime; /* Current time in microseconds */
203 # endif /* !WIN32 */
204 int i; /* Looping var */
205 unsigned char data[1024]; /* Seed data */
206 #endif /* HAVE_LIBSSL */
207
208 #ifdef WIN32
209 WSADATA winsockdata; /* WinSock data */
210 static int initialized = 0; /* Has WinSock been initialized? */
211
212
213 if (!initialized)
214 WSAStartup(MAKEWORD(1,1), &winsockdata);
215 #elif defined(HAVE_SIGSET)
216 sigset(SIGPIPE, SIG_IGN);
217 #elif defined(HAVE_SIGACTION)
218 struct sigaction action; /* POSIX sigaction data */
219
220
221 /*
222 * Ignore SIGPIPE signals...
223 */
224
225 memset(&action, 0, sizeof(action));
226 action.sa_handler = SIG_IGN;
227 sigaction(SIGPIPE, &action, NULL);
228 #else
229 signal(SIGPIPE, SIG_IGN);
230 #endif /* WIN32 */
231
232 #ifdef HAVE_GNUTLS
233 gnutls_global_init();
234 #endif /* HAVE_GNUTLS */
235
236 #ifdef HAVE_LIBSSL
237 SSL_load_error_strings();
238 SSL_library_init();
239
240 /*
241 * Using the current time is a dubious random seed, but on some systems
242 * it is the best we can do (on others, this seed isn't even used...)
243 */
244
245 #ifdef WIN32
246 #else
247 gettimeofday(&curtime, NULL);
248 srand(curtime.tv_sec + curtime.tv_usec);
249 #endif /* WIN32 */
250
251 for (i = 0; i < sizeof(data); i ++)
252 data[i] = rand(); /* Yes, this is a poor source of random data... */
253
254 RAND_seed(&data, sizeof(data));
255 #endif /* HAVE_LIBSSL */
256 }
257
258
259 /*
260 * 'httpCheck()' - Check to see if there is a pending response from the server.
261 */
262
263 int /* O - 0 = no data, 1 = data available */
264 httpCheck(http_t *http) /* I - HTTP connection */
265 {
266 return (httpWait(http, 0));
267 }
268
269
270 /*
271 * 'httpClearCookie()' - Clear the cookie value(s).
272 */
273
274 void
275 httpClearCookie(http_t *http) /* I - Connection */
276 {
277 if (!http)
278 return;
279
280 if (http->cookie)
281 {
282 free(http->cookie);
283 http->cookie = NULL;
284 }
285 }
286
287
288 /*
289 * 'httpClose()' - Close an HTTP connection...
290 */
291
292 void
293 httpClose(http_t *http) /* I - Connection to close */
294 {
295 DEBUG_printf(("httpClose(http=%p)\n", http));
296
297 if (!http)
298 return;
299
300 if (http->input_set)
301 free(http->input_set);
302
303 if (http->cookie)
304 free(http->cookie);
305
306 #ifdef HAVE_SSL
307 if (http->tls)
308 http_shutdown_ssl(http);
309 #endif /* HAVE_SSL */
310
311 #ifdef WIN32
312 closesocket(http->fd);
313 #else
314 close(http->fd);
315 #endif /* WIN32 */
316
317 free(http);
318 }
319
320
321 /*
322 * 'httpConnect()' - Connect to a HTTP server.
323 */
324
325 http_t * /* O - New HTTP connection */
326 httpConnect(const char *host, /* I - Host to connect to */
327 int port) /* I - Port number */
328 {
329 http_encryption_t encrypt; /* Type of encryption to use */
330
331
332 /*
333 * Set the default encryption status...
334 */
335
336 if (port == 443)
337 encrypt = HTTP_ENCRYPT_ALWAYS;
338 else
339 encrypt = HTTP_ENCRYPT_IF_REQUESTED;
340
341 return (httpConnectEncrypt(host, port, encrypt));
342 }
343
344
345 /*
346 * 'httpConnectEncrypt()' - Connect to a HTTP server using encryption.
347 */
348
349 http_t * /* O - New HTTP connection */
350 httpConnectEncrypt(const char *host, /* I - Host to connect to */
351 int port, /* I - Port number */
352 http_encryption_t encrypt)
353 /* I - Type of encryption to use */
354 {
355 int i; /* Looping var */
356 http_t *http; /* New HTTP connection */
357 struct hostent *hostaddr; /* Host address data */
358
359
360 DEBUG_printf(("httpConnectEncrypt(host=\"%s\", port=%d, encrypt=%d)\n",
361 host ? host : "(null)", port, encrypt));
362
363 if (!host)
364 return (NULL);
365
366 httpInitialize();
367
368 /*
369 * Lookup the host...
370 */
371
372 if ((hostaddr = httpGetHostByName(host)) == NULL)
373 {
374 /*
375 * This hack to make users that don't have a localhost entry in
376 * their hosts file or DNS happy...
377 */
378
379 if (strcasecmp(host, "localhost") != 0)
380 return (NULL);
381 else if ((hostaddr = httpGetHostByName("127.0.0.1")) == NULL)
382 return (NULL);
383 }
384
385 /*
386 * Verify that it is an IPv4 address (IPv6 support will come in CUPS 1.2...)
387 */
388
389 #ifdef AF_INET6
390 if ((hostaddr->h_addrtype != AF_INET || hostaddr->h_length != 4) &&
391 (hostaddr->h_addrtype != AF_INET6 || hostaddr->h_length != 16))
392 return (NULL);
393 #else
394 if (hostaddr->h_addrtype != AF_INET || hostaddr->h_length != 4)
395 return (NULL);
396 #endif /* AF_INET6 */
397
398 /*
399 * Allocate memory for the structure...
400 */
401
402 http = calloc(sizeof(http_t), 1);
403 if (http == NULL)
404 return (NULL);
405
406 http->version = HTTP_1_1;
407 http->blocking = 1;
408 http->activity = time(NULL);
409 http->fd = -1;
410
411 /*
412 * Set the encryption status...
413 */
414
415 if (port == 443) /* Always use encryption for https */
416 http->encryption = HTTP_ENCRYPT_ALWAYS;
417 else
418 http->encryption = encrypt;
419
420 /*
421 * Loop through the addresses we have until one of them connects...
422 */
423
424 strlcpy(http->hostname, host, sizeof(http->hostname));
425
426 for (i = 0; hostaddr->h_addr_list[i]; i ++)
427 {
428 /*
429 * Load the address...
430 */
431
432 httpAddrLoad(hostaddr, port, i, &(http->hostaddr));
433
434 /*
435 * Connect to the remote system...
436 */
437
438 if (!httpReconnect(http))
439 return (http);
440 }
441
442 /*
443 * Could not connect to any known address - bail out!
444 */
445
446 free(http);
447 return (NULL);
448 }
449
450
451 /*
452 * 'httpEncryption()' - Set the required encryption on the link.
453 */
454
455 int /* O - -1 on error, 0 on success */
456 httpEncryption(http_t *http, /* I - HTTP data */
457 http_encryption_t e) /* I - New encryption preference */
458 {
459 DEBUG_printf(("httpEncryption(http=%p, e=%d)\n", http, e));
460
461 #ifdef HAVE_SSL
462 if (!http)
463 return (0);
464
465 http->encryption = e;
466
467 if ((http->encryption == HTTP_ENCRYPT_ALWAYS && !http->tls) ||
468 (http->encryption == HTTP_ENCRYPT_NEVER && http->tls))
469 return (httpReconnect(http));
470 else if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls)
471 return (http_upgrade(http));
472 else
473 return (0);
474 #else
475 if (e == HTTP_ENCRYPT_ALWAYS || e == HTTP_ENCRYPT_REQUIRED)
476 return (-1);
477 else
478 return (0);
479 #endif /* HAVE_SSL */
480 }
481
482
483 /*
484 * 'httpReconnect()' - Reconnect to a HTTP server...
485 */
486
487 int /* O - 0 on success, non-zero on failure */
488 httpReconnect(http_t *http) /* I - HTTP data */
489 {
490 int val; /* Socket option value */
491 int status; /* Connect status */
492
493
494 DEBUG_printf(("httpReconnect(http=%p)\n", http));
495
496 if (!http)
497 return (-1);
498
499 #ifdef HAVE_SSL
500 if (http->tls)
501 http_shutdown_ssl(http);
502 #endif /* HAVE_SSL */
503
504 /*
505 * Close any previously open socket...
506 */
507
508 if (http->fd >= 0)
509 #ifdef WIN32
510 closesocket(http->fd);
511 #else
512 close(http->fd);
513 #endif /* WIN32 */
514
515 /*
516 * Create the socket and set options to allow reuse.
517 */
518
519 if ((http->fd = socket(http->hostaddr.addr.sa_family, SOCK_STREAM, 0)) < 0)
520 {
521 #ifdef WIN32
522 http->error = WSAGetLastError();
523 #else
524 http->error = errno;
525 #endif /* WIN32 */
526 http->status = HTTP_ERROR;
527 return (-1);
528 }
529
530 #ifdef FD_CLOEXEC
531 fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
532 * other processes... */
533 #endif /* FD_CLOEXEC */
534
535 val = 1;
536 setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
537
538 #ifdef SO_REUSEPORT
539 val = 1;
540 setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
541 #endif /* SO_REUSEPORT */
542
543 /*
544 * Using TCP_NODELAY improves responsiveness, especially on systems
545 * with a slow loopback interface... Since we write large buffers
546 * when sending print files and requests, there shouldn't be any
547 * performance penalty for this...
548 */
549
550 val = 1;
551 #ifdef WIN32
552 setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
553 #else
554 setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
555 #endif // WIN32
556
557 /*
558 * Connect to the server...
559 */
560
561 #ifdef AF_INET6
562 if (http->hostaddr.addr.sa_family == AF_INET6)
563 status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
564 sizeof(http->hostaddr.ipv6));
565 else
566 #endif /* AF_INET6 */
567 #ifdef AF_LOCAL
568 if (http->hostaddr.addr.sa_family == AF_LOCAL)
569 status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
570 SUN_LEN(&(http->hostaddr.un)));
571 else
572 #endif /* AF_LOCAL */
573 status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
574 sizeof(http->hostaddr.ipv4));
575
576 if (status < 0)
577 {
578 #ifdef WIN32
579 http->error = WSAGetLastError();
580 #else
581 http->error = errno;
582 #endif /* WIN32 */
583 http->status = HTTP_ERROR;
584
585 #ifdef WIN32
586 closesocket(http->fd);
587 #else
588 close(http->fd);
589 #endif
590
591 http->fd = -1;
592
593 return (-1);
594 }
595
596 http->error = 0;
597 http->status = HTTP_CONTINUE;
598
599 #ifdef HAVE_SSL
600 if (http->encryption == HTTP_ENCRYPT_ALWAYS)
601 {
602 /*
603 * Always do encryption via SSL.
604 */
605
606 if (http_setup_ssl(http) != 0)
607 {
608 #ifdef WIN32
609 closesocket(http->fd);
610 #else
611 close(http->fd);
612 #endif /* WIN32 */
613
614 return (-1);
615 }
616 }
617 else if (http->encryption == HTTP_ENCRYPT_REQUIRED)
618 return (http_upgrade(http));
619 #endif /* HAVE_SSL */
620
621 return (0);
622 }
623
624
625 /*
626 * 'httpGetSubField()' - Get a sub-field value.
627 */
628
629 char * /* O - Value or NULL */
630 httpGetSubField(http_t *http, /* I - HTTP data */
631 http_field_t field, /* I - Field index */
632 const char *name, /* I - Name of sub-field */
633 char *value) /* O - Value string */
634 {
635 const char *fptr; /* Pointer into field */
636 char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
637 *ptr; /* Pointer into string buffer */
638
639
640 DEBUG_printf(("httpGetSubField(http=%p, field=%d, name=\"%s\", value=%p)\n",
641 http, field, name, value));
642
643 if (http == NULL ||
644 field < HTTP_FIELD_ACCEPT_LANGUAGE ||
645 field > HTTP_FIELD_WWW_AUTHENTICATE ||
646 name == NULL || value == NULL)
647 return (NULL);
648
649 for (fptr = http->fields[field]; *fptr;)
650 {
651 /*
652 * Skip leading whitespace...
653 */
654
655 while (isspace(*fptr & 255))
656 fptr ++;
657
658 if (*fptr == ',')
659 {
660 fptr ++;
661 continue;
662 }
663
664 /*
665 * Get the sub-field name...
666 */
667
668 for (ptr = temp;
669 *fptr && *fptr != '=' && !isspace(*fptr & 255) && ptr < (temp + sizeof(temp) - 1);
670 *ptr++ = *fptr++);
671
672 *ptr = '\0';
673
674 DEBUG_printf(("httpGetSubField: name=\"%s\"\n", temp));
675
676 /*
677 * Skip trailing chars up to the '='...
678 */
679
680 while (isspace(*fptr & 255))
681 fptr ++;
682
683 if (!*fptr)
684 break;
685
686 if (*fptr != '=')
687 continue;
688
689 /*
690 * Skip = and leading whitespace...
691 */
692
693 fptr ++;
694
695 while (isspace(*fptr & 255))
696 fptr ++;
697
698 if (*fptr == '\"')
699 {
700 /*
701 * Read quoted string...
702 */
703
704 for (ptr = value, fptr ++;
705 *fptr && *fptr != '\"' && ptr < (value + HTTP_MAX_VALUE - 1);
706 *ptr++ = *fptr++);
707
708 *ptr = '\0';
709
710 while (*fptr && *fptr != '\"')
711 fptr ++;
712
713 if (*fptr)
714 fptr ++;
715 }
716 else
717 {
718 /*
719 * Read unquoted string...
720 */
721
722 for (ptr = value;
723 *fptr && !isspace(*fptr & 255) && *fptr != ',' && ptr < (value + HTTP_MAX_VALUE - 1);
724 *ptr++ = *fptr++);
725
726 *ptr = '\0';
727
728 while (*fptr && !isspace(*fptr & 255) && *fptr != ',')
729 fptr ++;
730 }
731
732 DEBUG_printf(("httpGetSubField: value=\"%s\"\n", value));
733
734 /*
735 * See if this is the one...
736 */
737
738 if (strcmp(name, temp) == 0)
739 return (value);
740 }
741
742 value[0] = '\0';
743
744 return (NULL);
745 }
746
747
748 /*
749 * 'httpSetField()' - Set the value of an HTTP header.
750 */
751
752 void
753 httpSetField(http_t *http, /* I - HTTP data */
754 http_field_t field, /* I - Field index */
755 const char *value) /* I - Value */
756 {
757 if (http == NULL ||
758 field < HTTP_FIELD_ACCEPT_LANGUAGE ||
759 field > HTTP_FIELD_WWW_AUTHENTICATE ||
760 value == NULL)
761 return;
762
763 strlcpy(http->fields[field], value, HTTP_MAX_VALUE);
764 }
765
766
767 /*
768 * 'httpDelete()' - Send a DELETE request to the server.
769 */
770
771 int /* O - Status of call (0 = success) */
772 httpDelete(http_t *http, /* I - HTTP data */
773 const char *uri) /* I - URI to delete */
774 {
775 return (http_send(http, HTTP_DELETE, uri));
776 }
777
778
779 /*
780 * 'httpGet()' - Send a GET request to the server.
781 */
782
783 int /* O - Status of call (0 = success) */
784 httpGet(http_t *http, /* I - HTTP data */
785 const char *uri) /* I - URI to get */
786 {
787 return (http_send(http, HTTP_GET, uri));
788 }
789
790
791 /*
792 * 'httpHead()' - Send a HEAD request to the server.
793 */
794
795 int /* O - Status of call (0 = success) */
796 httpHead(http_t *http, /* I - HTTP data */
797 const char *uri) /* I - URI for head */
798 {
799 return (http_send(http, HTTP_HEAD, uri));
800 }
801
802
803 /*
804 * 'httpOptions()' - Send an OPTIONS request to the server.
805 */
806
807 int /* O - Status of call (0 = success) */
808 httpOptions(http_t *http, /* I - HTTP data */
809 const char *uri) /* I - URI for options */
810 {
811 return (http_send(http, HTTP_OPTIONS, uri));
812 }
813
814
815 /*
816 * 'httpPost()' - Send a POST request to the server.
817 */
818
819 int /* O - Status of call (0 = success) */
820 httpPost(http_t *http, /* I - HTTP data */
821 const char *uri) /* I - URI for post */
822 {
823 httpGetLength(http);
824
825 return (http_send(http, HTTP_POST, uri));
826 }
827
828
829 /*
830 * 'httpPut()' - Send a PUT request to the server.
831 */
832
833 int /* O - Status of call (0 = success) */
834 httpPut(http_t *http, /* I - HTTP data */
835 const char *uri) /* I - URI to put */
836 {
837 httpGetLength(http);
838
839 return (http_send(http, HTTP_PUT, uri));
840 }
841
842
843 /*
844 * 'httpTrace()' - Send an TRACE request to the server.
845 */
846
847 int /* O - Status of call (0 = success) */
848 httpTrace(http_t *http, /* I - HTTP data */
849 const char *uri) /* I - URI for trace */
850 {
851 return (http_send(http, HTTP_TRACE, uri));
852 }
853
854
855 /*
856 * 'httpFlush()' - Flush data from a HTTP connection.
857 */
858
859 void
860 httpFlush(http_t *http) /* I - HTTP data */
861 {
862 char buffer[8192]; /* Junk buffer */
863
864
865 DEBUG_printf(("httpFlush(http=%p), state=%d\n", http, http->state));
866
867 while (httpRead(http, buffer, sizeof(buffer)) > 0);
868 }
869
870
871 /*
872 * 'httpRead()' - Read data from a HTTP connection.
873 */
874
875 int /* O - Number of bytes read */
876 httpRead(http_t *http, /* I - HTTP data */
877 char *buffer, /* I - Buffer for data */
878 int length) /* I - Maximum number of bytes */
879 {
880 int bytes; /* Bytes read */
881 char len[32]; /* Length string */
882
883
884 DEBUG_printf(("httpRead(http=%p, buffer=%p, length=%d)\n",
885 http, buffer, length));
886
887 if (http == NULL || buffer == NULL)
888 return (-1);
889
890 http->activity = time(NULL);
891
892 if (length <= 0)
893 return (0);
894
895 if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
896 http->data_remaining <= 0)
897 {
898 DEBUG_puts("httpRead: Getting chunk length...");
899
900 if (httpGets(len, sizeof(len), http) == NULL)
901 {
902 DEBUG_puts("httpRead: Could not get length!");
903 return (0);
904 }
905
906 http->data_remaining = strtol(len, NULL, 16);
907 if (http->data_remaining < 0)
908 {
909 DEBUG_puts("httpRead: Negative chunk length!");
910 return (0);
911 }
912 }
913
914 DEBUG_printf(("httpRead: data_remaining=%d\n", http->data_remaining));
915
916 if (http->data_remaining <= 0)
917 {
918 /*
919 * A zero-length chunk ends a transfer; unless we are reading POST
920 * data, go idle...
921 */
922
923 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
924 httpGets(len, sizeof(len), http);
925
926 if (http->state == HTTP_POST_RECV)
927 http->state ++;
928 else
929 http->state = HTTP_WAITING;
930
931 /*
932 * Prevent future reads for this request...
933 */
934
935 http->data_encoding = HTTP_ENCODE_LENGTH;
936
937 return (0);
938 }
939 else if (length > http->data_remaining)
940 length = http->data_remaining;
941
942 if (http->used == 0 && length <= 256)
943 {
944 /*
945 * Buffer small reads for better performance...
946 */
947
948 if (!http->blocking && !httpWait(http, 1000))
949 return (0);
950
951 if (http->data_remaining > sizeof(http->buffer))
952 bytes = sizeof(http->buffer);
953 else
954 bytes = http->data_remaining;
955
956 #ifdef HAVE_SSL
957 if (http->tls)
958 bytes = http_read_ssl(http, http->buffer, bytes);
959 else
960 #endif /* HAVE_SSL */
961 {
962 DEBUG_printf(("httpRead: reading %d bytes from socket into buffer...\n",
963 bytes));
964
965 bytes = recv(http->fd, http->buffer, bytes, 0);
966
967 DEBUG_printf(("httpRead: read %d bytes from socket into buffer...\n",
968 bytes));
969 }
970
971 if (bytes > 0)
972 http->used = bytes;
973 else if (bytes < 0)
974 {
975 #ifdef WIN32
976 http->error = WSAGetLastError();
977 return (-1);
978 #else
979 if (errno != EINTR)
980 {
981 http->error = errno;
982 return (-1);
983 }
984 #endif /* WIN32 */
985 }
986 else
987 {
988 http->error = EPIPE;
989 return (0);
990 }
991 }
992
993 if (http->used > 0)
994 {
995 if (length > http->used)
996 length = http->used;
997
998 bytes = length;
999
1000 DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes));
1001
1002 memcpy(buffer, http->buffer, length);
1003 http->used -= length;
1004
1005 if (http->used > 0)
1006 memmove(http->buffer, http->buffer + length, http->used);
1007 }
1008 #ifdef HAVE_SSL
1009 else if (http->tls)
1010 {
1011 if (!http->blocking && !httpWait(http, 1000))
1012 return (0);
1013
1014 bytes = http_read_ssl(http, buffer, length);
1015 }
1016 #endif /* HAVE_SSL */
1017 else
1018 {
1019 if (!http->blocking && !httpWait(http, 1000))
1020 return (0);
1021
1022 DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
1023 bytes = recv(http->fd, buffer, length, 0);
1024 DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
1025 }
1026
1027 if (bytes > 0)
1028 http->data_remaining -= bytes;
1029 else if (bytes < 0)
1030 {
1031 #ifdef WIN32
1032 http->error = WSAGetLastError();
1033 #else
1034 if (errno == EINTR)
1035 bytes = 0;
1036 else
1037 http->error = errno;
1038 #endif /* WIN32 */
1039 }
1040 else
1041 {
1042 http->error = EPIPE;
1043 return (0);
1044 }
1045
1046 if (http->data_remaining == 0)
1047 {
1048 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
1049 httpGets(len, sizeof(len), http);
1050
1051 if (http->data_encoding != HTTP_ENCODE_CHUNKED)
1052 {
1053 if (http->state == HTTP_POST_RECV)
1054 http->state ++;
1055 else
1056 http->state = HTTP_WAITING;
1057 }
1058 }
1059
1060 #ifdef DEBUG
1061 {
1062 int i, j, ch;
1063 printf("httpRead: Read %d bytes:\n", bytes);
1064 for (i = 0; i < bytes; i += 16)
1065 {
1066 printf(" ");
1067
1068 for (j = 0; j < 16 && (i + j) < bytes; j ++)
1069 printf(" %02X", buffer[i + j] & 255);
1070
1071 while (j < 16)
1072 {
1073 printf(" ");
1074 j ++;
1075 }
1076
1077 printf(" ");
1078 for (j = 0; j < 16 && (i + j) < bytes; j ++)
1079 {
1080 ch = buffer[i + j] & 255;
1081
1082 if (ch < ' ' || ch == 127)
1083 ch = '.';
1084
1085 putchar(ch);
1086 }
1087 putchar('\n');
1088 }
1089 }
1090 #endif /* DEBUG */
1091
1092 return (bytes);
1093 }
1094
1095
1096 /*
1097 * 'httpSetCookie()' - Set the cookie value(s)...
1098 */
1099
1100 void
1101 httpSetCookie(http_t *http, /* I - Connection */
1102 const char *cookie) /* I - Cookie string */
1103 {
1104 if (!http)
1105 return;
1106
1107 if (http->cookie)
1108 free(http->cookie);
1109
1110 if (cookie)
1111 http->cookie = strdup(cookie);
1112 else
1113 http->cookie = NULL;
1114 }
1115
1116
1117 /*
1118 * 'httpWait()' - Wait for data available on a connection.
1119 */
1120
1121 int /* O - 1 if data is available, 0 otherwise */
1122 httpWait(http_t *http, /* I - HTTP data */
1123 int msec) /* I - Milliseconds to wait */
1124 {
1125 /*
1126 * First see if there is data in the buffer...
1127 */
1128
1129 if (http == NULL)
1130 return (0);
1131
1132 if (http->used)
1133 return (1);
1134
1135 /*
1136 * If not, check the SSL/TLS buffers and do a select() on the connection...
1137 */
1138
1139 return (http_wait(http, msec));
1140 }
1141
1142
1143 /*
1144 * 'httpWrite()' - Write data to a HTTP connection.
1145 */
1146
1147 int /* O - Number of bytes written */
1148 httpWrite(http_t *http, /* I - HTTP data */
1149 const char *buffer, /* I - Buffer for data */
1150 int length) /* I - Number of bytes to write */
1151 {
1152 int tbytes, /* Total bytes sent */
1153 bytes; /* Bytes sent */
1154
1155
1156 if (http == NULL || buffer == NULL)
1157 return (-1);
1158
1159 http->activity = time(NULL);
1160
1161 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
1162 {
1163 if (httpPrintf(http, "%x\r\n", length) < 0)
1164 return (-1);
1165
1166 if (length == 0)
1167 {
1168 /*
1169 * A zero-length chunk ends a transfer; unless we are sending POST
1170 * or PUT data, go idle...
1171 */
1172
1173 DEBUG_puts("httpWrite: changing states...");
1174
1175 if (http->state == HTTP_POST_RECV)
1176 http->state ++;
1177 else if (http->state == HTTP_PUT_RECV)
1178 http->state = HTTP_STATUS;
1179 else
1180 http->state = HTTP_WAITING;
1181
1182 if (httpPrintf(http, "\r\n") < 0)
1183 return (-1);
1184
1185 return (0);
1186 }
1187 }
1188
1189 tbytes = 0;
1190
1191 while (length > 0)
1192 {
1193 #ifdef HAVE_SSL
1194 if (http->tls)
1195 bytes = http_write_ssl(http, buffer, length);
1196 else
1197 #endif /* HAVE_SSL */
1198 bytes = send(http->fd, buffer, length, 0);
1199
1200 if (bytes < 0)
1201 {
1202 #ifdef WIN32
1203 if (WSAGetLastError() != http->error)
1204 {
1205 http->error = WSAGetLastError();
1206 continue;
1207 }
1208 #else
1209 if (errno == EINTR)
1210 continue;
1211 else if (errno != http->error && errno != ECONNRESET)
1212 {
1213 http->error = errno;
1214 continue;
1215 }
1216 #endif /* WIN32 */
1217
1218 DEBUG_puts("httpWrite: error writing data...\n");
1219
1220 return (-1);
1221 }
1222
1223 buffer += bytes;
1224 tbytes += bytes;
1225 length -= bytes;
1226 if (http->data_encoding == HTTP_ENCODE_LENGTH)
1227 http->data_remaining -= bytes;
1228 }
1229
1230 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
1231 if (httpPrintf(http, "\r\n") < 0)
1232 return (-1);
1233
1234 if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
1235 {
1236 /*
1237 * Finished with the transfer; unless we are sending POST or PUT
1238 * data, go idle...
1239 */
1240
1241 DEBUG_puts("httpWrite: changing states...");
1242
1243 if (http->state == HTTP_POST_RECV)
1244 http->state ++;
1245 else if (http->state == HTTP_PUT_RECV)
1246 http->state = HTTP_STATUS;
1247 else
1248 http->state = HTTP_WAITING;
1249 }
1250
1251 #ifdef DEBUG
1252 {
1253 int i, j, ch;
1254 printf("httpWrite: wrote %d bytes: \n", tbytes);
1255 for (i = 0, buffer -= tbytes; i < tbytes; i += 16)
1256 {
1257 printf(" ");
1258
1259 for (j = 0; j < 16 && (i + j) < tbytes; j ++)
1260 printf(" %02X", buffer[i + j] & 255);
1261
1262 while (j < 16)
1263 {
1264 printf(" ");
1265 j ++;
1266 }
1267
1268 printf(" ");
1269 for (j = 0; j < 16 && (i + j) < tbytes; j ++)
1270 {
1271 ch = buffer[i + j] & 255;
1272
1273 if (ch < ' ' || ch == 127)
1274 ch = '.';
1275
1276 putchar(ch);
1277 }
1278 putchar('\n');
1279 }
1280 }
1281 #endif /* DEBUG */
1282 return (tbytes);
1283 }
1284
1285
1286 /*
1287 * 'httpGets()' - Get a line of text from a HTTP connection.
1288 */
1289
1290 char * /* O - Line or NULL */
1291 httpGets(char *line, /* I - Line to read into */
1292 int length, /* I - Max length of buffer */
1293 http_t *http) /* I - HTTP data */
1294 {
1295 char *lineptr, /* Pointer into line */
1296 *bufptr, /* Pointer into input buffer */
1297 *bufend; /* Pointer to end of buffer */
1298 int bytes; /* Number of bytes read */
1299
1300
1301 DEBUG_printf(("httpGets(line=%p, length=%d, http=%p)\n", line, length, http));
1302
1303 if (http == NULL || line == NULL)
1304 return (NULL);
1305
1306 /*
1307 * Pre-scan the buffer and see if there is a newline in there...
1308 */
1309
1310 #ifdef WIN32
1311 WSASetLastError(0);
1312 #else
1313 errno = 0;
1314 #endif /* WIN32 */
1315
1316 do
1317 {
1318 bufptr = http->buffer;
1319 bufend = http->buffer + http->used;
1320
1321 while (bufptr < bufend)
1322 if (*bufptr == 0x0a)
1323 break;
1324 else
1325 bufptr ++;
1326
1327 if (bufptr >= bufend && http->used < HTTP_MAX_BUFFER)
1328 {
1329 /*
1330 * No newline; see if there is more data to be read...
1331 */
1332
1333 if (!http->blocking && !http_wait(http, 1000))
1334 return (NULL);
1335
1336 #ifdef HAVE_SSL
1337 if (http->tls)
1338 bytes = http_read_ssl(http, bufend, HTTP_MAX_BUFFER - http->used);
1339 else
1340 #endif /* HAVE_SSL */
1341 bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
1342
1343 DEBUG_printf(("httpGets: read %d bytes...\n", bytes));
1344
1345 if (bytes < 0)
1346 {
1347 /*
1348 * Nope, can't get a line this time...
1349 */
1350
1351 #ifdef WIN32
1352 if (WSAGetLastError() != http->error)
1353 {
1354 http->error = WSAGetLastError();
1355 continue;
1356 }
1357
1358 DEBUG_printf(("httpGets: recv() error %d!\n", WSAGetLastError()));
1359 #else
1360 DEBUG_printf(("httpGets: recv() error %d!\n", errno));
1361
1362 if (errno == EINTR)
1363 continue;
1364 else if (errno != http->error)
1365 {
1366 http->error = errno;
1367 continue;
1368 }
1369 #endif /* WIN32 */
1370
1371 return (NULL);
1372 }
1373 else if (bytes == 0)
1374 {
1375 http->error = EPIPE;
1376
1377 return (NULL);
1378 }
1379
1380 /*
1381 * Yup, update the amount used and the end pointer...
1382 */
1383
1384 http->used += bytes;
1385 bufend += bytes;
1386 bufptr = bufend;
1387 }
1388 }
1389 while (bufptr >= bufend && http->used < HTTP_MAX_BUFFER);
1390
1391 http->activity = time(NULL);
1392
1393 /*
1394 * Read a line from the buffer...
1395 */
1396
1397 lineptr = line;
1398 bufptr = http->buffer;
1399 bytes = 0;
1400 length --;
1401
1402 while (bufptr < bufend && bytes < length)
1403 {
1404 bytes ++;
1405
1406 if (*bufptr == 0x0a)
1407 {
1408 bufptr ++;
1409 break;
1410 }
1411 else if (*bufptr == 0x0d)
1412 bufptr ++;
1413 else
1414 *lineptr++ = *bufptr++;
1415 }
1416
1417 if (bytes > 0)
1418 {
1419 *lineptr = '\0';
1420
1421 http->used -= bytes;
1422 if (http->used > 0)
1423 memmove(http->buffer, bufptr, http->used);
1424
1425 DEBUG_printf(("httpGets: Returning \"%s\"\n", line));
1426 return (line);
1427 }
1428
1429 DEBUG_puts("httpGets: No new line available!");
1430
1431 return (NULL);
1432 }
1433
1434
1435 /*
1436 * 'httpPrintf()' - Print a formatted string to a HTTP connection.
1437 */
1438
1439 int /* O - Number of bytes written */
1440 httpPrintf(http_t *http, /* I - HTTP data */
1441 const char *format, /* I - printf-style format string */
1442 ...) /* I - Additional args as needed */
1443 {
1444 int bytes, /* Number of bytes to write */
1445 nbytes, /* Number of bytes written */
1446 tbytes; /* Number of bytes all together */
1447 char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */
1448 *bufptr; /* Pointer into buffer */
1449 va_list ap; /* Variable argument pointer */
1450
1451
1452 DEBUG_printf(("httpPrintf(http=%p, format=\"%s\", ...)\n", http, format));
1453
1454 va_start(ap, format);
1455 bytes = vsnprintf(buf, sizeof(buf), format, ap);
1456 va_end(ap);
1457
1458 DEBUG_printf(("httpPrintf: %s", buf));
1459
1460 for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes)
1461 {
1462 #ifdef HAVE_SSL
1463 if (http->tls)
1464 nbytes = http_write_ssl(http, bufptr, bytes - tbytes);
1465 else
1466 #endif /* HAVE_SSL */
1467 nbytes = send(http->fd, bufptr, bytes - tbytes, 0);
1468
1469 if (nbytes < 0)
1470 {
1471 nbytes = 0;
1472
1473 #ifdef WIN32
1474 if (WSAGetLastError() != http->error)
1475 {
1476 http->error = WSAGetLastError();
1477 continue;
1478 }
1479 #else
1480 if (errno == EINTR)
1481 continue;
1482 else if (errno != http->error)
1483 {
1484 http->error = errno;
1485 continue;
1486 }
1487 #endif /* WIN32 */
1488
1489 return (-1);
1490 }
1491 }
1492
1493 return (bytes);
1494 }
1495
1496
1497 /*
1498 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
1499 */
1500
1501 const char * /* O - Date/time string */
1502 httpGetDateString(time_t t) /* I - UNIX time */
1503 {
1504 struct tm *tdate;
1505 static char datetime[256];
1506
1507
1508 tdate = gmtime(&t);
1509 snprintf(datetime, sizeof(datetime), "%s, %02d %s %d %02d:%02d:%02d GMT",
1510 days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon],
1511 tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
1512
1513 return (datetime);
1514 }
1515
1516
1517 /*
1518 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
1519 */
1520
1521 time_t /* O - UNIX time */
1522 httpGetDateTime(const char *s) /* I - Date/time string */
1523 {
1524 int i; /* Looping var */
1525 struct tm tdate; /* Time/date structure */
1526 char mon[16]; /* Abbreviated month name */
1527 int day, year; /* Day of month and year */
1528 int hour, min, sec; /* Time */
1529
1530
1531 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
1532 return (0);
1533
1534 for (i = 0; i < 12; i ++)
1535 if (strcasecmp(mon, months[i]) == 0)
1536 break;
1537
1538 if (i >= 12)
1539 return (0);
1540
1541 tdate.tm_mon = i;
1542 tdate.tm_mday = day;
1543 tdate.tm_year = year - 1900;
1544 tdate.tm_hour = hour;
1545 tdate.tm_min = min;
1546 tdate.tm_sec = sec;
1547 tdate.tm_isdst = 0;
1548
1549 return (mktime(&tdate));
1550 }
1551
1552
1553 /*
1554 * 'httpUpdate()' - Update the current HTTP state for incoming data.
1555 */
1556
1557 http_status_t /* O - HTTP status */
1558 httpUpdate(http_t *http) /* I - HTTP data */
1559 {
1560 char line[1024], /* Line from connection... */
1561 *value; /* Pointer to value on line */
1562 http_field_t field; /* Field index */
1563 int major, minor, /* HTTP version numbers */
1564 status; /* Request status */
1565
1566
1567 DEBUG_printf(("httpUpdate(http=%p), state=%d\n", http, http->state));
1568
1569 /*
1570 * If we haven't issued any commands, then there is nothing to "update"...
1571 */
1572
1573 if (http->state == HTTP_WAITING)
1574 return (HTTP_CONTINUE);
1575
1576 /*
1577 * Grab all of the lines we can from the connection...
1578 */
1579
1580 while (httpGets(line, sizeof(line), http) != NULL)
1581 {
1582 DEBUG_printf(("httpUpdate: Got \"%s\"\n", line));
1583
1584 if (line[0] == '\0')
1585 {
1586 /*
1587 * Blank line means the start of the data section (if any). Return
1588 * the result code, too...
1589 *
1590 * If we get status 100 (HTTP_CONTINUE), then we *don't* change states.
1591 * Instead, we just return HTTP_CONTINUE to the caller and keep on
1592 * tryin'...
1593 */
1594
1595 if (http->status == HTTP_CONTINUE)
1596 return (http->status);
1597
1598 if (http->status < HTTP_BAD_REQUEST)
1599 http->digest_tries = 0;
1600
1601 #ifdef HAVE_SSL
1602 if (http->status == HTTP_SWITCHING_PROTOCOLS && !http->tls)
1603 {
1604 if (http_setup_ssl(http) != 0)
1605 {
1606 # ifdef WIN32
1607 closesocket(http->fd);
1608 # else
1609 close(http->fd);
1610 # endif /* WIN32 */
1611
1612 return (HTTP_ERROR);
1613 }
1614
1615 return (HTTP_CONTINUE);
1616 }
1617 #endif /* HAVE_SSL */
1618
1619 httpGetLength(http);
1620
1621 switch (http->state)
1622 {
1623 case HTTP_GET :
1624 case HTTP_POST :
1625 case HTTP_POST_RECV :
1626 case HTTP_PUT :
1627 http->state ++;
1628 case HTTP_POST_SEND :
1629 break;
1630
1631 default :
1632 http->state = HTTP_WAITING;
1633 break;
1634 }
1635
1636 return (http->status);
1637 }
1638 else if (strncmp(line, "HTTP/", 5) == 0)
1639 {
1640 /*
1641 * Got the beginning of a response...
1642 */
1643
1644 if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3)
1645 return (HTTP_ERROR);
1646
1647 http->version = (http_version_t)(major * 100 + minor);
1648 http->status = (http_status_t)status;
1649 }
1650 else if ((value = strchr(line, ':')) != NULL)
1651 {
1652 /*
1653 * Got a value...
1654 */
1655
1656 *value++ = '\0';
1657 while (isspace(*value & 255))
1658 value ++;
1659
1660 /*
1661 * Be tolerants of servers that send unknown attribute fields...
1662 */
1663
1664 if (!strcasecmp(line, "expect"))
1665 {
1666 /*
1667 * "Expect: 100-continue" or similar...
1668 */
1669
1670 http->expect = (http_status_t)atoi(value);
1671 }
1672 else if (!strcasecmp(line, "cookie"))
1673 {
1674 /*
1675 * "Cookie: name=value[; name=value ...]" - replaces previous cookies...
1676 */
1677
1678 httpSetCookie(http, value);
1679 }
1680 else if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN)
1681 {
1682 DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line));
1683 continue;
1684 }
1685 else
1686 httpSetField(http, field, value);
1687 }
1688 else
1689 {
1690 http->status = HTTP_ERROR;
1691 return (HTTP_ERROR);
1692 }
1693 }
1694
1695 /*
1696 * See if there was an error...
1697 */
1698
1699 if (http->error == EPIPE && http->status > HTTP_CONTINUE)
1700 return (http->status);
1701
1702 if (http->error)
1703 {
1704 DEBUG_printf(("httpUpdate: socket error %d - %s\n", http->error,
1705 strerror(http->error)));
1706 http->status = HTTP_ERROR;
1707 return (HTTP_ERROR);
1708 }
1709
1710 /*
1711 * If we haven't already returned, then there is nothing new...
1712 */
1713
1714 return (HTTP_CONTINUE);
1715 }
1716
1717
1718 /*
1719 * 'httpDecode64()' - Base64-decode a string.
1720 */
1721
1722 char * /* O - Decoded string */
1723 httpDecode64(char *out, /* I - String to write to */
1724 const char *in) /* I - String to read from */
1725 {
1726 return (httpDecode64_2(out, in, 512));
1727 }
1728
1729
1730 /*
1731 * 'httpDecode64_2()' - Base64-decode a string.
1732 */
1733
1734 char * /* O - Decoded string */
1735 httpDecode64_2(char *out, /* I - String to write to */
1736 const char *in, /* I - String to read from */
1737 int outlen) /* I - Size of output string */
1738 {
1739 int pos, /* Bit position */
1740 base64; /* Value of this character */
1741 char *outptr, /* Output pointer */
1742 *outend; /* End of output buffer */
1743
1744
1745 for (outptr = out, outend = out + outlen - 1, pos = 0; *in != '\0'; in ++)
1746 {
1747 /*
1748 * Decode this character into a number from 0 to 63...
1749 */
1750
1751 if (*in >= 'A' && *in <= 'Z')
1752 base64 = *in - 'A';
1753 else if (*in >= 'a' && *in <= 'z')
1754 base64 = *in - 'a' + 26;
1755 else if (*in >= '0' && *in <= '9')
1756 base64 = *in - '0' + 52;
1757 else if (*in == '+')
1758 base64 = 62;
1759 else if (*in == '/')
1760 base64 = 63;
1761 else if (*in == '=')
1762 break;
1763 else
1764 continue;
1765
1766 /*
1767 * Store the result in the appropriate chars...
1768 */
1769
1770 switch (pos)
1771 {
1772 case 0 :
1773 if (outptr < outend)
1774 *outptr = base64 << 2;
1775 pos ++;
1776 break;
1777 case 1 :
1778 if (outptr < outend)
1779 *outptr++ |= (base64 >> 4) & 3;
1780 if (outptr < outend)
1781 *outptr = (base64 << 4) & 255;
1782 pos ++;
1783 break;
1784 case 2 :
1785 if (outptr < outend)
1786 *outptr++ |= (base64 >> 2) & 15;
1787 if (outptr < outend)
1788 *outptr = (base64 << 6) & 255;
1789 pos ++;
1790 break;
1791 case 3 :
1792 if (outptr < outend)
1793 *outptr++ |= base64;
1794 pos = 0;
1795 break;
1796 }
1797 }
1798
1799 *outptr = '\0';
1800
1801 /*
1802 * Return the decoded string...
1803 */
1804
1805 return (out);
1806 }
1807
1808
1809 /*
1810 * 'httpEncode64()' - Base64-encode a string.
1811 */
1812
1813 char * /* O - Encoded string */
1814 httpEncode64(char *out, /* I - String to write to */
1815 const char *in) /* I - String to read from */
1816 {
1817 return (httpEncode64_2(out, in, 512));
1818 }
1819
1820
1821 /*
1822 * 'httpEncode64_2()' - Base64-encode a string.
1823 */
1824
1825 char * /* O - Encoded string */
1826 httpEncode64_2(char *out, /* I - String to write to */
1827 const char *in, /* I - String to read from */
1828 int outlen) /* I - Size of output string */
1829 {
1830 char *outptr, /* Output pointer */
1831 *outend; /* End of output buffer */
1832 static const char base64[] = /* Base64 characters... */
1833 {
1834 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1835 "abcdefghijklmnopqrstuvwxyz"
1836 "0123456789"
1837 "+/"
1838 };
1839
1840
1841 for (outptr = out, outend = out + outlen - 1; *in != '\0'; in ++)
1842 {
1843 /*
1844 * Encode the up to 3 characters as 4 Base64 numbers...
1845 */
1846
1847 if (outptr < outend)
1848 *outptr ++ = base64[in[0] >> 2];
1849 if (outptr < outend)
1850 *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63];
1851
1852 in ++;
1853 if (*in == '\0')
1854 {
1855 if (outptr < outend)
1856 *outptr ++ = '=';
1857 if (outptr < outend)
1858 *outptr ++ = '=';
1859 break;
1860 }
1861
1862 if (outptr < outend)
1863 *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63];
1864
1865 in ++;
1866 if (*in == '\0')
1867 {
1868 if (outptr < outend)
1869 *outptr ++ = '=';
1870 break;
1871 }
1872
1873 if (outptr < outend)
1874 *outptr ++ = base64[in[0] & 63];
1875 }
1876
1877 *outptr = '\0';
1878
1879 /*
1880 * Return the encoded string...
1881 */
1882
1883 return (out);
1884 }
1885
1886
1887 /*
1888 * 'httpGetLength()' - Get the amount of data remaining from the
1889 * content-length or transfer-encoding fields.
1890 */
1891
1892 int /* O - Content length */
1893 httpGetLength(http_t *http) /* I - HTTP data */
1894 {
1895 DEBUG_printf(("httpGetLength(http=%p), state=%d\n", http, http->state));
1896
1897 if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
1898 {
1899 DEBUG_puts("httpGetLength: chunked request!");
1900
1901 http->data_encoding = HTTP_ENCODE_CHUNKED;
1902 http->data_remaining = 0;
1903 }
1904 else
1905 {
1906 http->data_encoding = HTTP_ENCODE_LENGTH;
1907
1908 /*
1909 * The following is a hack for HTTP servers that don't send a
1910 * content-length or transfer-encoding field...
1911 *
1912 * If there is no content-length then the connection must close
1913 * after the transfer is complete...
1914 */
1915
1916 if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0')
1917 http->data_remaining = 2147483647;
1918 else
1919 http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]);
1920
1921 DEBUG_printf(("httpGetLength: content_length=%d\n", http->data_remaining));
1922 }
1923
1924 return (http->data_remaining);
1925 }
1926
1927
1928 /*
1929 * 'http_field()' - Return the field index for a field name.
1930 */
1931
1932 static http_field_t /* O - Field index */
1933 http_field(const char *name) /* I - String name */
1934 {
1935 int i; /* Looping var */
1936
1937
1938 for (i = 0; i < HTTP_FIELD_MAX; i ++)
1939 if (strcasecmp(name, http_fields[i]) == 0)
1940 return ((http_field_t)i);
1941
1942 return (HTTP_FIELD_UNKNOWN);
1943 }
1944
1945
1946 /*
1947 * 'http_send()' - Send a request with all fields and the trailing blank line.
1948 */
1949
1950 static int /* O - 0 on success, non-zero on error */
1951 http_send(http_t *http, /* I - HTTP data */
1952 http_state_t request, /* I - Request code */
1953 const char *uri) /* I - URI */
1954 {
1955 int i; /* Looping var */
1956 char *ptr, /* Pointer in buffer */
1957 buf[1024]; /* Encoded URI buffer */
1958 static const char * const codes[] =
1959 { /* Request code strings */
1960 NULL,
1961 "OPTIONS",
1962 "GET",
1963 NULL,
1964 "HEAD",
1965 "POST",
1966 NULL,
1967 NULL,
1968 "PUT",
1969 NULL,
1970 "DELETE",
1971 "TRACE",
1972 "CLOSE"
1973 };
1974 static const char hex[] = "0123456789ABCDEF";
1975 /* Hex digits */
1976
1977
1978 DEBUG_printf(("http_send(http=%p, request=HTTP_%s, uri=\"%s\")\n",
1979 http, codes[request], uri));
1980
1981 if (http == NULL || uri == NULL)
1982 return (-1);
1983
1984 /*
1985 * Encode the URI as needed...
1986 */
1987
1988 for (ptr = buf; *uri != '\0' && ptr < (buf + sizeof(buf) - 1); uri ++)
1989 if (*uri <= ' ' || *uri >= 127)
1990 {
1991 if (ptr < (buf + sizeof(buf) - 1))
1992 *ptr ++ = '%';
1993 if (ptr < (buf + sizeof(buf) - 1))
1994 *ptr ++ = hex[(*uri >> 4) & 15];
1995 if (ptr < (buf + sizeof(buf) - 1))
1996 *ptr ++ = hex[*uri & 15];
1997 }
1998 else
1999 *ptr ++ = *uri;
2000
2001 *ptr = '\0';
2002
2003 /*
2004 * See if we had an error the last time around; if so, reconnect...
2005 */
2006
2007 if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST)
2008 httpReconnect(http);
2009
2010 /*
2011 * Send the request header...
2012 */
2013
2014 http->state = request;
2015 if (request == HTTP_POST || request == HTTP_PUT)
2016 http->state ++;
2017
2018 http->status = HTTP_CONTINUE;
2019
2020 #ifdef HAVE_SSL
2021 if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls)
2022 {
2023 httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");
2024 httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.0,SSL/2.0,SSL/3.0");
2025 }
2026 #endif /* HAVE_SSL */
2027
2028 if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
2029 {
2030 http->status = HTTP_ERROR;
2031 return (-1);
2032 }
2033
2034 for (i = 0; i < HTTP_FIELD_MAX; i ++)
2035 if (http->fields[i][0] != '\0')
2036 {
2037 DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i]));
2038
2039 if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1)
2040 {
2041 http->status = HTTP_ERROR;
2042 return (-1);
2043 }
2044 }
2045
2046 if (httpPrintf(http, "\r\n") < 1)
2047 {
2048 http->status = HTTP_ERROR;
2049 return (-1);
2050 }
2051
2052 httpClearFields(http);
2053
2054 return (0);
2055 }
2056
2057
2058 /*
2059 * 'http_wait()' - Wait for data available on a connection.
2060 */
2061
2062 static int /* O - 1 if data is available, 0 otherwise */
2063 http_wait(http_t *http, /* I - HTTP data */
2064 int msec) /* I - Milliseconds to wait */
2065 {
2066 #ifndef WIN32
2067 struct rlimit limit; /* Runtime limit */
2068 #endif /* !WIN32 */
2069 struct timeval timeout; /* Timeout */
2070 int nfds; /* Result from select() */
2071 int set_size; /* Size of select set */
2072
2073
2074 DEBUG_printf(("http_wait(http=%p, msec=%d)\n", http, msec));
2075
2076 /*
2077 * Check the SSL/TLS buffers for data first...
2078 */
2079
2080 #ifdef HAVE_SSL
2081 if (http->tls)
2082 {
2083 # ifdef HAVE_LIBSSL
2084 if (SSL_pending((SSL *)(http->tls)))
2085 return (1);
2086 # elif defined(HAVE_GNUTLS)
2087 if (gnutls_record_check_pending(((http_tls_t *)(http->tls))->session))
2088 return (1);
2089 # elif defined(HAVE_CDSASSL)
2090 size_t bytes; /* Bytes that are available */
2091
2092 if (!SSLGetBufferedReadSize((SSLContextRef)http->tls, &bytes) && bytes > 0)
2093 return;
2094 # endif /* HAVE_LIBSSL */
2095 }
2096 #endif /* HAVE_SSL */
2097
2098 /*
2099 * Then try doing a select() to poll the socket...
2100 */
2101
2102 if (!http->input_set)
2103 {
2104 #ifdef WIN32
2105 /*
2106 * Windows has a fixed-size select() structure, different (surprise,
2107 * surprise!) from all UNIX implementations. Just allocate this
2108 * fixed structure...
2109 */
2110
2111 http->input_set = calloc(1, sizeof(fd_set));
2112 #else
2113 /*
2114 * Allocate the select() input set based upon the max number of file
2115 * descriptors available for this process...
2116 */
2117
2118 getrlimit(RLIMIT_NOFILE, &limit);
2119
2120 set_size = (limit.rlim_cur + 31) / 8 + 4;
2121 if (set_size < sizeof(fd_set))
2122 set_size = sizeof(fd_set);
2123
2124 http->input_set = calloc(1, set_size);
2125 #endif /* WIN32 */
2126
2127 if (!http->input_set)
2128 return (0);
2129 }
2130
2131 FD_SET(http->fd, http->input_set);
2132
2133 if (msec >= 0)
2134 {
2135 timeout.tv_sec = msec / 1000;
2136 timeout.tv_usec = (msec % 1000) * 1000;
2137
2138 nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout);
2139 }
2140 else
2141 nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL);
2142
2143 FD_CLR(http->fd, http->input_set);
2144
2145 return (nfds > 0);
2146 }
2147
2148
2149 #ifdef HAVE_SSL
2150 /*
2151 * 'http_upgrade()' - Force upgrade to TLS encryption.
2152 */
2153
2154 static int /* O - Status of connection */
2155 http_upgrade(http_t *http) /* I - HTTP data */
2156 {
2157 int ret; /* Return value */
2158 http_t myhttp; /* Local copy of HTTP data */
2159
2160
2161 DEBUG_printf(("http_upgrade(%p)\n", http));
2162
2163 /*
2164 * Copy the HTTP data to a local variable so we can do the OPTIONS
2165 * request without interfering with the existing request data...
2166 */
2167
2168 memcpy(&myhttp, http, sizeof(myhttp));
2169
2170 /*
2171 * Send an OPTIONS request to the server, requiring SSL or TLS
2172 * encryption on the link...
2173 */
2174
2175 httpClearFields(&myhttp);
2176 httpSetField(&myhttp, HTTP_FIELD_CONNECTION, "upgrade");
2177 httpSetField(&myhttp, HTTP_FIELD_UPGRADE, "TLS/1.0, SSL/2.0, SSL/3.0");
2178
2179 if ((ret = httpOptions(&myhttp, "*")) == 0)
2180 {
2181 /*
2182 * Wait for the secure connection...
2183 */
2184
2185 while (httpUpdate(&myhttp) == HTTP_CONTINUE);
2186 }
2187
2188 httpFlush(&myhttp);
2189
2190 /*
2191 * Copy the HTTP data back over, if any...
2192 */
2193
2194 http->fd = myhttp.fd;
2195 http->error = myhttp.error;
2196 http->activity = myhttp.activity;
2197 http->status = myhttp.status;
2198 http->version = myhttp.version;
2199 http->keep_alive = myhttp.keep_alive;
2200 http->used = myhttp.used;
2201
2202 if (http->used)
2203 memcpy(http->buffer, myhttp.buffer, http->used);
2204
2205 http->auth_type = myhttp.auth_type;
2206 http->nonce_count = myhttp.nonce_count;
2207
2208 memcpy(http->nonce, myhttp.nonce, sizeof(http->nonce));
2209
2210 http->tls = myhttp.tls;
2211 http->encryption = myhttp.encryption;
2212
2213 /*
2214 * See if we actually went secure...
2215 */
2216
2217 if (!http->tls)
2218 {
2219 /*
2220 * Server does not support HTTP upgrade...
2221 */
2222
2223 DEBUG_puts("Server does not support HTTP upgrade!");
2224
2225 # ifdef WIN32
2226 closesocket(http->fd);
2227 # else
2228 close(http->fd);
2229 # endif
2230
2231 http->fd = -1;
2232
2233 return (-1);
2234 }
2235 else
2236 return (ret);
2237 }
2238
2239
2240 /*
2241 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
2242 */
2243
2244 static int /* O - Status of connection */
2245 http_setup_ssl(http_t *http) /* I - HTTP data */
2246 {
2247 # ifdef HAVE_LIBSSL
2248 SSL_CTX *context; /* Context for encryption */
2249 SSL *conn; /* Connection for encryption */
2250 # elif defined(HAVE_GNUTLS)
2251 http_tls_t *conn; /* TLS session object */
2252 gnutls_certificate_client_credentials *credentials;
2253 /* TLS credentials */
2254 # elif defined(HAVE_CDSASSL)
2255 SSLContextRef conn; /* Context for encryption */
2256 OSStatus error; /* Error info */
2257 # endif /* HAVE_LIBSSL */
2258
2259
2260 DEBUG_printf(("http_setup_ssl(http=%p)\n", http));
2261
2262 # ifdef HAVE_LIBSSL
2263 context = SSL_CTX_new(SSLv23_client_method());
2264 conn = SSL_new(context);
2265
2266 SSL_set_fd(conn, http->fd);
2267 if (SSL_connect(conn) != 1)
2268 {
2269 # ifdef DEBUG
2270 unsigned long error; /* Error code */
2271
2272 while ((error = ERR_get_error()) != 0)
2273 printf("http_setup_ssl: %s\n", ERR_error_string(error, NULL));
2274 # endif /* DEBUG */
2275
2276 SSL_CTX_free(context);
2277 SSL_free(conn);
2278
2279 # ifdef WIN32
2280 http->error = WSAGetLastError();
2281 # else
2282 http->error = errno;
2283 # endif /* WIN32 */
2284 http->status = HTTP_ERROR;
2285
2286 return (HTTP_ERROR);
2287 }
2288
2289 # elif defined(HAVE_GNUTLS)
2290 conn = (http_tls_t *)malloc(sizeof(http_tls_t));
2291
2292 if (conn == NULL)
2293 {
2294 http->error = errno;
2295 http->status = HTTP_ERROR;
2296
2297 return (-1);
2298 }
2299
2300 credentials = (gnutls_certificate_client_credentials *)
2301 malloc(sizeof(gnutls_certificate_client_credentials));
2302 if (credentials == NULL)
2303 {
2304 free(conn);
2305
2306 http->error = errno;
2307 http->status = HTTP_ERROR;
2308
2309 return (-1);
2310 }
2311
2312 gnutls_certificate_allocate_credentials(credentials);
2313
2314 gnutls_init(&(conn->session), GNUTLS_CLIENT);
2315 gnutls_set_default_priority(conn->session);
2316 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
2317 gnutls_transport_set_ptr(conn->session, http->fd);
2318
2319 if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS)
2320 {
2321 http->error = errno;
2322 http->status = HTTP_ERROR;
2323
2324 return (-1);
2325 }
2326
2327 conn->credentials = credentials;
2328
2329 # elif defined(HAVE_CDSASSL)
2330 error = SSLNewContext(false, &conn);
2331
2332 if (!error)
2333 error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc);
2334
2335 if (!error)
2336 error = SSLSetConnection(conn, (SSLConnectionRef)http->fd);
2337
2338 if (!error)
2339 error = SSLSetAllowsExpiredCerts(conn, true);
2340
2341 if (!error)
2342 error = SSLSetAllowsAnyRoot(conn, true);
2343
2344 if (!error)
2345 error = SSLHandshake(conn);
2346
2347 if (error != 0)
2348 {
2349 http->error = error;
2350 http->status = HTTP_ERROR;
2351
2352 SSLDisposeContext(conn);
2353
2354 close(http->fd);
2355
2356 return (-1);
2357 }
2358 # endif /* HAVE_CDSASSL */
2359
2360 http->tls = conn;
2361 return (0);
2362 }
2363
2364
2365 /*
2366 * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
2367 */
2368
2369 static void
2370 http_shutdown_ssl(http_t *http) /* I - HTTP data */
2371 {
2372 # ifdef HAVE_LIBSSL
2373 SSL_CTX *context; /* Context for encryption */
2374 SSL *conn; /* Connection for encryption */
2375
2376
2377 conn = (SSL *)(http->tls);
2378 context = SSL_get_SSL_CTX(conn);
2379
2380 SSL_shutdown(conn);
2381 SSL_CTX_free(context);
2382 SSL_free(conn);
2383
2384 # elif defined(HAVE_GNUTLS)
2385 http_tls_t *conn; /* Encryption session */
2386 gnutls_certificate_client_credentials *credentials;
2387 /* TLS credentials */
2388
2389
2390 conn = (http_tls_t *)(http->tls);
2391 credentials = (gnutls_certificate_client_credentials *)(conn->credentials);
2392
2393 gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
2394 gnutls_deinit(conn->session);
2395 gnutls_certificate_free_credentials(*credentials);
2396 free(credentials);
2397 free(conn);
2398
2399 # elif defined(HAVE_CDSASSL)
2400 SSLClose((SSLContextRef)http->tls);
2401 SSLDisposeContext((SSLContextRef)http->tls);
2402 # endif /* HAVE_LIBSSL */
2403
2404 http->tls = NULL;
2405 }
2406
2407
2408 /*
2409 * 'http_read_ssl()' - Read from a SSL/TLS connection.
2410 */
2411
2412 static int /* O - Bytes read */
2413 http_read_ssl(http_t *http, /* I - HTTP data */
2414 char *buf, /* I - Buffer to store data */
2415 int len) /* I - Length of buffer */
2416 {
2417 # if defined(HAVE_LIBSSL)
2418 return (SSL_read((SSL *)(http->tls), buf, len));
2419
2420 # elif defined(HAVE_GNUTLS)
2421 return (gnutls_record_recv(((http_tls_t *)(http->tls))->session, buf, len));
2422
2423 # elif defined(HAVE_CDSASSL)
2424 OSStatus error; /* Error info */
2425 size_t processed; /* Number of bytes processed */
2426
2427
2428 error = SSLRead((SSLContextRef)http->tls, buf, len, &processed);
2429
2430 if (error == 0)
2431 return (processed);
2432 else
2433 {
2434 http->error = error;
2435
2436 return (-1);
2437 }
2438 # endif /* HAVE_LIBSSL */
2439 }
2440
2441
2442 /*
2443 * 'http_write_ssl()' - Write to a SSL/TLS connection.
2444 */
2445
2446 static int /* O - Bytes written */
2447 http_write_ssl(http_t *http, /* I - HTTP data */
2448 const char *buf, /* I - Buffer holding data */
2449 int len) /* I - Length of buffer */
2450 {
2451 # if defined(HAVE_LIBSSL)
2452 return (SSL_write((SSL *)(http->tls), buf, len));
2453
2454 # elif defined(HAVE_GNUTLS)
2455 return (gnutls_record_send(((http_tls_t *)(http->tls))->session, buf, len));
2456 # elif defined(HAVE_CDSASSL)
2457 OSStatus error; /* Error info */
2458 size_t processed; /* Number of bytes processed */
2459
2460
2461 error = SSLWrite((SSLContextRef)http->tls, buf, len, &processed);
2462
2463 if (error == 0)
2464 return (processed);
2465 else
2466 {
2467 http->error = error;
2468 return (-1);
2469 }
2470 # endif /* HAVE_LIBSSL */
2471 }
2472
2473
2474 # if defined(HAVE_CDSASSL)
2475 /*
2476 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
2477 */
2478
2479 static OSStatus /* O - -1 on error, 0 on success */
2480 CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
2481 void *data, /* I - Data buffer */
2482 size_t *dataLength) /* IO - Number of bytes */
2483 {
2484 ssize_t bytes; /* Number of bytes read */
2485
2486
2487 bytes = recv((int)connection, data, *dataLength, 0);
2488 if (bytes >= 0)
2489 {
2490 *dataLength = bytes;
2491 return (0);
2492 }
2493 else
2494 return (-1);
2495 }
2496
2497
2498 /*
2499 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
2500 */
2501
2502 static OSStatus /* O - -1 on error, 0 on success */
2503 CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
2504 const void *data, /* I - Data buffer */
2505 size_t *dataLength) /* IO - Number of bytes */
2506 {
2507 ssize_t bytes;
2508
2509
2510 bytes = write((int)connection, data, *dataLength);
2511 if (bytes >= 0)
2512 {
2513 *dataLength = bytes;
2514 return (0);
2515 }
2516 else
2517 return (-1);
2518 }
2519 # endif /* HAVE_CDSASSL */
2520 #endif /* HAVE_SSL */
2521
2522
2523 /*
2524 * End of "$Id: http.c,v 1.82.2.54 2004/07/02 04:08:59 mike Exp $".
2525 */