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