1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2009, 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: easy.c,v 1.133 2009-01-29 20:32:31 yangtse Exp $
22  ***************************************************************************/
23 
24 #include "setup.h"
25 
26 /* -- WIN32 approved -- */
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <errno.h>
33 
34 #include "strequal.h"
35 
36 #ifdef WIN32
37 #include <time.h>
38 #include <io.h>
39 #else
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
42 #endif
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
46 #ifdef HAVE_SYS_TIME_H
47 #include <sys/time.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef HAVE_NETDB_H
53 #include <netdb.h>
54 #endif
55 #ifdef HAVE_ARPA_INET_H
56 #include <arpa/inet.h>
57 #endif
58 #ifdef HAVE_NET_IF_H
59 #include <net/if.h>
60 #endif
61 #ifdef HAVE_SYS_IOCTL_H
62 #include <sys/ioctl.h>
63 #endif
64 
65 #ifdef HAVE_SYS_PARAM_H
66 #include <sys/param.h>
67 #endif
68 
69 #endif  /* WIN32 ... */
70 
71 #include "urldata.h"
72 #include <curl/curl.h>
73 #include "transfer.h"
74 #include "sslgen.h"
75 #include "url.h"
76 #include "getinfo.h"
77 #include "hostip.h"
78 #include "share.h"
79 #include "strdup.h"
80 #include "memory.h"
81 #include "progress.h"
82 #include "easyif.h"
83 #include "select.h"
84 #include "sendf.h" /* for failf function prototype */
85 #include "http_ntlm.h"
86 #include "connect.h" /* for Curl_getconnectinfo */
87 
88 #define _MPRINTF_REPLACE /* use our functions only */
89 #include <curl/mprintf.h>
90 
91 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
92 #include <iconv.h>
93 /* set default codesets for iconv */
94 #ifndef CURL_ICONV_CODESET_OF_NETWORK
95 #define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
96 #endif
97 #ifndef CURL_ICONV_CODESET_FOR_UTF8
98 #define CURL_ICONV_CODESET_FOR_UTF8   "UTF-8"
99 #endif
100 #define ICONV_ERROR  (size_t)-1
101 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
102 
103 /* The last #include file should be: */
104 #include "memdebug.h"
105 
106 /* win32_cleanup() is for win32 socket cleanup functionality, the opposite
107    of win32_init() */
win32_cleanup(void)108 static void win32_cleanup(void)
109 {
110 #ifdef USE_WINSOCK
111   WSACleanup();
112 #endif
113 #ifdef USE_WINDOWS_SSPI
114   Curl_sspi_global_cleanup();
115 #endif
116 }
117 
118 /* win32_init() performs win32 socket initialization to properly setup the
119    stack to allow networking */
win32_init(void)120 static CURLcode win32_init(void)
121 {
122 #ifdef USE_WINSOCK
123   WORD wVersionRequested;
124   WSADATA wsaData;
125   int res;
126 
127 #if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
128   Error IPV6_requires_winsock2
129 #endif
130 
131   wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
132 
133   res = WSAStartup(wVersionRequested, &wsaData);
134 
135   if(res != 0)
136     /* Tell the user that we couldn't find a useable */
137     /* winsock.dll.     */
138     return CURLE_FAILED_INIT;
139 
140   /* Confirm that the Windows Sockets DLL supports what we need.*/
141   /* Note that if the DLL supports versions greater */
142   /* than wVersionRequested, it will still return */
143   /* wVersionRequested in wVersion. wHighVersion contains the */
144   /* highest supported version. */
145 
146   if( LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) ||
147        HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) {
148     /* Tell the user that we couldn't find a useable */
149 
150     /* winsock.dll. */
151     WSACleanup();
152     return CURLE_FAILED_INIT;
153   }
154   /* The Windows Sockets DLL is acceptable. Proceed. */
155 #endif
156 
157 #ifdef USE_WINDOWS_SSPI
158   {
159     CURLcode err = Curl_sspi_global_init();
160     if (err != CURLE_OK)
161       return err;
162   }
163 #endif
164 
165   return CURLE_OK;
166 }
167 
168 #ifdef USE_LIBIDN
169 /*
170  * Initialise use of IDNA library.
171  * It falls back to ASCII if $CHARSET isn't defined. This doesn't work for
172  * idna_to_ascii_lz().
173  */
idna_init(void)174 static void idna_init (void)
175 {
176 #ifdef WIN32
177   char buf[60];
178   UINT cp = GetACP();
179 
180   if(!getenv("CHARSET") && cp > 0) {
181     snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
182     putenv(buf);
183   }
184 #else
185   /* to do? */
186 #endif
187 }
188 #endif  /* USE_LIBIDN */
189 
190 /* true globals -- for curl_global_init() and curl_global_cleanup() */
191 static unsigned int  initialized;
192 static long          init_flags;
193 
194 /*
195  * strdup (and other memory functions) is redefined in complicated
196  * ways, but at this point it must be defined as the system-supplied strdup
197  * so the callback pointer is initialized correctly.
198  */
199 #if defined(_WIN32_WCE)
200 #define system_strdup _strdup
201 #elif !defined(HAVE_STRDUP)
202 #define system_strdup curlx_strdup
203 #else
204 #define system_strdup strdup
205 #endif
206 
207 #if defined(_MSC_VER) && defined(_DLL)
208 #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
209 #endif
210 
211 #ifndef __SYMBIAN32__
212 /*
213  * If a memory-using function (like curl_getenv) is used before
214  * curl_global_init() is called, we need to have these pointers set already.
215  */
216 curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
217 curl_free_callback Curl_cfree = (curl_free_callback)free;
218 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
219 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
220 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
221 #else
222 /*
223  * Symbian OS doesn't support initialization to code in writeable static data.
224  * Initialization will occur in the curl_global_init() call.
225  */
226 curl_malloc_callback Curl_cmalloc;
227 curl_free_callback Curl_cfree;
228 curl_realloc_callback Curl_crealloc;
229 curl_strdup_callback Curl_cstrdup;
230 curl_calloc_callback Curl_ccalloc;
231 #endif
232 
233 #if defined(_MSC_VER) && defined(_DLL)
234 #  pragma warning(default:4232) /* MSVC extension, dllimport identity */
235 #endif
236 
237 /**
238  * curl_global_init() globally initializes cURL given a bitwise set of the
239  * different features of what to initialize.
240  */
curl_global_init(long flags)241 CURLcode curl_global_init(long flags)
242 {
243   if(initialized++)
244     return CURLE_OK;
245 
246   /* Setup the default memory functions here (again) */
247   Curl_cmalloc = (curl_malloc_callback)malloc;
248   Curl_cfree = (curl_free_callback)free;
249   Curl_crealloc = (curl_realloc_callback)realloc;
250   Curl_cstrdup = (curl_strdup_callback)system_strdup;
251   Curl_ccalloc = (curl_calloc_callback)calloc;
252 
253   if(flags & CURL_GLOBAL_SSL)
254     if(!Curl_ssl_init()) {
255       DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
256       return CURLE_FAILED_INIT;
257     }
258 
259   if(flags & CURL_GLOBAL_WIN32)
260     if(win32_init() != CURLE_OK) {
261       DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
262       return CURLE_FAILED_INIT;
263     }
264 
265 #ifdef __AMIGA__
266   if(!amiga_init()) {
267     DEBUGF(fprintf(stderr, "Error: amiga_init failed\n"));
268     return CURLE_FAILED_INIT;
269   }
270 #endif
271 
272 #ifdef NETWARE
273   if(netware_init()) {
274     DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n"));
275   }
276 #endif
277 
278 #ifdef USE_LIBIDN
279   idna_init();
280 #endif
281 
282   init_flags  = flags;
283 
284   return CURLE_OK;
285 }
286 
287 /*
288  * curl_global_init_mem() globally initializes cURL and also registers the
289  * user provided callback routines.
290  */
curl_global_init_mem(long flags,curl_malloc_callback m,curl_free_callback f,curl_realloc_callback r,curl_strdup_callback s,curl_calloc_callback c)291 CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
292                               curl_free_callback f, curl_realloc_callback r,
293                               curl_strdup_callback s, curl_calloc_callback c)
294 {
295   CURLcode code = CURLE_OK;
296 
297   /* Invalid input, return immediately */
298   if(!m || !f || !r || !s || !c)
299     return CURLE_FAILED_INIT;
300 
301   /* Already initialized, don't do it again */
302   if( initialized )
303     return CURLE_OK;
304 
305   /* Call the actual init function first */
306   code = curl_global_init(flags);
307   if(code == CURLE_OK) {
308     Curl_cmalloc = m;
309     Curl_cfree = f;
310     Curl_cstrdup = s;
311     Curl_crealloc = r;
312     Curl_ccalloc = c;
313   }
314 
315   return code;
316 }
317 
318 /**
319  * curl_global_cleanup() globally cleanups cURL, uses the value of
320  * "init_flags" to determine what needs to be cleaned up and what doesn't.
321  */
curl_global_cleanup(void)322 void curl_global_cleanup(void)
323 {
324   if(!initialized)
325     return;
326 
327   if(--initialized)
328     return;
329 
330   Curl_global_host_cache_dtor();
331 
332   if(init_flags & CURL_GLOBAL_SSL)
333     Curl_ssl_cleanup();
334 
335   if(init_flags & CURL_GLOBAL_WIN32)
336     win32_cleanup();
337 
338 #ifdef __AMIGA__
339   amiga_cleanup();
340 #endif
341 
342   init_flags  = 0;
343 }
344 
345 /*
346  * curl_easy_init() is the external interface to alloc, setup and init an
347  * easy handle that is returned. If anything goes wrong, NULL is returned.
348  */
curl_easy_init(void)349 CURL *curl_easy_init(void)
350 {
351   CURLcode res;
352   struct SessionHandle *data;
353 
354   /* Make sure we inited the global SSL stuff */
355   if(!initialized) {
356     res = curl_global_init(CURL_GLOBAL_DEFAULT);
357     if(res) {
358       /* something in the global init failed, return nothing */
359       DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
360       return NULL;
361     }
362   }
363 
364   /* We use curl_open() with undefined URL so far */
365   res = Curl_open(&data);
366   if(res != CURLE_OK) {
367     DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
368     return NULL;
369   }
370 
371   return data;
372 }
373 
374 /*
375  * curl_easy_setopt() is the external interface for setting options on an
376  * easy handle.
377  */
378 
379 #undef curl_easy_setopt
curl_easy_setopt(CURL * curl,CURLoption tag,...)380 CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
381 {
382   va_list arg;
383   struct SessionHandle *data = curl;
384   CURLcode ret;
385 
386   if(!curl)
387     return CURLE_BAD_FUNCTION_ARGUMENT;
388 
389   va_start(arg, tag);
390 
391   ret = Curl_setopt(data, tag, arg);
392 
393   va_end(arg);
394   return ret;
395 }
396 
397 #ifdef CURL_MULTIEASY
398 /***************************************************************************
399  * This function is still only for testing purposes. It makes a great way
400  * to run the full test suite on the multi interface instead of the easy one.
401  ***************************************************************************
402  *
403  * The *new* curl_easy_perform() is the external interface that performs a
404  * transfer previously setup.
405  *
406  * Wrapper-function that: creates a multi handle, adds the easy handle to it,
407  * runs curl_multi_perform() until the transfer is done, then detaches the
408  * easy handle, destroys the multi handle and returns the easy handle's return
409  * code. This will make everything internally use and assume multi interface.
410  */
curl_easy_perform(CURL * easy)411 CURLcode curl_easy_perform(CURL *easy)
412 {
413   CURLM *multi;
414   CURLMcode mcode;
415   CURLcode code = CURLE_OK;
416   int still_running;
417   struct timeval timeout;
418   int rc;
419   CURLMsg *msg;
420   fd_set fdread;
421   fd_set fdwrite;
422   fd_set fdexcep;
423   int maxfd;
424 
425   if(!easy)
426     return CURLE_BAD_FUNCTION_ARGUMENT;
427 
428   multi = curl_multi_init();
429   if(!multi)
430     return CURLE_OUT_OF_MEMORY;
431 
432   mcode = curl_multi_add_handle(multi, easy);
433   if(mcode) {
434     curl_multi_cleanup(multi);
435     if(mcode == CURLM_OUT_OF_MEMORY)
436       return CURLE_OUT_OF_MEMORY;
437     else
438       return CURLE_FAILED_INIT;
439   }
440 
441   /* we start some action by calling perform right away */
442 
443   do {
444     while(CURLM_CALL_MULTI_PERFORM ==
445           curl_multi_perform(multi, &still_running));
446 
447     if(!still_running)
448       break;
449 
450     FD_ZERO(&fdread);
451     FD_ZERO(&fdwrite);
452     FD_ZERO(&fdexcep);
453 
454     /* timeout once per second */
455     timeout.tv_sec = 1;
456     timeout.tv_usec = 0;
457 
458     /* Old deprecated style: get file descriptors from the transfers */
459     curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
460     rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
461 
462     /* The way is to extract the sockets and wait for them without using
463        select. This whole alternative version should probably rather use the
464        curl_multi_socket() approach. */
465 
466     if(rc == -1)
467       /* select error */
468       break;
469 
470     /* timeout or data to send/receive => loop! */
471   } while(still_running);
472 
473   msg = curl_multi_info_read(multi, &rc);
474   if(msg)
475     code = msg->data.result;
476 
477   mcode = curl_multi_remove_handle(multi, easy);
478   /* what to do if it fails? */
479 
480   mcode = curl_multi_cleanup(multi);
481   /* what to do if it fails? */
482 
483   return code;
484 }
485 #else
486 /*
487  * curl_easy_perform() is the external interface that performs a transfer
488  * previously setup.
489  */
curl_easy_perform(CURL * curl)490 CURLcode curl_easy_perform(CURL *curl)
491 {
492   struct SessionHandle *data = (struct SessionHandle *)curl;
493 
494   if(!data)
495     return CURLE_BAD_FUNCTION_ARGUMENT;
496 
497   if( ! (data->share && data->share->hostcache) ) {
498     /* this handle is not using a shared dns cache */
499 
500     if(data->set.global_dns_cache &&
501        (data->dns.hostcachetype != HCACHE_GLOBAL)) {
502       /* global dns cache was requested but still isn't */
503       struct curl_hash *ptr;
504 
505       if(data->dns.hostcachetype == HCACHE_PRIVATE) {
506         /* if the current cache is private, kill it first */
507         Curl_hash_destroy(data->dns.hostcache);
508         data->dns.hostcachetype = HCACHE_NONE;
509         data->dns.hostcache = NULL;
510       }
511 
512       ptr = Curl_global_host_cache_init();
513       if(ptr) {
514         /* only do this if the global cache init works */
515         data->dns.hostcache = ptr;
516         data->dns.hostcachetype = HCACHE_GLOBAL;
517       }
518     }
519 
520     if(!data->dns.hostcache) {
521       data->dns.hostcachetype = HCACHE_PRIVATE;
522       data->dns.hostcache = Curl_mk_dnscache();
523 
524       if(!data->dns.hostcache)
525         /* While we possibly could survive and do good without a host cache,
526            the fact that creating it failed indicates that things are truly
527            screwed up and we should bail out! */
528         return CURLE_OUT_OF_MEMORY;
529     }
530 
531   }
532 
533   if(!data->state.connc) {
534     /* oops, no connection cache, make one up */
535     data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1);
536     if(!data->state.connc)
537       return CURLE_OUT_OF_MEMORY;
538   }
539 
540   return Curl_perform(data);
541 }
542 #endif
543 
544 /*
545  * curl_easy_cleanup() is the external interface to cleaning/freeing the given
546  * easy handle.
547  */
curl_easy_cleanup(CURL * curl)548 void curl_easy_cleanup(CURL *curl)
549 {
550   struct SessionHandle *data = (struct SessionHandle *)curl;
551 
552   if(!data)
553     return;
554 
555   Curl_close(data);
556 }
557 
558 /*
559  * Store a pointed to the multi handle within the easy handle's data struct.
560  */
Curl_easy_addmulti(struct SessionHandle * data,void * multi)561 void Curl_easy_addmulti(struct SessionHandle *data,
562                         void *multi)
563 {
564   data->multi = multi;
565   if(multi == NULL)
566     /* the association is cleared, mark the easy handle as not used by an
567        interface */
568     data->state.used_interface = Curl_if_none;
569 }
570 
Curl_easy_initHandleData(struct SessionHandle * data)571 void Curl_easy_initHandleData(struct SessionHandle *data)
572 {
573     memset(&data->req, 0, sizeof(struct SingleRequest));
574 
575     data->req.maxdownload = -1;
576 }
577 
578 /*
579  * curl_easy_getinfo() is an external interface that allows an app to retrieve
580  * information from a performed transfer and similar.
581  */
582 #undef curl_easy_getinfo
curl_easy_getinfo(CURL * curl,CURLINFO info,...)583 CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
584 {
585   va_list arg;
586   void *paramp;
587   struct SessionHandle *data = (struct SessionHandle *)curl;
588 
589   va_start(arg, info);
590   paramp = va_arg(arg, void *);
591 
592   return Curl_getinfo(data, info, paramp);
593 }
594 
595 /*
596  * curl_easy_duphandle() is an external interface to allow duplication of a
597  * given input easy handle. The returned handle will be a new working handle
598  * with all options set exactly as the input source handle.
599  */
curl_easy_duphandle(CURL * incurl)600 CURL *curl_easy_duphandle(CURL *incurl)
601 {
602   bool fail = TRUE;
603   struct SessionHandle *data=(struct SessionHandle *)incurl;
604 
605   struct SessionHandle *outcurl = calloc(sizeof(struct SessionHandle), 1);
606 
607   if(NULL == outcurl)
608     return NULL; /* failure */
609 
610   do {
611 
612     /*
613      * We setup a few buffers we need. We should probably make them
614      * get setup on-demand in the code, as that would probably decrease
615      * the likeliness of us forgetting to init a buffer here in the future.
616      */
617     outcurl->state.headerbuff = malloc(HEADERSIZE);
618     if(!outcurl->state.headerbuff) {
619       break;
620     }
621     outcurl->state.headersize=HEADERSIZE;
622 
623     /* copy all userdefined values */
624     if(Curl_dupset(outcurl, data) != CURLE_OK)
625       break;
626 
627     /* the connection cache is setup on demand */
628     outcurl->state.connc = NULL;
629 
630     outcurl->state.lastconnect = -1;
631 
632     outcurl->progress.flags    = data->progress.flags;
633     outcurl->progress.callback = data->progress.callback;
634 
635 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
636     if(data->cookies) {
637       /* If cookies are enabled in the parent handle, we enable them
638          in the clone as well! */
639       outcurl->cookies = Curl_cookie_init(data,
640                                           data->cookies->filename,
641                                           outcurl->cookies,
642                                           data->set.cookiesession);
643       if(!outcurl->cookies) {
644         break;
645       }
646     }
647 #endif   /* CURL_DISABLE_HTTP */
648 
649     /* duplicate all values in 'change' */
650 
651     if(data->change.url) {
652       outcurl->change.url = strdup(data->change.url);
653       if(!outcurl->change.url)
654         break;
655       outcurl->change.url_alloc = TRUE;
656     }
657 
658     if(data->change.referer) {
659       outcurl->change.referer = strdup(data->change.referer);
660       if(!outcurl->change.referer)
661         break;
662       outcurl->change.referer_alloc = TRUE;
663     }
664 
665 #ifdef USE_ARES
666     /* If we use ares, we setup a new ares channel for the new handle */
667     if(ARES_SUCCESS != ares_init(&outcurl->state.areschannel))
668       break;
669 #endif
670 
671 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
672     outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
673                                      CURL_ICONV_CODESET_OF_NETWORK);
674     outcurl->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
675                                       CURL_ICONV_CODESET_OF_HOST);
676     outcurl->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
677                                   CURL_ICONV_CODESET_FOR_UTF8);
678 #endif
679 
680     Curl_easy_initHandleData(outcurl);
681 
682     outcurl->magic = CURLEASY_MAGIC_NUMBER;
683 
684     fail = FALSE; /* we reach this point and thus we are OK */
685 
686   } while(0);
687 
688   if(fail) {
689     if(outcurl) {
690       if(outcurl->state.connc &&
691          (outcurl->state.connc->type == CONNCACHE_PRIVATE))
692         Curl_rm_connc(outcurl->state.connc);
693       if(outcurl->state.headerbuff)
694         free(outcurl->state.headerbuff);
695       if(outcurl->change.url)
696         free(outcurl->change.url);
697       if(outcurl->change.referer)
698         free(outcurl->change.referer);
699       Curl_freeset(outcurl);
700       free(outcurl); /* free the memory again */
701       outcurl = NULL;
702     }
703   }
704 
705   return outcurl;
706 }
707 
708 /*
709  * curl_easy_reset() is an external interface that allows an app to re-
710  * initialize a session handle to the default values.
711  */
curl_easy_reset(CURL * curl)712 void curl_easy_reset(CURL *curl)
713 {
714   struct SessionHandle *data = (struct SessionHandle *)curl;
715 
716   Curl_safefree(data->state.pathbuffer);
717   data->state.pathbuffer=NULL;
718 
719   Curl_safefree(data->state.proto.generic);
720   data->state.proto.generic=NULL;
721 
722   /* zero out UserDefined data: */
723   Curl_freeset(data);
724   memset(&data->set, 0, sizeof(struct UserDefined));
725   (void)Curl_init_userdefined(&data->set);
726 
727   /* zero out Progress data: */
728   memset(&data->progress, 0, sizeof(struct Progress));
729 
730   /* init Handle data */
731   Curl_easy_initHandleData(data);
732 
733   data->progress.flags |= PGRS_HIDE;
734   data->state.current_speed = -1; /* init to negative == impossible */
735 }
736 
737 /*
738  * curl_easy_pause() allows an application to pause or unpause a specific
739  * transfer and direction. This function sets the full new state for the
740  * current connection this easy handle operates on.
741  *
742  * NOTE: if you have the receiving paused and you call this function to remove
743  * the pausing, you may get your write callback called at this point.
744  *
745  * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
746  */
curl_easy_pause(CURL * curl,int action)747 CURLcode curl_easy_pause(CURL *curl, int action)
748 {
749   struct SessionHandle *data = (struct SessionHandle *)curl;
750   struct SingleRequest *k = &data->req;
751   CURLcode result = CURLE_OK;
752 
753   /* first switch off both pause bits */
754   int newstate = k->keepon &~ (KEEP_READ_PAUSE| KEEP_WRITE_PAUSE);
755 
756   /* set the new desired pause bits */
757   newstate |= ((action & CURLPAUSE_RECV)?KEEP_READ_PAUSE:0) |
758     ((action & CURLPAUSE_SEND)?KEEP_WRITE_PAUSE:0);
759 
760   /* put it back in the keepon */
761   k->keepon = newstate;
762 
763   if(!(newstate & KEEP_READ_PAUSE) && data->state.tempwrite) {
764     /* we have a buffer for writing that we now seem to be able to deliver since
765        the receive pausing is lifted! */
766 
767     /* get the pointer, type and length in local copies since the function may
768        return PAUSE again and then we'll get a new copy allocted and stored in
769        the tempwrite variables */
770     char *tempwrite = data->state.tempwrite;
771     char *freewrite = tempwrite; /* store this pointer to free it later */
772     size_t tempsize = data->state.tempwritesize;
773     int temptype = data->state.tempwritetype;
774     size_t chunklen;
775 
776     /* clear tempwrite here just to make sure it gets cleared if there's no
777        further use of it, and make sure we don't clear it after the function
778        invoke as it may have been set to a new value by then */
779     data->state.tempwrite = NULL;
780 
781     /* since the write callback API is define to never exceed
782        CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact
783        have more data than that in our buffer here, we must loop sending the
784        data in multiple calls until there's no data left or we get another
785        pause returned.
786 
787        A tricky part is that the function we call will "buffer" the data
788        itself when it pauses on a particular buffer, so we may need to do some
789        extra trickery if we get a pause return here.
790     */
791     do {
792       chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
793 
794       result = Curl_client_write(data->state.current_conn,
795                                  temptype, tempwrite, chunklen);
796       if(result)
797         /* failures abort the loop at once */
798         break;
799 
800       if(data->state.tempwrite && (tempsize - chunklen)) {
801         /* Ouch, the reading is again paused and the block we send is now
802            "cached". If this is the final chunk we can leave it like this, but
803            if we have more chunks that are cached after this, we need to free
804            the newly cached one and put back a version that is truly the entire
805            contents that is saved for later
806         */
807         char *newptr;
808 
809         /* note that tempsize is still the size as before the callback was
810            used, and thus the whole piece of data to keep */
811         newptr = realloc(data->state.tempwrite, tempsize);
812 
813         if(!newptr) {
814           free(data->state.tempwrite); /* free old area */
815           data->state.tempwrite = NULL;
816           result = CURLE_OUT_OF_MEMORY;
817           /* tempwrite will be freed further down */
818           break;
819         }
820         data->state.tempwrite = newptr; /* store new pointer */
821         memcpy(newptr, tempwrite, tempsize);
822         data->state.tempwritesize = tempsize; /* store new size */
823         /* tempwrite will be freed further down */
824         break; /* go back to pausing until further notice */
825       }
826       else {
827         tempsize -= chunklen;  /* left after the call above */
828         tempwrite += chunklen; /* advance the pointer */
829       }
830 
831     } while((result == CURLE_OK) && tempsize);
832 
833     free(freewrite); /* this is unconditionally no longer used */
834   }
835 
836   return result;
837 }
838 
839 #ifdef CURL_DOES_CONVERSIONS
840 /*
841  * Curl_convert_to_network() is an internal function
842  * for performing ASCII conversions on non-ASCII platforms.
843  */
Curl_convert_to_network(struct SessionHandle * data,char * buffer,size_t length)844 CURLcode Curl_convert_to_network(struct SessionHandle *data,
845                                  char *buffer, size_t length)
846 {
847   CURLcode rc;
848 
849   if(data->set.convtonetwork) {
850     /* use translation callback */
851     rc = data->set.convtonetwork(buffer, length);
852     if(rc != CURLE_OK) {
853       failf(data,
854             "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %i: %s",
855             rc, curl_easy_strerror(rc));
856     }
857     return(rc);
858   } else {
859 #ifdef HAVE_ICONV
860     /* do the translation ourselves */
861     char *input_ptr, *output_ptr;
862     size_t in_bytes, out_bytes, rc;
863     int error;
864 
865     /* open an iconv conversion descriptor if necessary */
866     if(data->outbound_cd == (iconv_t)-1) {
867       data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
868                                      CURL_ICONV_CODESET_OF_HOST);
869       if(data->outbound_cd == (iconv_t)-1) {
870         error = ERRNO;
871         failf(data,
872               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
873                CURL_ICONV_CODESET_OF_NETWORK,
874                CURL_ICONV_CODESET_OF_HOST,
875                error, strerror(error));
876         return CURLE_CONV_FAILED;
877       }
878     }
879     /* call iconv */
880     input_ptr = output_ptr = buffer;
881     in_bytes = out_bytes = length;
882     rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes,
883                &output_ptr, &out_bytes);
884     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
885       error = ERRNO;
886       failf(data,
887         "The Curl_convert_to_network iconv call failed with errno %i: %s",
888              error, strerror(error));
889       return CURLE_CONV_FAILED;
890     }
891 #else
892     failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required");
893     return CURLE_CONV_REQD;
894 #endif /* HAVE_ICONV */
895   }
896 
897   return CURLE_OK;
898 }
899 
900 /*
901  * Curl_convert_from_network() is an internal function
902  * for performing ASCII conversions on non-ASCII platforms.
903  */
Curl_convert_from_network(struct SessionHandle * data,char * buffer,size_t length)904 CURLcode Curl_convert_from_network(struct SessionHandle *data,
905                                       char *buffer, size_t length)
906 {
907   CURLcode rc;
908 
909   if(data->set.convfromnetwork) {
910     /* use translation callback */
911     rc = data->set.convfromnetwork(buffer, length);
912     if(rc != CURLE_OK) {
913       failf(data,
914             "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %i: %s",
915             rc, curl_easy_strerror(rc));
916     }
917     return(rc);
918   }
919   else {
920 #ifdef HAVE_ICONV
921     /* do the translation ourselves */
922     char *input_ptr, *output_ptr;
923     size_t in_bytes, out_bytes, rc;
924     int error;
925 
926     /* open an iconv conversion descriptor if necessary */
927     if(data->inbound_cd == (iconv_t)-1) {
928       data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
929                                     CURL_ICONV_CODESET_OF_NETWORK);
930       if(data->inbound_cd == (iconv_t)-1) {
931         error = ERRNO;
932         failf(data,
933               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
934               CURL_ICONV_CODESET_OF_HOST,
935               CURL_ICONV_CODESET_OF_NETWORK,
936               error, strerror(error));
937         return CURLE_CONV_FAILED;
938       }
939     }
940     /* call iconv */
941     input_ptr = output_ptr = buffer;
942     in_bytes = out_bytes = length;
943     rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes,
944                &output_ptr, &out_bytes);
945     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
946       error = ERRNO;
947       failf(data,
948             "The Curl_convert_from_network iconv call failed with errno %i: %s",
949             error, strerror(error));
950       return CURLE_CONV_FAILED;
951     }
952 #else
953     failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required");
954     return CURLE_CONV_REQD;
955 #endif /* HAVE_ICONV */
956   }
957 
958   return CURLE_OK;
959 }
960 
961 /*
962  * Curl_convert_from_utf8() is an internal function
963  * for performing UTF-8 conversions on non-ASCII platforms.
964  */
Curl_convert_from_utf8(struct SessionHandle * data,char * buffer,size_t length)965 CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
966                                      char *buffer, size_t length)
967 {
968   CURLcode rc;
969 
970   if(data->set.convfromutf8) {
971     /* use translation callback */
972     rc = data->set.convfromutf8(buffer, length);
973     if(rc != CURLE_OK) {
974       failf(data,
975             "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %i: %s",
976             rc, curl_easy_strerror(rc));
977     }
978     return(rc);
979   } else {
980 #ifdef HAVE_ICONV
981     /* do the translation ourselves */
982     const char *input_ptr;
983     char *output_ptr;
984     size_t in_bytes, out_bytes, rc;
985     int error;
986 
987     /* open an iconv conversion descriptor if necessary */
988     if(data->utf8_cd == (iconv_t)-1) {
989       data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
990                                  CURL_ICONV_CODESET_FOR_UTF8);
991       if(data->utf8_cd == (iconv_t)-1) {
992         error = ERRNO;
993         failf(data,
994               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
995               CURL_ICONV_CODESET_OF_HOST,
996               CURL_ICONV_CODESET_FOR_UTF8,
997               error, strerror(error));
998         return CURLE_CONV_FAILED;
999       }
1000     }
1001     /* call iconv */
1002     input_ptr = output_ptr = buffer;
1003     in_bytes = out_bytes = length;
1004     rc = iconv(data->utf8_cd, &input_ptr, &in_bytes,
1005                &output_ptr, &out_bytes);
1006     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
1007       error = ERRNO;
1008       failf(data,
1009             "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
1010             error, strerror(error));
1011       return CURLE_CONV_FAILED;
1012     }
1013     if(output_ptr < input_ptr) {
1014       /* null terminate the now shorter output string */
1015       *output_ptr = 0x00;
1016     }
1017 #else
1018     failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required");
1019     return CURLE_CONV_REQD;
1020 #endif /* HAVE_ICONV */
1021   }
1022 
1023   return CURLE_OK;
1024 }
1025 
1026 #endif /* CURL_DOES_CONVERSIONS */
1027 
easy_connection(struct SessionHandle * data,curl_socket_t * sfd,struct connectdata ** connp)1028 static CURLcode easy_connection(struct SessionHandle *data,
1029                                 curl_socket_t *sfd,
1030                                 struct connectdata **connp)
1031 {
1032   CURLcode ret;
1033   long sockfd;
1034 
1035   if(data == NULL)
1036     return CURLE_BAD_FUNCTION_ARGUMENT;
1037 
1038   /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */
1039   if(!data->set.connect_only) {
1040     failf(data, "CONNECT_ONLY is required!");
1041     return CURLE_UNSUPPORTED_PROTOCOL;
1042   }
1043 
1044   ret = Curl_getconnectinfo(data, &sockfd, connp);
1045   if(ret != CURLE_OK)
1046     return ret;
1047 
1048   if(sockfd == -1) {
1049     failf(data, "Failed to get recent socket");
1050     return CURLE_UNSUPPORTED_PROTOCOL;
1051   }
1052 
1053   *sfd = (curl_socket_t)sockfd; /* we know that this is actually a socket
1054                                    descriptor so the typecast is fine here */
1055 
1056   return CURLE_OK;
1057 }
1058 
1059 /*
1060  * Receives data from the connected socket. Use after successful
1061  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
1062  * Returns CURLE_OK on success, error code on error.
1063  */
curl_easy_recv(CURL * curl,void * buffer,size_t buflen,size_t * n)1064 CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
1065 {
1066   curl_socket_t sfd;
1067   CURLcode ret;
1068   int ret1;
1069   ssize_t n1;
1070   struct connectdata *c;
1071   struct SessionHandle *data = (struct SessionHandle *)curl;
1072 
1073   ret = easy_connection(data, &sfd, &c);
1074   if(ret)
1075     return ret;
1076 
1077   *n = 0;
1078   ret1 = Curl_read(c, sfd, buffer, buflen, &n1);
1079 
1080   if(ret1 == -1)
1081     return CURLE_AGAIN;
1082 
1083   if(n1 == -1)
1084     return CURLE_RECV_ERROR;
1085 
1086   *n = (size_t)n1;
1087 
1088   return CURLE_OK;
1089 }
1090 
1091 /*
1092  * Sends data over the connected socket. Use after successful
1093  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
1094  */
curl_easy_send(CURL * curl,const void * buffer,size_t buflen,size_t * n)1095 CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
1096                         size_t *n)
1097 {
1098   curl_socket_t sfd;
1099   CURLcode ret;
1100   ssize_t n1;
1101   struct connectdata *c = NULL;
1102   struct SessionHandle *data = (struct SessionHandle *)curl;
1103 
1104   ret = easy_connection(data, &sfd, &c);
1105   if(ret)
1106     return ret;
1107 
1108   *n = 0;
1109   ret = Curl_write(c, sfd, buffer, buflen, &n1);
1110 
1111   if(n1 == -1)
1112     return CURLE_SEND_ERROR;
1113 
1114   /* detect EAGAIN */
1115   if((CURLE_OK == ret) && (0 == n1))
1116     return CURLE_AGAIN;
1117 
1118   *n = (size_t)n1;
1119 
1120   return ret;
1121 }
1122