]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/http.c
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / curl / lib / http.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  * 
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: http.c,v 1.204 2004/03/14 18:15:04 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifndef CURL_DISABLE_HTTP
27 /* -- WIN32 approved -- */
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <errno.h>
37
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
39 #include <time.h>
40 #include <io.h>
41 #else
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #include <sys/time.h>
49
50 #ifdef HAVE_TIME_H
51 #ifdef TIME_WITH_SYS_TIME
52 #include <time.h>
53 #endif
54 #endif
55
56 #ifdef HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59 #include <netdb.h>
60 #ifdef HAVE_ARPA_INET_H
61 #include <arpa/inet.h>
62 #endif
63 #ifdef HAVE_NET_IF_H
64 #include <net/if.h>
65 #endif
66 #include <sys/ioctl.h>
67 #include <signal.h>
68
69 #ifdef HAVE_SYS_PARAM_H
70 #include <sys/param.h>
71 #endif
72
73 #ifdef HAVE_SYS_SELECT_H
74 #include <sys/select.h>
75 #endif
76
77
78 #endif
79
80 #include "urldata.h"
81 #include <curl/curl.h>
82 #include "transfer.h"
83 #include "sendf.h"
84 #include "formdata.h"
85 #include "progress.h"
86 #include "base64.h"
87 #include "cookie.h"
88 #include "strequal.h"
89 #include "ssluse.h"
90 #include "http_digest.h"
91 #include "http_ntlm.h"
92 #include "http_negotiate.h"
93 #include "url.h"
94 #include "share.h"
95 #include "http.h"
96
97 #define _MPRINTF_REPLACE /* use our functions only */
98 #include <curl/mprintf.h>
99
100 /* The last #include file should be: */
101 #ifdef CURLDEBUG
102 #include "memdebug.h"
103 #endif
104
105 static CURLcode Curl_output_basic_proxy(struct connectdata *conn);
106
107 /*
108  * This function checks the linked list of custom HTTP headers for a particular
109  * header (prefix).
110  */
111 static char *checkheaders(struct SessionHandle *data, const char *thisheader)
112 {
113   struct curl_slist *head;
114   size_t thislen = strlen(thisheader);
115
116   for(head = data->set.headers; head; head=head->next) {
117     if(strnequal(head->data, thisheader, thislen))
118       return head->data;
119   }
120   return NULL;
121 }
122
123 static CURLcode Curl_output_basic(struct connectdata *conn)
124 {
125   char *authorization;
126   struct SessionHandle *data=conn->data;
127
128   sprintf(data->state.buffer, "%s:%s", conn->user, conn->passwd);
129   if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
130                         &authorization) > 0) {
131     if(conn->allocptr.userpwd)
132       free(conn->allocptr.userpwd);
133     conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012",
134                                       authorization);
135     free(authorization);
136   }
137   else
138     return CURLE_OUT_OF_MEMORY;
139   return CURLE_OK;
140 }
141
142 static CURLcode Curl_output_basic_proxy(struct connectdata *conn)
143 {
144   char *authorization;
145   struct SessionHandle *data=conn->data;
146
147   sprintf(data->state.buffer, "%s:%s",
148           conn->proxyuser, conn->proxypasswd);
149   if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
150                         &authorization) > 0) {
151     Curl_safefree(conn->allocptr.proxyuserpwd);
152     conn->allocptr.proxyuserpwd =
153       aprintf("Proxy-authorization: Basic %s\015\012", authorization);
154     free(authorization);
155   }
156   else
157     return CURLE_OUT_OF_MEMORY;
158   return CURLE_OK;
159 }
160
161 /*
162  * Curl_http_auth_act() checks what authentication methods that are available
163  * and decides which one (if any) to use. It will set 'newurl' if an auth
164  * metod was picked.
165  */
166
167 void Curl_http_auth_act(struct connectdata *conn)
168 {
169   struct SessionHandle *data = conn->data;
170
171   if(data->state.authavail) {
172     /* The order of these checks is highly relevant, as this will be the order
173        of preference in case of the existance of multiple accepted types. */
174     if(data->state.authavail & CURLAUTH_GSSNEGOTIATE)
175       data->state.authwant = CURLAUTH_GSSNEGOTIATE;
176     else if(data->state.authavail & CURLAUTH_DIGEST)
177       data->state.authwant = CURLAUTH_DIGEST;
178     else if(data->state.authavail & CURLAUTH_NTLM)
179       data->state.authwant = CURLAUTH_NTLM;
180     else if(data->state.authavail & CURLAUTH_BASIC)
181       data->state.authwant = CURLAUTH_BASIC;
182     else
183       data->state.authwant = CURLAUTH_NONE; /* clear it */
184
185     if(data->state.authwant)
186       conn->newurl = strdup(data->change.url); /* clone URL */
187     data->state.authavail = CURLAUTH_NONE; /* clear it here */
188   }
189 }
190
191 /*
192  * Setup the authentication headers for the host/proxy and the correct
193  * authentication method.
194  */
195
196 static CURLcode http_auth_headers(struct connectdata *conn,
197                                   char *request,
198                                   char *path,
199                                   bool *ready) /* set TRUE when the auth phase
200                                            is done and ready to do the *actual*
201                                            request */
202 {
203   CURLcode result = CURLE_OK;
204   struct SessionHandle *data = conn->data;
205   char *auth=NULL;
206
207   *ready = FALSE; /* default is no */
208
209   if(!data->state.authstage) {
210     if(conn->bits.httpproxy && conn->bits.proxy_user_passwd)
211       Curl_http_auth_stage(data, 407);
212     else if(conn->bits.user_passwd)
213       Curl_http_auth_stage(data, 401);
214     else {
215       *ready = TRUE;
216       return CURLE_OK; /* no authentication with no user or password */
217     }
218   }
219
220   /* To prevent the user+password to get sent to other than the original
221      host due to a location-follow, we do some weirdo checks here */
222   if(!data->state.this_is_a_follow ||
223      !data->state.auth_host ||
224      curl_strequal(data->state.auth_host, conn->hostname) ||
225      data->set.http_disable_hostname_check_before_authentication) {
226
227   /* Send proxy authentication header if needed */
228     if (data->state.authstage == 407) {
229 #ifdef USE_SSLEAY
230       if(data->state.authwant == CURLAUTH_NTLM) {
231         auth=(char *)"NTLM";
232         result = Curl_output_ntlm(conn, TRUE, ready);
233         if(result)
234           return result;
235       }
236       else
237 #endif
238       if(data->state.authwant == CURLAUTH_BASIC) {
239         /* Basic */
240         if(conn->bits.proxy_user_passwd &&
241            !checkheaders(data, "Proxy-authorization:")) {
242           auth=(char *)"Basic";
243           result = Curl_output_basic_proxy(conn);
244           if(result)
245             return result;
246         }
247         *ready = TRUE;
248         /* Switch to web authentication after proxy authentication is done */
249         Curl_http_auth_stage(data, 401);
250       }
251       infof(data, "Proxy auth using %s with user '%s'\n",
252             auth, conn->proxyuser?conn->proxyuser:"");
253     }
254     /* Send web authentication header if needed */
255     if (data->state.authstage == 401) {
256       auth = NULL;
257 #ifdef HAVE_GSSAPI
258       if((data->state.authwant == CURLAUTH_GSSNEGOTIATE) &&
259          data->state.negotiate.context && 
260          !GSS_ERROR(data->state.negotiate.status)) {
261         auth=(char *)"GSS-Negotiate";
262         result = Curl_output_negotiate(conn);
263         if (result)
264           return result;
265         *ready = TRUE;
266       }
267       else
268 #endif
269 #ifdef USE_SSLEAY
270       if(data->state.authwant == CURLAUTH_NTLM) {
271         auth=(char *)"NTLM";
272         result = Curl_output_ntlm(conn, FALSE, ready);
273         if(result)
274           return result;
275       }
276       else
277 #endif
278       {
279         if((data->state.authwant == CURLAUTH_DIGEST) &&
280            data->state.digest.nonce) {
281           auth=(char *)"Digest";
282           result = Curl_output_digest(conn,
283                                       (unsigned char *)request,
284                                       (unsigned char *)path);
285           if(result)
286             return result;
287           *ready = TRUE;
288         }
289         else if(data->state.authwant == CURLAUTH_BASIC) {/* Basic */
290           if(conn->bits.user_passwd &&
291              !checkheaders(data, "Authorization:")) {
292             auth=(char *)"Basic";
293             result = Curl_output_basic(conn);
294             if(result)
295               return result;
296           }
297           /* basic is always ready */
298           *ready = TRUE;
299         }
300       }
301       if(auth)
302         infof(data, "Server auth using %s with user '%s'\n",
303               auth, conn->user);
304     }
305   }
306   else
307     *ready = TRUE;
308
309   return result;
310 }
311
312
313 /*
314  * Curl_http_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
315  * headers. They are dealt with both in the transfer.c main loop and in the
316  * proxy CONNECT loop.
317  */
318
319 CURLcode Curl_http_auth(struct connectdata *conn,
320                         int httpcode,
321                         char *header) /* pointing to the first non-space */
322 {
323   /*
324    * This resource requires authentication
325    */
326   struct SessionHandle *data = conn->data;
327
328   long *availp;
329   char *start;
330
331   if (httpcode == 407) {
332     start = header+strlen("Proxy-authenticate:");
333     availp = &data->info.proxyauthavail;
334   }
335   else {
336     start = header+strlen("WWW-Authenticate:");
337     availp = &data->info.httpauthavail;
338   }
339   /*
340    * Switch from proxy to web authentication and back if needed
341    */
342   if (httpcode == 407 && data->state.authstage != 407)
343     Curl_http_auth_stage(data, 407);
344               
345   else if (httpcode == 401 && data->state.authstage != 401)
346     Curl_http_auth_stage(data, 401);
347
348   /* pass all white spaces */
349   while(*start && isspace((int)*start))
350     start++;
351
352   /*
353    * Here we check if we want the specific single authentiction (using ==) and
354    * if we do, we initiate usage of it.
355    *
356    * If the provided authentication is wanted as one out of several accepted
357    * types (using &), we OR this authenticaion type to the authavail
358    * variable.
359    */
360   
361 #ifdef HAVE_GSSAPI
362   if (checkprefix("GSS-Negotiate", start) ||
363       checkprefix("Negotiate", start)) {
364     *availp |= CURLAUTH_GSSNEGOTIATE;
365     if(data->state.authwant == CURLAUTH_GSSNEGOTIATE) {
366       /* if exactly this is wanted, go */
367       int neg = Curl_input_negotiate(conn, start);
368       if (neg == 0)
369         conn->newurl = strdup(data->change.url);
370     }
371     else
372       if(data->state.authwant & CURLAUTH_GSSNEGOTIATE)
373         data->state.authavail |= CURLAUTH_GSSNEGOTIATE;
374   }
375   else
376 #endif
377 #ifdef USE_SSLEAY
378     /* NTLM support requires the SSL crypto libs */
379     if(checkprefix("NTLM", start)) {
380       *availp |= CURLAUTH_NTLM;
381       if(data->state.authwant == CURLAUTH_NTLM) {
382         /* NTLM authentication is activated */
383         CURLntlm ntlm =
384           Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
385                   
386         if(CURLNTLM_BAD != ntlm)
387           conn->newurl = strdup(data->change.url); /* clone string */
388         else
389           infof(data, "Authentication problem. Ignoring this.\n");
390       }
391       else
392         if(data->state.authwant & CURLAUTH_NTLM)
393           data->state.authavail |= CURLAUTH_NTLM;
394     }
395     else
396 #endif
397       if(checkprefix("Digest", start)) {
398         *availp |= CURLAUTH_DIGEST;
399         if(data->state.authwant == CURLAUTH_DIGEST) {
400           /* Digest authentication is activated */
401           CURLdigest dig = CURLDIGEST_BAD;
402
403           if(data->state.digest.nonce)
404             infof(data, "Authentication problem. Ignoring this.\n");
405           else
406             dig = Curl_input_digest(conn, start);
407           
408           if(CURLDIGEST_FINE == dig)
409             /* We act on it. Store our new url, which happens to be
410                the same one we already use! */
411             conn->newurl = strdup(data->change.url); /* clone string */
412         }
413         else
414           if(data->state.authwant & CURLAUTH_DIGEST) {
415             /* We don't know if Digest is what we're gonna use, but we
416                call this function anyway to store the digest data that
417                is provided on this line, to skip the extra round-trip
418                we need to do otherwise. We must sure to free this
419                data! */
420             Curl_input_digest(conn, start);
421             data->state.authavail |= CURLAUTH_DIGEST;
422           }
423       }
424       else if(checkprefix("Basic", start)) {
425         *availp |= CURLAUTH_BASIC;
426         if((data->state.authwant == CURLAUTH_BASIC) &&
427            (httpcode == data->state.authstage)) {
428           /* We asked for Basic authentication but got a 40X back
429              anyway, which basicly means our name+password isn't
430              valid. */
431           data->state.authavail = CURLAUTH_NONE;
432           infof(data, "Authentication problem. Ignoring this.\n");
433         }
434         else if(data->state.authwant & CURLAUTH_BASIC) {
435           data->state.authavail |= CURLAUTH_BASIC;
436         }
437       }
438   return CURLE_OK;
439 }
440
441
442 /* fread() emulation to provide POST and/or request data */
443 static size_t readmoredata(char *buffer,
444                            size_t size,
445                            size_t nitems,
446                            void *userp)
447 {
448   struct connectdata *conn = (struct connectdata *)userp;
449   struct HTTP *http = conn->proto.http;
450   size_t fullsize = size * nitems;
451
452   if(0 == http->postsize)
453     /* nothing to return */
454     return 0;
455   
456   /* make sure that a HTTP request is never sent away chunked! */
457   conn->bits.forbidchunk= (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
458
459   if(http->postsize <= (curl_off_t)fullsize) {
460     memcpy(buffer, http->postdata, (size_t)http->postsize);
461     fullsize = (size_t)http->postsize;
462
463     if(http->backup.postsize) {
464       /* move backup data into focus and continue on that */
465       http->postdata = http->backup.postdata;
466       http->postsize = http->backup.postsize;
467       conn->fread =    http->backup.fread;
468       conn->fread_in = http->backup.fread_in;
469
470       http->sending++; /* move one step up */
471
472       http->backup.postsize=0;
473     }
474     else
475       http->postsize = 0;
476
477     return fullsize;
478   }
479
480   memcpy(buffer, http->postdata, fullsize);
481   http->postdata += fullsize;
482   http->postsize -= fullsize;
483
484   return fullsize;
485 }
486
487 /* ------------------------------------------------------------------------- */
488 /*
489  * The add_buffer series of functions are used to build one large memory chunk
490  * from repeated function invokes. Used so that the entire HTTP request can
491  * be sent in one go.
492  */
493
494 struct send_buffer {
495   char *buffer;
496   size_t size_max;
497   size_t size_used;
498 };
499 typedef struct send_buffer send_buffer;
500
501 static CURLcode
502  add_buffer(send_buffer *in, const void *inptr, size_t size);
503
504 /*
505  * add_buffer_init() returns a fine buffer struct
506  */
507 static
508 send_buffer *add_buffer_init(void)
509 {
510   send_buffer *blonk;
511   blonk=(send_buffer *)malloc(sizeof(send_buffer));
512   if(blonk) {
513     memset(blonk, 0, sizeof(send_buffer));
514     return blonk;
515   }
516   return NULL; /* failed, go home */
517 }
518
519 /*
520  * add_buffer_send() sends a buffer and frees all associated memory.
521  */
522 static
523 CURLcode add_buffer_send(send_buffer *in,
524                          struct connectdata *conn,
525                          long *bytes_written) /* add the number of sent
526                                                  bytes to this counter */
527 {
528   ssize_t amount;
529   CURLcode res;
530   char *ptr;
531   size_t size;
532   struct HTTP *http = conn->proto.http;
533   size_t sendsize;
534   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
535
536   /* The looping below is required since we use non-blocking sockets, but due
537      to the circumstances we will just loop and try again and again etc */
538
539   ptr = in->buffer;
540   size = in->size_used;
541
542   if(conn->protocol & PROT_HTTPS) {
543     /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
544        when we speak HTTPS, as if only a fraction of it is sent now, this data
545        needs to fit into the normal read-callback buffer later on and that
546        buffer is using this size.
547     */
548
549     sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
550
551     /* OpenSSL is very picky and we must send the SAME buffer pointer to the
552        library when we attempt to re-send this buffer. Sending the same data
553        is not enough, we must use the exact same address. For this reason, we
554        must copy the data to the uploadbuffer first, since that is the buffer
555        we will be using if this send is retried later.
556     */
557     memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
558     ptr = conn->data->state.uploadbuffer;
559   }
560   else
561     sendsize = size;
562   
563   res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
564
565   if(CURLE_OK == res) {
566
567     if(conn->data->set.verbose)
568       /* this data _may_ contain binary stuff */
569       Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount);
570
571     *bytes_written += amount;
572     
573     if((size_t)amount != size) {
574       /* The whole request could not be sent in one system call. We must queue
575          it up and send it later when we get the chance. We must not loop here
576          and wait until it might work again. */
577
578       size -= amount;
579
580       ptr = in->buffer + amount;
581     
582       /* backup the currently set pointers */
583       http->backup.fread = conn->fread;
584       http->backup.fread_in = conn->fread_in;
585       http->backup.postdata = http->postdata;
586       http->backup.postsize = http->postsize;
587
588       /* set the new pointers for the request-sending */
589       conn->fread = (curl_read_callback)readmoredata;
590       conn->fread_in = (void *)conn;
591       http->postdata = ptr;
592       http->postsize = size;
593
594       http->send_buffer = in;
595       http->sending = HTTPSEND_REQUEST;
596       
597       return CURLE_OK;
598     }
599     http->sending = HTTPSEND_BODY;
600     /* the full buffer was sent, clean up and return */
601   }
602   if(in->buffer)
603     free(in->buffer);
604   free(in);
605
606   return res;
607 }
608
609
610 /* 
611  * add_bufferf() builds a buffer from the formatted input
612  */
613 static
614 CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
615 {
616   char *s;
617   va_list ap;
618   va_start(ap, fmt);
619   s = vaprintf(fmt, ap); /* this allocs a new string to append */
620   va_end(ap);
621
622   if(s) {
623     CURLcode result = add_buffer(in, s, strlen(s));
624     free(s);
625     if(CURLE_OK == result)
626       return CURLE_OK;
627   }
628   /* If we failed, we cleanup the whole buffer and return error */
629   if(in->buffer)
630     free(in->buffer);
631   free(in);
632   return CURLE_OUT_OF_MEMORY;
633 }
634
635 /*
636  * add_buffer() appends a memory chunk to the existing one
637  */
638 static
639 CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
640 {
641   char *new_rb;
642   size_t new_size;
643
644   if(!in->buffer ||
645      ((in->size_used + size) > (in->size_max - 1))) {
646     new_size = (in->size_used+size)*2;
647     if(in->buffer)
648       /* we have a buffer, enlarge the existing one */
649       new_rb = (char *)realloc(in->buffer, new_size);
650     else
651       /* create a new buffer */
652       new_rb = (char *)malloc(new_size);
653
654     if(!new_rb)
655       return CURLE_OUT_OF_MEMORY;
656
657     in->buffer = new_rb;
658     in->size_max = new_size;
659   }
660   memcpy(&in->buffer[in->size_used], inptr, size);
661       
662   in->size_used += size;
663
664   return CURLE_OK;
665 }
666
667 /* end of the add_buffer functions */
668 /* ------------------------------------------------------------------------- */
669
670 /*
671  * Curl_compareheader()
672  *
673  * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
674  * Pass headers WITH the colon.
675  */
676 bool
677 Curl_compareheader(char *headerline,    /* line to check */
678                    const char *header,  /* header keyword _with_ colon */
679                    const char *content) /* content string to find */
680 {
681   /* RFC2616, section 4.2 says: "Each header field consists of a name followed
682    * by a colon (":") and the field value. Field names are case-insensitive.
683    * The field value MAY be preceded by any amount of LWS, though a single SP
684    * is preferred." */
685
686   size_t hlen = strlen(header);
687   size_t clen;
688   size_t len;
689   char *start;
690   char *end;
691
692   if(!strnequal(headerline, header, hlen))
693     return FALSE; /* doesn't start with header */
694
695   /* pass the header */
696   start = &headerline[hlen];
697
698   /* pass all white spaces */
699   while(*start && isspace((int)*start))
700     start++;
701
702   /* find the end of the header line */
703   end = strchr(start, '\r'); /* lines end with CRLF */
704   if(!end) {
705     /* in case there's a non-standard compliant line here */
706     end = strchr(start, '\n');
707
708     if(!end)
709       /* hm, there's no line ending here, use the zero byte! */
710       end = strchr(start, '\0');
711   }
712
713   len = end-start; /* length of the content part of the input line */
714   clen = strlen(content); /* length of the word to find */
715
716   /* find the content string in the rest of the line */
717   for(;len>=clen;len--, start++) {
718     if(strnequal(start, content, clen))
719       return TRUE; /* match! */
720   }
721
722   return FALSE; /* no match */
723 }
724
725 /*
726  * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This
727  * function will issue the necessary commands to get a seamless tunnel through
728  * this proxy. After that, the socket can be used just as a normal socket.
729  */
730
731 CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
732                                      int sockindex,
733                                      char *hostname,
734                                      int remote_port)
735 {
736   int httpcode=0;
737   int subversion=0;
738   struct SessionHandle *data=conn->data;
739   CURLcode result;
740   int res;
741
742   size_t nread;   /* total size read */
743   int perline; /* count bytes per line */
744   bool keepon=TRUE;
745   ssize_t gotbytes;
746   char *ptr;
747   long timeout = 3600; /* default timeout in seconds */
748   struct timeval interval;
749   fd_set rkeepfd;
750   fd_set readfd;
751   char *line_start;
752   char *host_port;
753   curl_socket_t tunnelsocket = conn->sock[sockindex];
754
755 #define SELECT_OK      0
756 #define SELECT_ERROR   1
757 #define SELECT_TIMEOUT 2
758   int error = SELECT_OK;
759
760   infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
761
762   do {
763     bool auth; /* we don't really have to know when the auth phase is done,
764                   but this variable will be set to true then */
765
766     if(conn->newurl) {
767       /* This only happens if we've looped here due to authentication reasons,
768          and we don't really use the newly cloned URL here then. Just free()
769          it. */
770       free(conn->newurl); 
771       conn->newurl = NULL;
772     }
773
774     host_port = aprintf("%s:%d", hostname, remote_port);
775     if(!host_port)
776       return CURLE_OUT_OF_MEMORY;
777
778     /* Setup the proxy-authorization header, if any */
779     result = http_auth_headers(conn, (char *)"CONNECT", host_port, &auth);
780     if(CURLE_OK == result) {
781
782       /* OK, now send the connect request to the proxy */
783       result =
784         Curl_sendf(tunnelsocket, conn,
785                    "CONNECT %s:%d HTTP/1.0\015\012"
786                    "%s"
787                    "%s"
788                    "\r\n",
789                    hostname, remote_port,
790                    conn->bits.proxy_user_passwd?
791                    conn->allocptr.proxyuserpwd:"",
792                    data->set.useragent?conn->allocptr.uagent:""
793                    );
794       if(result)
795         failf(data, "Failed sending CONNECT to proxy");
796     }
797     free(host_port);
798     if(result)
799       return result;
800
801     FD_ZERO (&readfd);          /* clear it */
802     FD_SET (tunnelsocket, &readfd);     /* read socket */
803
804     /* get this in a backup variable to be able to restore it on each lap in
805        the select() loop */
806     rkeepfd = readfd;
807
808     ptr=data->state.buffer;
809     line_start = ptr;
810
811     nread=0;
812     perline=0;
813     keepon=TRUE;
814
815     while((nread<BUFSIZE) && (keepon && !error)) {
816       readfd = rkeepfd;     /* set every lap */
817       interval.tv_sec = 1;  /* timeout each second and check the timeout */
818       interval.tv_usec = 0;
819
820       if(data->set.timeout) {
821         /* if timeout is requested, find out how much remaining time we have */
822         timeout = data->set.timeout - /* timeout time */
823           Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
824         if(timeout <=0 ) {
825           failf(data, "Proxy connection aborted due to timeout");
826           error = SELECT_TIMEOUT; /* already too little time */
827           break;
828         }
829       }
830       
831       switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) {
832       case -1: /* select() error, stop reading */
833         error = SELECT_ERROR;
834         failf(data, "Proxy CONNECT aborted due to select() error");
835         break;
836       case 0: /* timeout */
837         break;
838       default:
839         /*
840          * This code previously didn't use the kerberos sec_read() code
841          * to read, but when we use Curl_read() it may do so. Do confirm
842          * that this is still ok and then remove this comment!
843          */
844         res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes);
845         if(res< 0)
846           /* EWOULDBLOCK */
847           continue; /* go loop yourself */
848         else if(res)
849           keepon = FALSE;
850         else if(gotbytes <= 0) {
851           keepon = FALSE;
852           error = SELECT_ERROR;
853           failf(data, "Proxy CONNECT aborted");
854         }
855         else {
856           /*
857            * We got a whole chunk of data, which can be anything from one byte
858            * to a set of lines and possibly just a piece of the last line.
859            *
860            * TODO: To make this code work less error-prone, we need to make
861            * sure that we read and create full lines before we compare them,
862            * as there is really nothing that stops the proxy from delivering
863            * the response lines in multiple parts, each part consisting of
864            * only a little piece of the line(s).  */
865           int i;
866
867           nread += gotbytes;
868           for(i = 0; i < gotbytes; ptr++, i++) {
869             perline++; /* amount of bytes in this line so far */
870             if(*ptr=='\n') {
871               char letter;
872               int writetype;
873             
874               /* output debug output if that is requested */
875               if(data->set.verbose)
876                 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
877
878               /* send the header to the callback */
879               writetype = CLIENTWRITE_HEADER;
880               if(data->set.http_include_header)
881                 writetype |= CLIENTWRITE_BODY;
882
883               result = Curl_client_write(data, writetype, line_start, perline);
884               if(result)
885                 return result;
886
887               /* Newlines are CRLF, so the CR is ignored as the line isn't
888                  really terminated until the LF comes. Treat a following CR
889                  as end-of-headers as well.*/
890
891               if(('\r' == line_start[0]) ||
892                  ('\n' == line_start[0])) {
893                 /* end of response-headers from the proxy */
894                 keepon=FALSE;
895                 break; /* breaks out of for-loop, not switch() */
896               }
897
898               /* keep a backup of the position we are about to blank */
899               letter = line_start[perline];
900               line_start[perline]=0; /* zero terminate the buffer */
901               if((checkprefix("WWW-Authenticate:", line_start) &&
902                   (401 == httpcode)) ||
903                  (checkprefix("Proxy-authenticate:", line_start) &&
904                   (407 == httpcode))) {
905                 result = Curl_http_auth(conn, httpcode, line_start);
906                 if(result)
907                   return result;
908               }
909               else if(2 == sscanf(line_start, "HTTP/1.%d %d",
910                                   &subversion,
911                                   &httpcode)) {
912                 /* store the HTTP code */
913                 data->info.httpproxycode = httpcode;                
914               }
915               /* put back the letter we blanked out before */
916               line_start[perline]= letter;
917
918               perline=0; /* line starts over here */
919               line_start = ptr+1; /* this skips the zero byte we wrote */
920             }
921           }
922         }
923         break;
924       } /* switch */
925     } /* while there's buffer left and loop is requested */
926
927     if(error)
928       return CURLE_RECV_ERROR;
929
930     /* Deal with the possibly already received authenticate headers. 'newurl'
931        is set to a new URL if we must loop. */
932     Curl_http_auth_act(conn);
933   
934   } while(conn->newurl);
935
936   if(200 != httpcode) {
937     failf(data, "Received HTTP code %d from proxy after CONNECT", httpcode);
938     return CURLE_RECV_ERROR;
939   }
940   
941   /* If a proxy-authorization header was used for the proxy, then we should
942      make sure that it isn't accidentally used for the document request
943      after we've connected. So let's free and clear it here. */
944   Curl_safefree(conn->allocptr.proxyuserpwd);
945   conn->allocptr.proxyuserpwd = NULL;
946
947   Curl_http_auth_stage(data, 401); /* move on to the host auth */
948
949   infof (data, "Proxy replied OK to CONNECT request\n");
950   return CURLE_OK;
951 }
952
953 /*
954  * HTTP stuff to do at connect-time.
955  */
956 CURLcode Curl_http_connect(struct connectdata *conn)
957 {
958   struct SessionHandle *data;
959   CURLcode result;
960
961   data=conn->data;
962
963   /* If we are not using a proxy and we want a secure connection,
964    * perform SSL initialization & connection now.
965    * If using a proxy with https, then we must tell the proxy to CONNECT
966    * us to the host we want to talk to.  Only after the connect
967    * has occured, can we start talking SSL
968    */
969
970   if(conn->bits.httpproxy &&
971      ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {
972
973     /* either HTTPS over proxy, OR explicitly asked for a tunnel */
974     result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
975                                          conn->hostname, conn->remote_port);
976     if(CURLE_OK != result)
977       return result;
978   }    
979
980   if(conn->protocol & PROT_HTTPS) {
981     /* now, perform the SSL initialization for this socket */
982     result = Curl_SSLConnect(conn, FIRSTSOCKET);
983     if(result)
984       return result;
985   }
986
987   if(conn->bits.user_passwd && !data->state.this_is_a_follow) {
988     /* Authorization: is requested, this is not a followed location, get the
989        original host name */
990     if (data->state.auth_host)
991       /* Free to avoid leaking memory on multiple requests*/
992       free(data->state.auth_host);
993
994     data->state.auth_host = strdup(conn->hostname);
995   }
996
997   return CURLE_OK;
998 }
999
1000 CURLcode Curl_http_done(struct connectdata *conn)
1001 {
1002   struct SessionHandle *data;
1003   struct HTTP *http;
1004
1005   data=conn->data;
1006   http=conn->proto.http;
1007
1008   /* set the proper values (possibly modified on POST) */
1009   conn->fread = data->set.fread; /* restore */
1010   conn->fread_in = data->set.in; /* restore */
1011
1012   if (http == NULL) 
1013     return CURLE_OK;
1014
1015   if(http->send_buffer) {
1016     send_buffer *buff = http->send_buffer;
1017     
1018     free(buff->buffer);
1019     free(buff);
1020     http->send_buffer = NULL; /* cleaer the pointer */
1021   }
1022
1023   if(HTTPREQ_POST_FORM == data->set.httpreq) {
1024     conn->bytecount = http->readbytecount + http->writebytecount;
1025       
1026     Curl_formclean(http->sendit); /* Now free that whole lot */
1027   }
1028   else if(HTTPREQ_PUT == data->set.httpreq)
1029     conn->bytecount = http->readbytecount + http->writebytecount;
1030
1031   if(!conn->bits.retry &&
1032      !(http->readbytecount + conn->headerbytecount)) {
1033     /* If this connection isn't simply closed to be retried, AND nothing was
1034        read from the HTTP server, this can't be right so we return an error
1035        here */
1036     failf(data, "Empty reply from server");
1037     return CURLE_GOT_NOTHING;
1038   }
1039
1040   return CURLE_OK;
1041 }
1042
1043 void Curl_http_auth_stage(struct SessionHandle *data,
1044                           int stage)
1045 {
1046   curlassert((stage == 401) || (stage == 407));
1047
1048   /* We set none, one or more bits for which authentication types we accept
1049      for this stage. */
1050   data->state.authwant = (stage == 401)?
1051     data->set.httpauth:data->set.proxyauth;
1052
1053   data->state.authstage = stage;
1054   data->state.authavail = CURLAUTH_NONE; /* no type available yet */
1055 }
1056
1057 CURLcode Curl_http(struct connectdata *conn)
1058 {
1059   struct SessionHandle *data=conn->data;
1060   char *buf = data->state.buffer; /* this is a short cut to the buffer */
1061   CURLcode result=CURLE_OK;
1062   struct HTTP *http;
1063   struct Cookie *co=NULL; /* no cookies from start */
1064   char *ppath = conn->ppath; /* three previous function arguments */
1065   char *host = conn->name;
1066   const char *te = ""; /* tranfer-encoding */
1067   char *ptr;
1068   char *request;
1069   bool authdone=TRUE; /* if the authentication phase is done */
1070
1071   if(!conn->proto.http) {
1072     /* Only allocate this struct if we don't already have it! */
1073
1074     http = (struct HTTP *)malloc(sizeof(struct HTTP));
1075     if(!http)
1076       return CURLE_OUT_OF_MEMORY;
1077     memset(http, 0, sizeof(struct HTTP));
1078     conn->proto.http = http;
1079   }
1080   else
1081     http = conn->proto.http;
1082
1083   /* We default to persistant connections */
1084   conn->bits.close = FALSE;
1085
1086   if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
1087        data->set.upload) {
1088     data->set.httpreq = HTTPREQ_PUT;
1089   }
1090
1091   request = data->set.customrequest?
1092     data->set.customrequest:
1093     (data->set.no_body?(char *)"HEAD":
1094      ((HTTPREQ_POST == data->set.httpreq) ||
1095       (HTTPREQ_POST_FORM == data->set.httpreq))?(char *)"POST":
1096      (HTTPREQ_PUT == data->set.httpreq)?(char *)"PUT":(char *)"GET");
1097   
1098   /* The User-Agent string has been built in url.c already, because it might
1099      have been used in the proxy connect, but if we have got a header with
1100      the user-agent string specified, we erase the previously made string
1101      here. */
1102   if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
1103     free(conn->allocptr.uagent);
1104     conn->allocptr.uagent=NULL;
1105   }
1106
1107   /* setup the authentication headers */
1108   result = http_auth_headers(conn, request, ppath, &authdone);
1109   if(result)
1110     return result;
1111
1112   Curl_safefree(conn->allocptr.ref);
1113   if(data->change.referer && !checkheaders(data, "Referer:"))
1114     conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer);
1115   else
1116     conn->allocptr.ref = NULL;
1117
1118   Curl_safefree(conn->allocptr.cookie);
1119   if(data->set.cookie && !checkheaders(data, "Cookie:"))
1120     conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie);
1121   else
1122     conn->allocptr.cookie = NULL;
1123
1124   if(!conn->bits.upload_chunky && (data->set.httpreq != HTTPREQ_GET)) {
1125     /* not a chunky transfer yet, but data is to be sent */
1126     ptr = checkheaders(data, "Transfer-Encoding:");
1127     if(ptr) {
1128       /* Some kind of TE is requested, check if 'chunked' is chosen */
1129       conn->bits.upload_chunky =
1130         Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
1131       te = "";
1132     }
1133   }
1134   else if(conn->bits.upload_chunky) {
1135     /* RFC2616 section 4.4:
1136        Messages MUST NOT include both a Content-Length header field and a
1137        non-identity transfer-coding. If the message does include a non-
1138        identity transfer-coding, the Content-Length MUST be ignored. */
1139
1140     if(!checkheaders(data, "Transfer-Encoding:")) {
1141       te = "Transfer-Encoding: chunked\r\n";
1142     }
1143     else {
1144       te = "";
1145       conn->bits.upload_chunky = FALSE; /* transfer-encoding was disabled,
1146                                            so don't chunkify this! */
1147     }
1148   }
1149
1150   ptr = checkheaders(data, "Host:");
1151   if(ptr && !data->state.this_is_a_follow) {
1152     /* If we have a given custom Host: header, we extract the host name in
1153        order to possibly use it for cookie reasons later on. We only allow the
1154        custom Host: header if this is NOT a redirect, as setting Host: in the
1155        redirected request is being out on thin ice. */
1156     char *start = ptr+strlen("Host:");
1157     while(*start && isspace((int)*start ))
1158       start++;
1159     ptr = start; /* start host-scanning here */
1160
1161     /* scan through the string to find the end (space or colon) */
1162     while(*ptr && !isspace((int)*ptr) && !(':'==*ptr))
1163       ptr++;
1164     
1165     if(ptr != start) {
1166       size_t len=ptr-start;
1167       conn->allocptr.cookiehost = malloc(len+1);
1168       if(!conn->allocptr.cookiehost)
1169         return CURLE_OUT_OF_MEMORY;
1170       memcpy(conn->allocptr.cookiehost, start, len);
1171       conn->allocptr.cookiehost[len]=0;
1172     }
1173   }    
1174   else {
1175     Curl_safefree(conn->allocptr.host);
1176
1177     /* When building Host: headers, we must put the host name within
1178        [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
1179        
1180     if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
1181        (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
1182       /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
1183          the port number in the host string */
1184       conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
1185                                     conn->bits.ipv6_ip?"[":"",
1186                                     host,
1187                                     conn->bits.ipv6_ip?"]":"");
1188     else
1189       conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
1190                                     conn->bits.ipv6_ip?"[":"",
1191                                     host,
1192                                     conn->bits.ipv6_ip?"]":"",
1193                                     conn->remote_port);
1194
1195     if(!conn->allocptr.host)
1196       /* without Host: we can't make a nice request */
1197       return CURLE_OUT_OF_MEMORY;
1198   }
1199
1200   if(data->cookies) {
1201     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1202     co = Curl_cookie_getlist(data->cookies,
1203                              conn->allocptr.cookiehost?
1204                              conn->allocptr.cookiehost:host, ppath,
1205                              (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
1206     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1207   }
1208
1209   if (conn->bits.httpproxy &&
1210       !data->set.tunnel_thru_httpproxy &&
1211       !(conn->protocol&PROT_HTTPS))  {
1212     /* The path sent to the proxy is in fact the entire URL */
1213     ppath = data->change.url;
1214   }
1215   if(HTTPREQ_POST_FORM == data->set.httpreq) {
1216     /* we must build the whole darned post sequence first, so that we have
1217        a size of the whole shebang before we start to send it */
1218      result = Curl_getFormData(&http->sendit, data->set.httppost,
1219                                &http->postsize);
1220      if(CURLE_OK != result) {
1221        /* Curl_getFormData() doesn't use failf() */
1222        failf(data, "failed creating formpost data");
1223        return result;
1224      }
1225   }
1226
1227
1228   if(!checkheaders(data, "Pragma:"))
1229     http->p_pragma = "Pragma: no-cache\r\n";
1230
1231   if(!checkheaders(data, "Accept:"))
1232     http->p_accept = "Accept: */*\r\n";
1233
1234   if(( (HTTPREQ_POST == data->set.httpreq) ||
1235        (HTTPREQ_POST_FORM == data->set.httpreq) ||
1236        (HTTPREQ_PUT == data->set.httpreq) ) &&
1237      conn->resume_from) {
1238     /**********************************************************************
1239      * Resuming upload in HTTP means that we PUT or POST and that we have
1240      * got a resume_from value set. The resume value has already created
1241      * a Range: header that will be passed along. We need to "fast forward"
1242      * the file the given number of bytes and decrease the assume upload
1243      * file size before we continue this venture in the dark lands of HTTP.
1244      *********************************************************************/
1245    
1246     if(conn->resume_from < 0 ) {
1247       /*
1248        * This is meant to get the size of the present remote-file by itself.
1249        * We don't support this now. Bail out!
1250        */
1251        conn->resume_from = 0;
1252     }
1253
1254     if(conn->resume_from) {
1255       /* do we still game? */
1256       curl_off_t passed=0;
1257
1258       /* Now, let's read off the proper amount of bytes from the
1259          input. If we knew it was a proper file we could've just
1260          fseek()ed but we only have a stream here */
1261       do {
1262         size_t readthisamountnow = (size_t)(conn->resume_from - passed);
1263         size_t actuallyread;
1264
1265         if(readthisamountnow > BUFSIZE)
1266           readthisamountnow = BUFSIZE;
1267
1268         actuallyread =
1269           data->set.fread(data->state.buffer, 1, (size_t)readthisamountnow,
1270                           data->set.in);
1271
1272         passed += actuallyread;
1273         if(actuallyread != readthisamountnow) {
1274           failf(data, "Could only read %" FORMAT_OFF_T
1275                 " bytes from the input",
1276                 passed);
1277           return CURLE_READ_ERROR;
1278         }
1279       } while(passed != conn->resume_from); /* loop until done */
1280
1281       /* now, decrease the size of the read */
1282       if(data->set.infilesize>0) {
1283         data->set.infilesize -= conn->resume_from;
1284
1285         if(data->set.infilesize <= 0) {
1286           failf(data, "File already completely uploaded");
1287           return CURLE_PARTIAL_FILE;
1288         }
1289       }
1290       /* we've passed, proceed as normal */
1291     }
1292   }
1293   if(conn->bits.use_range) {
1294     /*
1295      * A range is selected. We use different headers whether we're downloading
1296      * or uploading and we always let customized headers override our internal
1297      * ones if any such are specified.
1298      */
1299     if((data->set.httpreq == HTTPREQ_GET) &&
1300        !checkheaders(data, "Range:")) {
1301       /* if a line like this was already allocated, free the previous one */
1302       if(conn->allocptr.rangeline)
1303         free(conn->allocptr.rangeline);
1304       conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->range);
1305     }
1306     else if((data->set.httpreq != HTTPREQ_GET) &&
1307             !checkheaders(data, "Content-Range:")) {
1308
1309       if(conn->resume_from) {
1310         /* This is because "resume" was selected */
1311         curl_off_t total_expected_size=
1312           conn->resume_from + data->set.infilesize;
1313         conn->allocptr.rangeline =
1314             aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
1315                     "/%" FORMAT_OFF_T "\r\n",
1316                     conn->range, total_expected_size-1,
1317                     total_expected_size);
1318       }
1319       else {
1320         /* Range was selected and then we just pass the incoming range and 
1321            append total size */
1322         conn->allocptr.rangeline =
1323             aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
1324                     conn->range, data->set.infilesize);
1325       }
1326     }
1327   }
1328
1329   {
1330     /* Use 1.1 unless the use specificly asked for 1.0 */
1331     const char *httpstring=
1332       data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
1333
1334     send_buffer *req_buffer;
1335     struct curl_slist *headers=data->set.headers;
1336     curl_off_t postsize; /* off_t type to be able to hold a large file size */
1337
1338     /* initialize a dynamic send-buffer */
1339     req_buffer = add_buffer_init();
1340
1341     if(!req_buffer)
1342       return CURLE_OUT_OF_MEMORY;
1343
1344     /* add the main request stuff */
1345     result =
1346       add_bufferf(req_buffer,
1347                   "%s " /* GET/HEAD/POST/PUT */
1348                   "%s HTTP/%s\r\n" /* path + HTTP version */
1349                   "%s" /* proxyuserpwd */
1350                   "%s" /* userpwd */
1351                   "%s" /* range */
1352                   "%s" /* user agent */
1353                   "%s" /* cookie */
1354                   "%s" /* host */
1355                   "%s" /* pragma */
1356                   "%s" /* accept */
1357                   "%s" /* accept-encoding */
1358                   "%s" /* referer */
1359                   "%s",/* transfer-encoding */
1360
1361                 request,
1362                 ppath,
1363                 httpstring,
1364                 (conn->bits.httpproxy && conn->allocptr.proxyuserpwd)?
1365                 conn->allocptr.proxyuserpwd:"",
1366                 conn->allocptr.userpwd?conn->allocptr.userpwd:"",
1367                 (conn->bits.use_range && conn->allocptr.rangeline)?
1368                 conn->allocptr.rangeline:"",
1369                 (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
1370                 conn->allocptr.uagent:"",
1371                 (conn->allocptr.cookie?conn->allocptr.cookie:""), /* Cookie: <data> */
1372                 (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
1373                 http->p_pragma?http->p_pragma:"",
1374                 http->p_accept?http->p_accept:"",
1375                 (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
1376                 conn->allocptr.accept_encoding:"", /* 08/28/02 jhrg */
1377                 (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> <CRLF> */,
1378                 te
1379                 );
1380
1381     if(result)
1382       return result;
1383
1384     if(co) {
1385       int count=0;
1386       struct Cookie *store=co;
1387       /* now loop through all cookies that matched */
1388       while(co) {
1389         if(co->value) {
1390           if(0 == count) {
1391             add_bufferf(req_buffer, "Cookie: ");
1392           }
1393           add_bufferf(req_buffer,
1394                       "%s%s=%s", count?"; ":"", co->name, co->value);
1395           count++;
1396         }
1397         co = co->next; /* next cookie please */
1398       }
1399       if(count) {
1400         add_buffer(req_buffer, "\r\n", 2);
1401       }
1402       Curl_cookie_freelist(store); /* free the cookie list */
1403       co=NULL;
1404     }
1405
1406     if(data->set.timecondition) {
1407       struct tm *thistime;
1408
1409       /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since
1410        * header family should have their times set in GMT as RFC2616 defines:
1411        * "All HTTP date/time stamps MUST be represented in Greenwich Mean Time
1412        * (GMT), without exception. For the purposes of HTTP, GMT is exactly
1413        * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616).
1414        */
1415
1416 #ifdef HAVE_GMTIME_R
1417       /* thread-safe version */
1418       struct tm keeptime;
1419       thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
1420 #else
1421       thistime = gmtime(&data->set.timevalue);
1422 #endif
1423
1424 #ifdef HAVE_STRFTIME
1425       /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
1426       strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime);
1427 #else
1428       /* TODO: Right, we *could* write a replacement here */
1429       strcpy(buf, "no strftime() support");
1430 #endif
1431       switch(data->set.timecondition) {
1432       case CURL_TIMECOND_IFMODSINCE:
1433       default:
1434         add_bufferf(req_buffer,
1435                     "If-Modified-Since: %s\r\n", buf);
1436         break;
1437       case CURL_TIMECOND_IFUNMODSINCE:
1438         add_bufferf(req_buffer,
1439                     "If-Unmodified-Since: %s\r\n", buf);
1440         break;
1441       case CURL_TIMECOND_LASTMOD:
1442         add_bufferf(req_buffer,
1443                     "Last-Modified: %s\r\n", buf);
1444         break;
1445       }
1446     }
1447
1448     while(headers) {
1449       ptr = strchr(headers->data, ':');
1450       if(ptr) {
1451         /* we require a colon for this to be a true header */
1452
1453         ptr++; /* pass the colon */
1454         while(*ptr && isspace((int)*ptr))
1455           ptr++;
1456
1457         if(*ptr) {
1458           /* only send this if the contents was non-blank */
1459
1460           add_bufferf(req_buffer, "%s\r\n", headers->data);
1461         }
1462       }
1463       headers = headers->next;
1464     }
1465
1466     http->postdata = NULL;  /* nothing to post at this point */
1467     Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
1468
1469     /* If 'authdone' is still FALSE, we must not set the write socket index to
1470        the Curl_transfer() call below, as we're not ready to actually upload
1471        any data yet. */
1472
1473     switch(data->set.httpreq) {
1474
1475     case HTTPREQ_POST_FORM:
1476       if(Curl_FormInit(&http->form, http->sendit)) {
1477         failf(data, "Internal HTTP POST error!");
1478         return CURLE_HTTP_POST_ERROR;
1479       }
1480
1481       /* set the read function to read from the generated form data */
1482       conn->fread = (curl_read_callback)Curl_FormReader;
1483       conn->fread_in = &http->form;
1484
1485       http->sending = HTTPSEND_BODY;
1486
1487       if(!conn->bits.upload_chunky)
1488         /* only add Content-Length if not uploading chunked */
1489         add_bufferf(req_buffer,
1490                     "Content-Length: %" FORMAT_OFF_T "\r\n", http->postsize);
1491
1492       if(!checkheaders(data, "Expect:")) {
1493         /* if not disabled explicitly we add a Expect: 100-continue
1494            to the headers which actually speeds up post operations (as
1495            there is one packet coming back from the web server) */
1496         add_bufferf(req_buffer,
1497                     "Expect: 100-continue\r\n");
1498         data->set.expect100header = TRUE;
1499       }
1500
1501       if(!checkheaders(data, "Content-Type:")) {
1502         /* Get Content-Type: line from Curl_FormReadOneLine, which happens
1503            to always be the first line. We can know this for sure since
1504            we always build the formpost linked list the same way!
1505
1506            The Content-Type header line also contains the MIME boundary
1507            string etc why disabling this header is likely to not make things
1508            work, but we support it anyway.
1509         */
1510         char contentType[256];
1511         size_t linelength=0;
1512         linelength = Curl_FormReadOneLine(contentType,
1513                                           sizeof(contentType),
1514                                           1,
1515                                           (FILE *)&http->form);
1516         if(!linelength) {
1517           failf(data, "Could not get Content-Type header line!");
1518           return CURLE_HTTP_POST_ERROR;
1519         }
1520         add_buffer(req_buffer, contentType, linelength);
1521       }
1522
1523       /* make the request end in a true CRLF */
1524       add_buffer(req_buffer, "\r\n", 2);
1525
1526       /* set upload size to the progress meter */
1527       Curl_pgrsSetUploadSize(data, http->postsize);
1528
1529       /* fire away the whole request to the server */
1530       result = add_buffer_send(req_buffer, conn, 
1531                                &data->info.request_size);
1532       if(result)
1533         failf(data, "Failed sending POST request");
1534       else
1535         /* setup variables for the upcoming transfer */
1536         result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
1537                                &http->readbytecount,
1538                                authdone?FIRSTSOCKET:-1,
1539                                authdone?&http->writebytecount:NULL);
1540       if(result) {
1541         Curl_formclean(http->sendit); /* free that whole lot */
1542         return result;
1543       }
1544       break;
1545
1546     case HTTPREQ_PUT: /* Let's PUT the data to the server! */
1547
1548       if((data->set.infilesize>0) && !conn->bits.upload_chunky)
1549         /* only add Content-Length if not uploading chunked */
1550         add_bufferf(req_buffer,
1551                     "Content-Length: %" FORMAT_OFF_T "\r\n", /* size */
1552                     data->set.infilesize );
1553
1554       if(!checkheaders(data, "Expect:")) {
1555         /* if not disabled explicitly we add a Expect: 100-continue
1556            to the headers which actually speeds up post operations (as
1557            there is one packet coming back from the web server) */
1558         add_bufferf(req_buffer,
1559                     "Expect: 100-continue\r\n");
1560         data->set.expect100header = TRUE;
1561       }
1562
1563       add_buffer(req_buffer, "\r\n", 2); /* end of headers */
1564
1565       /* set the upload size to the progress meter */
1566       Curl_pgrsSetUploadSize(data, data->set.infilesize);
1567
1568       /* this sends the buffer and frees all the buffer resources */
1569       result = add_buffer_send(req_buffer, conn,
1570                                &data->info.request_size);
1571       if(result)
1572         failf(data, "Failed sending POST request");
1573       else
1574         /* prepare for transfer */
1575         result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
1576                                &http->readbytecount,
1577                                authdone?FIRSTSOCKET:-1,
1578                                authdone?&http->writebytecount:NULL);
1579       if(result)
1580         return result;
1581       break;
1582
1583     case HTTPREQ_POST:
1584       /* this is the simple POST, using x-www-form-urlencoded style */
1585
1586       /* store the size of the postfields */
1587       postsize = data->set.postfieldsize?
1588         data->set.postfieldsize:
1589         (data->set.postfields?(curl_off_t)strlen(data->set.postfields):0);
1590       
1591       if(!conn->bits.upload_chunky) {
1592         /* We only set Content-Length and allow a custom Content-Length if
1593            we don't upload data chunked, as RFC2616 forbids us to set both
1594            kinds of headers (Transfer-Encoding: chunked and Content-Length) */
1595
1596         if(!checkheaders(data, "Content-Length:"))
1597           /* we allow replacing this header, although it isn't very wise to
1598              actually set your own */
1599           add_bufferf(req_buffer, "Content-Length: %" FORMAT_OFF_T"\r\n",
1600                       postsize);
1601       }
1602
1603       if(!checkheaders(data, "Content-Type:"))
1604         add_bufferf(req_buffer,
1605                     "Content-Type: application/x-www-form-urlencoded\r\n");
1606
1607       if(data->set.postfields) {
1608
1609         if(authdone && (postsize < (100*1024))) {
1610           /* If we're not done with the authentication phase, we don't expect
1611              to actually send off any data yet. Hence, we delay the sending of
1612              the body until we receive that friendly 100-continue response */
1613              
1614           /* The post data is less than 100K, then append it to the header.
1615              This limit is no magic limit but only set to prevent really huge
1616              POSTs to get the data duplicated with malloc() and family. */
1617
1618           add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
1619
1620           if(!conn->bits.upload_chunky)
1621             /* We're not sending it 'chunked', append it to the request
1622                already now to reduce the number if send() calls */
1623             add_buffer(req_buffer, data->set.postfields, (size_t)postsize);
1624           else {
1625             /* Append the POST data chunky-style */
1626             add_bufferf(req_buffer, "%x\r\n", (int)postsize);
1627             add_buffer(req_buffer, data->set.postfields, (size_t)postsize);
1628             add_buffer(req_buffer, "\r\n0\r\n\r\n", 7); /* end of a chunked
1629                                                            transfer stream */
1630           }
1631         }
1632         else {
1633           /* A huge POST coming up, do data separate from the request */
1634           http->postsize = postsize;
1635           http->postdata = data->set.postfields;
1636
1637           http->sending = HTTPSEND_BODY;
1638
1639           conn->fread = (curl_read_callback)readmoredata;
1640           conn->fread_in = (void *)conn;
1641
1642           /* set the upload size to the progress meter */
1643           Curl_pgrsSetUploadSize(data, http->postsize);
1644
1645           if(!authdone && !checkheaders(data, "Expect:")) {
1646             /* if not disabled explicitly we add a Expect: 100-continue to the
1647                headers which actually speeds up post operations (as there is
1648                one packet coming back from the web server) */
1649             add_bufferf(req_buffer,
1650                         "Expect: 100-continue\r\n");
1651             data->set.expect100header = TRUE;
1652           }
1653
1654           add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
1655         }
1656       }
1657       else {
1658         add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
1659
1660         /* set the upload size to the progress meter */
1661         Curl_pgrsSetUploadSize(data, data->set.infilesize);
1662
1663         /* set the pointer to mark that we will send the post body using
1664            the read callback */
1665         http->postdata = (char *)&http->postdata;
1666       }
1667       /* issue the request */
1668       result = add_buffer_send(req_buffer, conn,
1669                                &data->info.request_size);
1670
1671       if(result)
1672         failf(data, "Failed sending HTTP POST request");
1673       else
1674         result =
1675           Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
1676                         &http->readbytecount,
1677                         http->postdata?FIRSTSOCKET:-1,
1678                         http->postdata?&http->writebytecount:NULL);
1679       break;
1680
1681     default:
1682       add_buffer(req_buffer, "\r\n", 2);
1683       
1684       /* issue the request */
1685       result = add_buffer_send(req_buffer, conn,
1686                                &data->info.request_size);
1687
1688       if(result)
1689         failf(data, "Failed sending HTTP request");
1690       else
1691         /* HTTP GET/HEAD download: */
1692         result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
1693                                &http->readbytecount,
1694                                http->postdata?FIRSTSOCKET:-1,
1695                                http->postdata?&http->writebytecount:NULL);
1696     }
1697     if(result)
1698       return result;
1699   }
1700
1701   return CURLE_OK;
1702 }
1703 #endif