Compare commits
	
		
			108 Commits
		
	
	
		
			curl-7_9_3
			...
			curl-7_9_4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5370d7a6eb | ||
|   | 685b180ab6 | ||
|   | 9dab850874 | ||
|   | 0d5bfe883e | ||
|   | cc2f1d4894 | ||
|   | a8dd13db4c | ||
|   | 325391aef9 | ||
|   | 3474ec4ecb | ||
|   | ec1736d488 | ||
|   | 4522579688 | ||
|   | 907a6e0eed | ||
|   | d20186a7b8 | ||
|   | b28051881e | ||
|   | bdea56cd3f | ||
|   | 8a3ec2c659 | ||
|   | 14e9420d2c | ||
|   | 5b58e61f28 | ||
|   | be2f3071b5 | ||
|   | 85dbf82d93 | ||
|   | a9c4963cc0 | ||
|   | a4934387d5 | ||
|   | e88a2ec6fc | ||
|   | 0666960173 | ||
|   | f114caca90 | ||
|   | 9468c9c796 | ||
|   | 76c53c690c | ||
|   | c341b11aaf | ||
|   | 6212e6990a | ||
|   | 28049a183c | ||
|   | 5d3dd7911e | ||
|   | ae8375516b | ||
|   | e3f10eb825 | ||
|   | 2b1f683239 | ||
|   | a2b19c9a63 | ||
|   | 4146ce8267 | ||
|   | 170bd6dafc | ||
|   | 7e16ec8724 | ||
|   | 8c459156f8 | ||
|   | 2db894807b | ||
|   | 95ceeb6e0b | ||
|   | c9c00d2a23 | ||
|   | 1afe49864d | ||
|   | 6924bee3a0 | ||
|   | 39d4552dab | ||
|   | a23c63738f | ||
|   | e911945c55 | ||
|   | 6d58d13710 | ||
|   | 0b177cb165 | ||
|   | 3e31b619de | ||
|   | f925979b2f | ||
|   | 49f7fa82b9 | ||
|   | e4cd4cf3f3 | ||
|   | e74b20926d | ||
|   | a312127c91 | ||
|   | 1dc5bf4f73 | ||
|   | 01cfe670c5 | ||
|   | fd307bfe29 | ||
|   | a00de093a7 | ||
|   | 7bfe853af3 | ||
|   | cbaecca8e9 | ||
|   | 8edfb370a8 | ||
|   | 4c08c8f7db | ||
|   | c174680a03 | ||
|   | cb5f6e18e6 | ||
|   | b798e7a5ae | ||
|   | 5deab7ad27 | ||
|   | 12cdfd282d | ||
|   | eba8035e12 | ||
|   | edcbf4350b | ||
|   | 9289ea471f | ||
|   | 7d06185aa6 | ||
|   | 01ecb1d7e7 | ||
|   | e177f14595 | ||
|   | 5c6eddcadd | ||
|   | b3b4786990 | ||
|   | fbe2907599 | ||
|   | 343da8d4b3 | ||
|   | 8d97792dbc | ||
|   | 8d07c87be7 | ||
|   | ed21701df3 | ||
|   | df01507582 | ||
|   | f2bda5fd5b | ||
|   | cba9838e8f | ||
|   | b6dba9f5dd | ||
|   | 6e9d1617c6 | ||
|   | ea811fee52 | ||
|   | 7391fd8f6a | ||
|   | 6c00c58f2a | ||
|   | 4931fbce49 | ||
|   | fefc7ea600 | ||
|   | d220389647 | ||
|   | a1f910c159 | ||
|   | e4866563de | ||
|   | 47f45aa229 | ||
|   | affe334675 | ||
|   | ee7e184e26 | ||
|   | bec0ebacf1 | ||
|   | 5bd6d631c6 | ||
|   | fd1799f3bb | ||
|   | d84a0c51e0 | ||
|   | d9a7c7de51 | ||
|   | d57e09889a | ||
|   | eecb86bfb0 | ||
|   | 0b1197936c | ||
|   | b545ac6391 | ||
|   | a922132e4a | ||
|   | 9474e8d6d2 | ||
|   | 6328428568 | 
							
								
								
									
										165
									
								
								CHANGES
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								CHANGES
									
									
									
									
									
								
							| @@ -7,6 +7,171 @@ | |||||||
|                                History of Changes |                                History of Changes | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Version 7.9.4 | ||||||
|  |  | ||||||
|  | - no changes since pre-release | ||||||
|  |  | ||||||
|  | Version 7.9.4-pre2 | ||||||
|  |  | ||||||
|  | Daniel (3 February 2002) | ||||||
|  | - Eric Melville provided a few spelling corrections in the curl man page. | ||||||
|  |  | ||||||
|  | Daniel (1 February 2002) | ||||||
|  | - Andreas Damm corrected the unconditional use of gmtime() in getdate, it now | ||||||
|  |   uses gmtime_r() on all hosts that have it. | ||||||
|  |  | ||||||
|  | Daniel (31 January 2002) | ||||||
|  | - An anonymous bug report identified a problem in the DNS caching which made it | ||||||
|  |   sometimes allocate one byte too little to store the cache entry in. This | ||||||
|  |   happened when the port number started with 1! | ||||||
|  |  | ||||||
|  | - Albert Chin provided a patch that improves the gethostbyname_r() configure | ||||||
|  |   check on HP-UX 11.00. | ||||||
|  |  | ||||||
|  | Version 7.9.4-pre1 | ||||||
|  |  | ||||||
|  | Daniel (30 January 2002) | ||||||
|  | - Georg Horn found another way the SSL reading failed due to the non-blocking | ||||||
|  |   state of the sockets! I fixed. | ||||||
|  |  | ||||||
|  | Daniel (29 January 2002) | ||||||
|  | - Multipart formposts now send the full request properly, including the CRLF. | ||||||
|  |   They were previously treated as part of the post data. | ||||||
|  |  | ||||||
|  | - The upload byte counter bugged. | ||||||
|  |  | ||||||
|  | - T. Bharath pointed out that we seed SSL on every connect, which is a time- | ||||||
|  |   consuming operation that should only be needed to do once. We patched | ||||||
|  |   libcurl to now only seed on the first connect when unseeded. The seeded | ||||||
|  |   status is global so it'll now only happen once during a program's life time. | ||||||
|  |  | ||||||
|  |   If the random_file or egdsocket is set, the seed will be re-made though. | ||||||
|  |  | ||||||
|  | - Giaslas Georgios introduced CURLINFO_CONTENT_TYPE that lets | ||||||
|  |   curl_easy_getinfo() read the content-type from the previous request. | ||||||
|  |  | ||||||
|  | Daniel (28 January 2002) | ||||||
|  | - Kjetil Jacobsen found a way to crash curl and after much debugging, it | ||||||
|  |   turned out it was a IPv4-linux only problem introduced in 7.9.3 related to | ||||||
|  |   name resolving. | ||||||
|  |  | ||||||
|  | - Andreas Damm posted a huge patch that made the curl_getdate() function fully | ||||||
|  |   reentrant! | ||||||
|  |  | ||||||
|  | - Steve Marx pointed out that you couldn't mix CURLOPT_CUSTOMREQUEST with | ||||||
|  |   CURLOPT_POSTFIELDS. You can now! | ||||||
|  |  | ||||||
|  | Daniel (25 January 2002) | ||||||
|  | - Krishnendu Majumdar pointed out that the header length counter was not reset | ||||||
|  |   between multiple requests on the same handle. | ||||||
|  |  | ||||||
|  | - Pedro Neves rightfully questioned why curl always append \r\n to the data | ||||||
|  |   that is sent in HTTP POST requests. Unfortunately, this broke the test suite | ||||||
|  |   as the test HTTP server is lame enough not to deal with this... :-O | ||||||
|  |  | ||||||
|  | - Following Location: headers when the connection didn't close didn't work as | ||||||
|  |   libcurl didn't properly stop reading. This problem was added in 7.9.3 due to | ||||||
|  |   the restructured internals. 'Frank' posted a bug report about this. | ||||||
|  |  | ||||||
|  | Daniel (24 January 2002) | ||||||
|  | - Kevin Roth very quickly spotted that we wrongly installed the example | ||||||
|  |   programs that were built in the multi directory, when 'make install' was | ||||||
|  |   used. :-/ | ||||||
|  |  | ||||||
|  | Version 7.9.3 | ||||||
|  |  | ||||||
|  | Daniel (23 January 2002) | ||||||
|  | - Andr<64>s Garc<72>a found a persistancy problem when doing HTTP HEAD, that made | ||||||
|  |   curl "hang" until the connection was closed by the server. This problem has | ||||||
|  |   been introduced in 7.9.3 due to internal rewrites, this was not present in | ||||||
|  |   7.9.2. | ||||||
|  |  | ||||||
|  | Version 7.9.3-pre4 | ||||||
|  |  | ||||||
|  | Daniel (19 January 2002) | ||||||
|  | - Antonio filed bug report #505514 and provided a fix! When doing multipart | ||||||
|  |   formposts, libcurl would include an error text in the actual post if a | ||||||
|  |   specified file wasn't found. This is not libcurl's job. Instead we add an | ||||||
|  |   empty part. | ||||||
|  |  | ||||||
|  | Daniel (18 January 2002) | ||||||
|  | - Played around with stricter compiler warnings for gcc (when ./configure | ||||||
|  |   --enable-debug is used) and changed some minor things to stop the warnings. | ||||||
|  |  | ||||||
|  | - Commented out the 'long long' and 'long double' checks in configure.in, as | ||||||
|  |   we don't currently use them anyway and the code in lib/mprintf.c that use | ||||||
|  |   them causes warnings. | ||||||
|  |  | ||||||
|  | - Saul Good and jonatan pointed out Mac OS X build problems with pre3 and how | ||||||
|  |   to correct them. Two compiler warnings were removed as well. | ||||||
|  |  | ||||||
|  | - Andr<64>s Garc<72>a fixed two minor mingw32 building problems. | ||||||
|  |  | ||||||
|  | Version 7.9.3-pre3 | ||||||
|  |  | ||||||
|  | Daniel (17 January 2002) | ||||||
|  | - docs/libcurl-the-guide is a new tutorial for our libcurl programming | ||||||
|  |   friends. | ||||||
|  |  | ||||||
|  | - Richard Archer brought back the ability to compile and build with OpenSSL | ||||||
|  |   versions before 0.9.5. | ||||||
|  |   [http://sourceforge.net/tracker/?func=detail&atid=100976&aid=504163&group_id=976] | ||||||
|  |  | ||||||
|  | - The DNS cache code didn't take the port number into account, which made it | ||||||
|  |   work rather bad on IPv6-enabled hosts (especially when doing passive | ||||||
|  |   FTP). Sterling fixed it. | ||||||
|  |  | ||||||
|  | Daniel (16 January 2002) | ||||||
|  | - Georg Horn could make a transfer time-out without error text. I found it and | ||||||
|  |   corrected it. | ||||||
|  |  | ||||||
|  | - SSL writes didn't work, they return an uninitialized value that caused | ||||||
|  |   havoc all over. Georg Horn experienced this. | ||||||
|  |  | ||||||
|  | - Kevin Roth patched the curl_version() function to use the proper OpenSSL | ||||||
|  |   function for version information. This way, curl will report the version of | ||||||
|  |   the SSL library actually running right now, not the one that had its headers | ||||||
|  |   installed when libcurl was built. Mainly intersting when running with shared | ||||||
|  |   OpenSSL libraries.   | ||||||
|  |  | ||||||
|  | Version 7.9.3-pre2 | ||||||
|  |  | ||||||
|  | Daniel (16 January 2002) | ||||||
|  | - Mofied the main transfer loop and related stuff to deal with non-blocking | ||||||
|  |   sockets in the upload section. While doing this, I've now separated the | ||||||
|  |   connection oriented buffers to have one for downloads and one for uploads | ||||||
|  |   (as two can happen simultaneously). I also shrunk the buffers to 20K | ||||||
|  |   each. As we have a scratch buffer twice the size of the upload buffer, we | ||||||
|  |   arrived at 80K for buffers compared with the previous 150K. | ||||||
|  |  | ||||||
|  | - Added the --cc option to curl-config command as it enables so very cool | ||||||
|  |   one-liners. Have a go a this one, building the simple.c example: | ||||||
|  |  | ||||||
|  |         $ `curl-config --cc --cflags --libs` -o example simple.c | ||||||
|  |  | ||||||
|  | Daniel (14 January 2002) | ||||||
|  | - I made all socket reads (recv) handle EWOULDBLOCK. I hope nicely. Now we | ||||||
|  |   only need to address all writes (send) too and then I'm ready for another | ||||||
|  |   pre-release... | ||||||
|  |  | ||||||
|  | - Stoned Elipot patched the in_addr_t configure test to make it work better on | ||||||
|  |   more platforms. | ||||||
|  |  | ||||||
|  | Daniel (9 January 2002) | ||||||
|  | - Cris Bailiff found out that filling up curl's SSL session cache caused a | ||||||
|  |   crash! | ||||||
|  |  | ||||||
|  | - Posted the curl questionnaire on the web site. If you haven't posted your | ||||||
|  |   opinions there yet, go there and do it now while it is still there: | ||||||
|  |  | ||||||
|  |         http://curl.haxx.se/q/ | ||||||
|  |  | ||||||
|  | - Georg Horn quickly found out that the SSL reading no longer worked as | ||||||
|  |   supposed since the switch to non-blocking sockets. I've made a quick patch | ||||||
|  |   (for reading only) but we should improve it even further. | ||||||
|  |  | ||||||
|  | Version 7.9.3-pre1 | ||||||
|  |  | ||||||
| Daniel (7 January 2002) | Daniel (7 January 2002) | ||||||
| - I made the 'bool' typedef use an "unsigned char". It makes it the same on | - I made the 'bool' typedef use an "unsigned char". It makes it the same on | ||||||
|   all platforms, no matter what the platform thinks the default format for |   all platforms, no matter what the platform thinks the default format for | ||||||
|   | |||||||
| @@ -176,7 +176,7 @@ AC_DEFUN([TYPE_IN_ADDR_T], | |||||||
|       AC_DEFINE_UNQUOTED(in_addr_t, $curl_cv_in_addr_t_equiv, |       AC_DEFINE_UNQUOTED(in_addr_t, $curl_cv_in_addr_t_equiv, | ||||||
| 			[type to use in place of in_addr_t if not defined])], | 			[type to use in place of in_addr_t if not defined])], | ||||||
|       [#include <sys/types.h> |       [#include <sys/types.h> | ||||||
| #include <sys/socket.h>, | #include <sys/socket.h> | ||||||
| #include <arpa/inet.h>]) | #include <arpa/inet.h>]) | ||||||
| ]) | ]) | ||||||
|  |  | ||||||
| @@ -377,6 +377,7 @@ AC_DEFUN(CURL_CHECK_GETHOSTBYNAME_R, | |||||||
|  |  | ||||||
| int | int | ||||||
| gethostbyname_r(const char *, struct hostent *, struct hostent_data *);],[ | gethostbyname_r(const char *, struct hostent *, struct hostent_data *);],[ | ||||||
|  | struct hostent_data data; | ||||||
| gethostbyname_r(NULL, NULL, NULL);],[ | gethostbyname_r(NULL, NULL, NULL);],[ | ||||||
|       AC_MSG_RESULT(yes) |       AC_MSG_RESULT(yes) | ||||||
|       AC_DEFINE(HAVE_GETHOSTBYNAME_R_3) |       AC_DEFINE(HAVE_GETHOSTBYNAME_R_3) | ||||||
| @@ -394,6 +395,7 @@ gethostbyname_r(NULL, NULL, NULL);],[ | |||||||
|  |  | ||||||
| int | int | ||||||
| gethostbyname_r(const char *,struct hostent *, struct hostent_data *);],[ | gethostbyname_r(const char *,struct hostent *, struct hostent_data *);],[ | ||||||
|  | struct hostent_data data; | ||||||
| gethostbyname_r(NULL, NULL, NULL);],[ | gethostbyname_r(NULL, NULL, NULL);],[ | ||||||
| 	AC_MSG_RESULT(yes) | 	AC_MSG_RESULT(yes) | ||||||
| 	AC_DEFINE(HAVE_GETHOSTBYNAME_R_3) | 	AC_DEFINE(HAVE_GETHOSTBYNAME_R_3) | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ AC_ARG_ENABLE(debug, | |||||||
|   *)   AC_MSG_RESULT(yes) |   *)   AC_MSG_RESULT(yes) | ||||||
|  |  | ||||||
|     CPPFLAGS="$CPPFLAGS -DMALLOCDEBUG" |     CPPFLAGS="$CPPFLAGS -DMALLOCDEBUG" | ||||||
|     CFLAGS="-W -Wall -Wwrite-strings -pedantic -g"  |     CFLAGS="-W -Wall -Wwrite-strings -pedantic -Wundef -Wpointer-arith -Wcast-align -Wnested-externs -g"  | ||||||
|        ;; |        ;; | ||||||
|   esac ], |   esac ], | ||||||
|        AC_MSG_RESULT(no) |        AC_MSG_RESULT(no) | ||||||
| @@ -524,9 +524,9 @@ AC_HEADER_TIME | |||||||
| # mprintf() checks: | # mprintf() checks: | ||||||
|  |  | ||||||
| # check for 'long double' | # check for 'long double' | ||||||
| AC_CHECK_SIZEOF(long double, 8) | # AC_CHECK_SIZEOF(long double, 8) | ||||||
| # check for 'long long' | # check for 'long long' | ||||||
| AC_CHECK_SIZEOF(long long, 4) | # AC_CHECK_SIZEOF(long long, 4) | ||||||
|  |  | ||||||
| # check for ssize_t | # check for ssize_t | ||||||
| AC_CHECK_TYPE(ssize_t, int) | AC_CHECK_TYPE(ssize_t, int) | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ Usage: curl-config [OPTION] | |||||||
|  |  | ||||||
| Available values for OPTION include: | Available values for OPTION include: | ||||||
|  |  | ||||||
|  |   --cc        compiler | ||||||
|   --cflags    pre-processor and compiler flags |   --cflags    pre-processor and compiler flags | ||||||
|   --feature   newline separated list of enabled features |   --feature   newline separated list of enabled features | ||||||
|   --help      display this help and exit |   --help      display this help and exit | ||||||
| @@ -42,6 +43,10 @@ while test $# -gt 0; do | |||||||
|     esac |     esac | ||||||
|  |  | ||||||
|     case "$1" in |     case "$1" in | ||||||
|  |     --cc) | ||||||
|  | 	echo @CC@ | ||||||
|  | 	;; | ||||||
|  |  | ||||||
|     --prefix) |     --prefix) | ||||||
| 	echo $prefix | 	echo $prefix | ||||||
| 	;; | 	;; | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								docs/BUGS
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								docs/BUGS
									
									
									
									
									
								
							| @@ -23,11 +23,16 @@ BUGS | |||||||
|  |  | ||||||
|   When reporting a bug, you should include information that will help us |   When reporting a bug, you should include information that will help us | ||||||
|   understand what's wrong, what you expected to happen and how to repeat the |   understand what's wrong, what you expected to happen and how to repeat the | ||||||
|   bad behaviour. You therefore need to supply your operating system's name and |   bad behavior. You therefore need to supply your operating system's name and | ||||||
|   version number (uname -a under a unix is fine), what version of curl you're |   version number (uname -a under a unix is fine), what version of curl you're | ||||||
|   using (curl -V is fine), what URL you were working with and anything else |   using (curl -V is fine), what URL you were working with and anything else | ||||||
|   you think matters. |   you think matters. | ||||||
|  |  | ||||||
|  |   Since curl deals with networks, it often helps us a lot if you include a | ||||||
|  |   protocol debug dump with your bug report. The output you get by using the -v | ||||||
|  |   flag. Usually, you also get more info by using -i so that is likely to be | ||||||
|  |   useful when reporting bugs as well. | ||||||
|  |  | ||||||
|   If curl crashed, causing a core dump (in unix), there is hardly any use to |   If curl crashed, causing a core dump (in unix), there is hardly any use to | ||||||
|   send that huge file to anyone of us. Unless we have an exact same system |   send that huge file to anyone of us. Unless we have an exact same system | ||||||
|   setup as you, we can't do much with it. What we instead ask of you is to get |   setup as you, we can't do much with it. What we instead ask of you is to get | ||||||
| @@ -36,8 +41,7 @@ BUGS | |||||||
|   The address and how to subscribe to the mailing list is detailed in the |   The address and how to subscribe to the mailing list is detailed in the | ||||||
|   MANUAL file. |   MANUAL file. | ||||||
|  |  | ||||||
|   How To Get A Stack Trace | HOW TO GET A STACK TRACE | ||||||
|   ======================== |  | ||||||
|  |  | ||||||
|   First, you must make sure that you compile all sources with -g and that you |   First, you must make sure that you compile all sources with -g and that you | ||||||
|   don't 'strip' the final executable. Try to avoid optimizing the code as |   don't 'strip' the final executable. Try to avoid optimizing the code as | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								docs/FAQ
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								docs/FAQ
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| Updated: December 21, 2001 (http://curl.haxx.se/docs/faq.shtml) | Updated: January 22, 2002 (http://curl.haxx.se/docs/faq.shtml) | ||||||
|                                   _   _ ____  _      |                                   _   _ ____  _      | ||||||
|                               ___| | | |  _ \| |     |                               ___| | | |  _ \| |     | ||||||
|                              / __| | | | |_) | |     |                              / __| | | | |_) | |     | ||||||
| @@ -163,9 +163,8 @@ FAQ | |||||||
|  |  | ||||||
|   1.6 What do you get for making cURL? |   1.6 What do you get for making cURL? | ||||||
|  |  | ||||||
|   Project cURL is entirely free and open, without any commercial interests or |   Project cURL is entirely free and open. No person gets paid in any way for | ||||||
|   money involved. No person gets paid in any way for developing curl. We all |   developing curl. We all do this voluntarily on our spare time. | ||||||
|   do this voluntarily on our spare time. |  | ||||||
|  |  | ||||||
|   We get some help from companies. Contactor Data hosts the curl web site and |   We get some help from companies. Contactor Data hosts the curl web site and | ||||||
|   the main mailing list, Haxx owns the curl web site's domain and |   the main mailing list, Haxx owns the curl web site's domain and | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								docs/INSTALL
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								docs/INSTALL
									
									
									
									
									
								
							| @@ -326,19 +326,15 @@ QNX | |||||||
| === | === | ||||||
|    (This section was graciously brought to us by David Bentham) |    (This section was graciously brought to us by David Bentham) | ||||||
|  |  | ||||||
|    By setting FD_SETSIZE early in connect.c we override the QNX default value |    As QNX is targetted for resource constrained environments, the QNX headers | ||||||
|    and thus avoid a crash. |    set conservative limits. This includes the FD_SETSIZE macro, set by default | ||||||
|  |    to 32. Socket descriptors returned within the CURL library may exceed this, | ||||||
|  |    resulting in memory faults/SIGSEGV crashes when passed into select(..) | ||||||
|  |    calls using fd_set macros. | ||||||
|  |  | ||||||
|    Fortunately in the QNX headers its defined as |    A good all-round solution to this is to override the default when building | ||||||
|  |    libcurl, by overriding CFLAGS during configure, example | ||||||
|     #ifndef FD_SETSIZE |    #  configure CFLAGS='-DFD_SETSIZE=64 -g -O2' | ||||||
|       #define FD_SETSIZE 32 |  | ||||||
|     #endif |  | ||||||
|  |  | ||||||
|    so its relatively easy to override without changing the original |  | ||||||
|    definition. QNX claim posix compliance so this definition style could be |  | ||||||
|    standard in other o/s's. Eg Microsoft Visual C++ 6 defines it similarly, |  | ||||||
|    but its set to 64. |  | ||||||
|  |  | ||||||
| CROSS COMPILE | CROSS COMPILE | ||||||
| ============= | ============= | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								docs/TODO
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								docs/TODO
									
									
									
									
									
								
							| @@ -19,10 +19,7 @@ TODO | |||||||
|  |  | ||||||
|  * The new 'multi' interface is being designed. Work out the details, start |  * The new 'multi' interface is being designed. Work out the details, start | ||||||
|    implementing and write test applications! |    implementing and write test applications! | ||||||
|    [http://curl.haxx.se/dev/multi.h] |    [http://curl.haxx.se/lxr/source/lib/multi.h] | ||||||
|  |  | ||||||
|  * Add a name resolve cache to libcurl to make repeated fetches to the same |  | ||||||
|    host name (when persitancy isn't available) faster. |  | ||||||
|  |  | ||||||
|  * Introduce another callback interface for upload/download that makes one |  * Introduce another callback interface for upload/download that makes one | ||||||
|    less copy of data and thus a faster operation. |    less copy of data and thus a faster operation. | ||||||
| @@ -33,13 +30,36 @@ TODO | |||||||
|    telnet, ldap, dict or file. |    telnet, ldap, dict or file. | ||||||
|  |  | ||||||
|  * Add asynchronous name resolving. http://curl.haxx.se/dev/async-resolver.txt |  * Add asynchronous name resolving. http://curl.haxx.se/dev/async-resolver.txt | ||||||
|  |    This should be made to work on most of the supported platforms, or | ||||||
|  |    otherwise it isn't really interesting. | ||||||
|  |  | ||||||
|  * Strip any trailing CR from the error message when Curl_failf() is used. |  * Data sharing. Tell which easy handles within a multi handle that should | ||||||
|  |    share cookies, connection cache, dns cache, ssl session cache. | ||||||
|  |  | ||||||
|  |  * Mutexes. By adding mutex callback support, the 'data sharing' mentioned | ||||||
|  |    above can be made between several easy handles running in different threads | ||||||
|  |    too. The actual mutex implementations will be left for the application to | ||||||
|  |    implement, libcurl will merely call 'getmutex' and 'leavemutex' callbacks. | ||||||
|  |  | ||||||
|  |  * No-faster-then-this transfers. Many people have limited bandwidth and they | ||||||
|  |    want the ability to make sure their transfers never use more bandwith than | ||||||
|  |    they think is good. | ||||||
|  |  | ||||||
|  |  * Set the SO_KEEPALIVE socket option to make libcurl notice and disconnect | ||||||
|  |    very long time idle connections. | ||||||
|  |  | ||||||
|  |  * Make sure we don't ever loop because of non-blocking sockets return | ||||||
|  |    EWOULDBLOCK or similar. This concerns the HTTP request sending, the FTP | ||||||
|  |    command sending etc. | ||||||
|  |  | ||||||
|  |  * Go through the code and verify that libcurl deals with big files >2GB and | ||||||
|  |    >4GB all over. Bug reports indicate that it doesn't currently work | ||||||
|  |    properly. | ||||||
|  |  | ||||||
|  DOCUMENTATION |  DOCUMENTATION | ||||||
|  |  | ||||||
|  * Document all CURLcode error codes, why they happen and what most likely |  * Document all CURLcode error codes, why they happen and what most likely | ||||||
|    will make them not happen again. |    will make them not happen again. In a libcurl point of view. | ||||||
|  |  | ||||||
|  FTP |  FTP | ||||||
|  |  | ||||||
| @@ -54,11 +74,7 @@ TODO | |||||||
|    already working http dito works. It of course requires that 'MDTM' works, |    already working http dito works. It of course requires that 'MDTM' works, | ||||||
|    and it isn't a standard FTP command. |    and it isn't a standard FTP command. | ||||||
|  |  | ||||||
|  * Suggested on the mailing list: CURLOPT_FTP_MKDIR...! |  * Add FTPS support with SSL for the data connection too. | ||||||
|  |  | ||||||
|  * Always use the FTP SIZE command before downloading, as that makes it more |  | ||||||
|    likely that we know the size when downloading. Some sites support SIZE but |  | ||||||
|    don't show the size in the RETR response! |  | ||||||
|  |  | ||||||
|  HTTP |  HTTP | ||||||
|  |  | ||||||
| @@ -83,34 +99,53 @@ TODO | |||||||
|    http://www.innovation.ch/java/ntlm.html that contains detailed reverse- |    http://www.innovation.ch/java/ntlm.html that contains detailed reverse- | ||||||
|    engineered info. |    engineered info. | ||||||
|  |  | ||||||
|  * RFC2617 compliance, "Digest Access Authentication" |  * RFC2617 compliance, "Digest Access Authentication" A valid test page seem | ||||||
|    A valid test page seem to exist at: |    to exist at: http://hopf.math.nwu.edu/testpage/digest/ And some friendly | ||||||
|    http://hopf.math.nwu.edu/testpage/digest/ |    person's server source code is available at | ||||||
|    And some friendly person's server source code is available at |    http://hopf.math.nwu.edu/digestauth/index.html Then there's the Apache | ||||||
|    http://hopf.math.nwu.edu/digestauth/index.html |    mod_digest source code too of course.  It seems as if Netscape doesn't | ||||||
|    Then there's the Apache mod_digest source code too of course.  It seems as |    support this, and not many servers do. Although this is a lot better | ||||||
|    if Netscape doesn't support this, and not many servers do. Although this is |    authentication method than the more common "Basic". Basic sends the | ||||||
|    a lot better authentication method than the more common "Basic". Basic |    password in cleartext over the network, this "Digest" method uses a | ||||||
|    sends the password in cleartext over the network, this "Digest" method uses |    challange-response protocol which increases security quite a lot. | ||||||
|    a challange-response protocol which increases security quite a lot. |  | ||||||
|  |  * Pipelining. Sending multiple requests before the previous one(s) are done. | ||||||
|  |    This could possibly be implemented using the multi interface to queue | ||||||
|  |    requests and the response data. | ||||||
|  |  | ||||||
|  TELNET |  TELNET | ||||||
|  |  | ||||||
|  * Make TELNET work on windows98! |  * Make TELNET work on windows98! | ||||||
|  |  | ||||||
|  |  * Reading input (to send to the remote server) on stdin is a crappy solution | ||||||
|  |    for library purposes. We need to invent a good way for the application to | ||||||
|  |    be able to provide the data to send. | ||||||
|  |  | ||||||
|  |  * Move the telnet support's network select() loop go away and merge the code | ||||||
|  |    into the main transfer loop. Until this is done, the multi interface won't | ||||||
|  |    work for telnet. | ||||||
|  |  | ||||||
|  SSL |  SSL | ||||||
|  |  | ||||||
|  * Add an interface to libcurl that enables "session IDs" to get |  * Add an interface to libcurl that enables "session IDs" to get | ||||||
|    exported/imported. Cris Bailiff said: "OpenSSL has functions which can |    exported/imported. Cris Bailiff said: "OpenSSL has functions which can | ||||||
|    serialise the current SSL state to a buffer of your choice, and |    serialise the current SSL state to a buffer of your choice, and | ||||||
|    recover/reset the state from such a buffer at a later date - this is used |    recover/reset the state from such a buffer at a later date - this is used | ||||||
|    by mod_ssl for apache to implement and SSL session ID cache" |    by mod_ssl for apache to implement and SSL session ID cache". This whole | ||||||
|  |    idea might become moot if we enable the 'data sharing' as mentioned in the | ||||||
|  |    LIBCURL label above. | ||||||
|  |  | ||||||
|  * Make curl's SSL layer option capable of using other free SSL libraries. |  * Make curl's SSL layer option capable of using other free SSL libraries. | ||||||
|    Such as the Mozilla Security Services |    Such as the Mozilla Security Services | ||||||
|    (http://www.mozilla.org/projects/security/pki/nss/) and GNUTLS |    (http://www.mozilla.org/projects/security/pki/nss/) and GNUTLS | ||||||
|    (http://gnutls.hellug.gr/) |    (http://gnutls.hellug.gr/) | ||||||
|  |  | ||||||
|  |  LDAP | ||||||
|  |  | ||||||
|  |  * Look over the implementation. The looping will have to "go away" from the | ||||||
|  |    lib/ldap.c source file and get moved to the main network code so that the | ||||||
|  |    multi interface and friends will work for LDAP as well. | ||||||
|  |  | ||||||
|  CLIENT |  CLIENT | ||||||
|  |  | ||||||
|  * "curl ftp://site.com/*.txt" |  * "curl ftp://site.com/*.txt" | ||||||
| @@ -119,11 +154,10 @@ TODO | |||||||
|    the same syntax to specify several files to get uploaded (using the same |    the same syntax to specify several files to get uploaded (using the same | ||||||
|    persistant connection), using -T. |    persistant connection), using -T. | ||||||
|  |  | ||||||
|  * Say you have a list of FTP addresses to download in a file named |  * When the multi interface has been implemented and proved to work, the | ||||||
|    ftp-list.txt: "cat ftp-list.txt | xargs curl -O -O -O [...]". curl _needs_ |    client could be told to use maximum N simultaneous transfers and then just | ||||||
|    an "-Oalways" flag -- all addresses on the command line use the base |    make sure that happens. It should of course not make more than one | ||||||
|    filename to store locally.  Else a script must precount the # of URLs, |    connection to the same remote host. | ||||||
|    construct the proper number of "-O"s... |  | ||||||
|  |  | ||||||
|  TEST SUITE |  TEST SUITE | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| .\" nroff -man curl-config.1 | .\" nroff -man curl-config.1 | ||||||
| .\" Written by Daniel Stenberg | .\" Written by Daniel Stenberg | ||||||
| .\" | .\" | ||||||
| .TH curl-config 1 "16 August 2001" "Curl 7.8.1" "curl-config manual" | .TH curl-config 1 "21 January 2002" "Curl 7.9.3" "curl-config manual" | ||||||
| .SH NAME | .SH NAME | ||||||
| curl-config \- Get information about a libcurl installation | curl-config \- Get information about a libcurl installation | ||||||
| .SH SYNOPSIS | .SH SYNOPSIS | ||||||
| @@ -11,6 +11,8 @@ curl-config \- Get information about a libcurl installation | |||||||
| .B curl-config | .B curl-config | ||||||
| displays information about a previous curl and libcurl installation. | displays information about a previous curl and libcurl installation. | ||||||
| .SH OPTIONS | .SH OPTIONS | ||||||
|  | .IP "--cc" | ||||||
|  | Displays the compiler used to build libcurl. | ||||||
| .IP "--cflags" | .IP "--cflags" | ||||||
| Set of compiler options (CFLAGS) to use when compiling files that use | Set of compiler options (CFLAGS) to use when compiling files that use | ||||||
| libcurl. Currently that is only thw include path to the curl include files. | libcurl. Currently that is only thw include path to the curl include files. | ||||||
| @@ -38,18 +40,23 @@ major, minor, patch. So that libcurl 7.7.4 would appear as 070704 and libcurl | |||||||
| .SH "EXAMPLES" | .SH "EXAMPLES" | ||||||
| What linker options do I need when I link with libcurl? | What linker options do I need when I link with libcurl? | ||||||
|  |  | ||||||
|   curl-config --libs |   $ curl-config --libs | ||||||
|  |  | ||||||
| What compiler options do I need when I compile using libcurl functions? | What compiler options do I need when I compile using libcurl functions? | ||||||
|  |  | ||||||
|   curl-config --cflags |   $ curl-config --cflags | ||||||
|  |  | ||||||
| How do I know if libcurl was built with SSL support? | How do I know if libcurl was built with SSL support? | ||||||
|  |  | ||||||
|   curl-config --feature | grep SSL |   $ curl-config --feature | grep SSL | ||||||
|  |  | ||||||
| What's the installed libcurl version? | What's the installed libcurl version? | ||||||
|  |  | ||||||
|   curl-config --version |   $ curl-config --version | ||||||
|  |  | ||||||
|  | How do I build a single file with a one-line command? | ||||||
|  |  | ||||||
|  |   $ `curl-config --cc --cflags --libs` -o example example.c | ||||||
|  |  | ||||||
| .SH "SEE ALSO" | .SH "SEE ALSO" | ||||||
| .BR curl (1) | .BR curl (1) | ||||||
|   | |||||||
| @@ -510,7 +510,7 @@ password is specified, curl will ask for it interactively. | |||||||
|  |  | ||||||
| If this option is used several times, the last one will be used. | If this option is used several times, the last one will be used. | ||||||
| .IP "--url <URL>" | .IP "--url <URL>" | ||||||
| Specify a URL to fetch. This option is mostly handy when you wanna specify | Specify a URL to fetch. This option is mostly handy when you want to specify | ||||||
| URL(s) in a config file. | URL(s) in a config file. | ||||||
|  |  | ||||||
| This option may be used any number of times. To control where this URL is written, use the | This option may be used any number of times. To control where this URL is written, use the | ||||||
| @@ -538,7 +538,7 @@ write "@-". | |||||||
| The variables present in the output format will be substituted by the value or | The variables present in the output format will be substituted by the value or | ||||||
| text that curl thinks fit, as described below. All variables are specified | text that curl thinks fit, as described below. All variables are specified | ||||||
| like %{variable_name} and to output a normal % you just write them like | like %{variable_name} and to output a normal % you just write them like | ||||||
| %%. You can output a newline by using \\n, a carrige return with \\r and a tab | %%. You can output a newline by using \\n, a carriage return with \\r and a tab | ||||||
| space with \\t. | space with \\t. | ||||||
|  |  | ||||||
| .B NOTE: | .B NOTE: | ||||||
| @@ -788,7 +788,7 @@ Internal error. A function was called in a bad order. | |||||||
| .IP 45 | .IP 45 | ||||||
| Interface error. A specified outgoing interface could not be used. | Interface error. A specified outgoing interface could not be used. | ||||||
| .IP 46 | .IP 46 | ||||||
| Bad password entered. An error was signalled when the password was entered. | Bad password entered. An error was signaled when the password was entered. | ||||||
| .IP 47 | .IP 47 | ||||||
| Too many redirects. When following redirects, curl hit the maximum amount. | Too many redirects. When following redirects, curl hit the maximum amount. | ||||||
| .IP 48 | .IP 48 | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| .\" nroff -man [file] | .\" nroff -man [file] | ||||||
| .\" $Id$ | .\" $Id$ | ||||||
| .\" | .\" | ||||||
| .TH curl_easy_init 3 "5 March 2001" "libcurl 7.6.1" "libcurl Manual" | .TH curl_easy_init 3 "31 Jan 2001" "libcurl 7.9.4" "libcurl Manual" | ||||||
| .SH NAME | .SH NAME | ||||||
| curl_easy_getinfo - Extract information from a curl session (added in 7.4) | curl_easy_getinfo - Extract information from a curl session (added in 7.4) | ||||||
| .SH SYNOPSIS | .SH SYNOPSIS | ||||||
| @@ -30,9 +30,11 @@ Pass a pointer to a long to receive the last received HTTP code. | |||||||
| .TP | .TP | ||||||
| .B CURLINFO_FILETIME | .B CURLINFO_FILETIME | ||||||
| Pass a pointer to a long to receive the remote time of the retrieved | Pass a pointer to a long to receive the remote time of the retrieved | ||||||
| document. If you get 0, it can be because of many reasons (unknown, the server | document. If you get -1, it can be because of many reasons (unknown, the | ||||||
| hides it or the server doesn't support the command that tells document time | server hides it or the server doesn't support the command that tells document | ||||||
| etc) and the time of the document is unknown. (Added in 7.5) | time etc) and the time of the document is unknown. Note that you must tell the | ||||||
|  | server to collect this information before the transfer is made, by using the | ||||||
|  | CURLOPT_FILETIME option to \fIcurl_easy_setopt(3)\fP. (Added in 7.5) | ||||||
| .TP | .TP | ||||||
| .B CURLINFO_TOTAL_TIME | .B CURLINFO_TOTAL_TIME | ||||||
| Pass a pointer to a double to receive the total transaction time in seconds | Pass a pointer to a double to receive the total transaction time in seconds | ||||||
| @@ -95,6 +97,12 @@ is the value read from the Content-Length: field.  (Added in 7.6.1) | |||||||
| .B CURLINFO_CONTENT_LENGTH_UPLOAD | .B CURLINFO_CONTENT_LENGTH_UPLOAD | ||||||
| Pass a pointer to a double to receive the specified size of the upload. | Pass a pointer to a double to receive the specified size of the upload. | ||||||
| (Added in 7.6.1) | (Added in 7.6.1) | ||||||
|  | .TP | ||||||
|  | .B CURLINFO_CONTENT_TYPE | ||||||
|  | Pass a pointer to a 'char *' to receive the content-type of the downloaded | ||||||
|  | object. This is the value read from the Content-Type: field. If you get NULL, | ||||||
|  | it means that the server didn't send a valid Content-Type header or that the | ||||||
|  | protocol used doesn't support this.  (Added in 7.9.4) | ||||||
| .PP | .PP | ||||||
|  |  | ||||||
| .SH RETURN VALUE | .SH RETURN VALUE | ||||||
|   | |||||||
| @@ -319,13 +319,54 @@ with \fIcurl_easy_cleanup(3)\fP. | |||||||
| .TP | .TP | ||||||
| .B CURLOPT_SSLCERT | .B CURLOPT_SSLCERT | ||||||
| Pass a pointer to a zero terminated string as parameter. The string should be | Pass a pointer to a zero terminated string as parameter. The string should be | ||||||
| the file name of your certificate in PEM format. | the file name of your certificate. The default format is "PEM" and can be | ||||||
|  | changed with \fICURLOPT_SSLCERTTYPE\fP. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSLCERTTYPE | ||||||
|  | Pass a pointer to a zero terminated string as parameter. The string should be | ||||||
|  | the format of your certificate. Supported formats are "PEM" and "DER". | ||||||
| .TP | .TP | ||||||
| .B CURLOPT_SSLCERTPASSWD | .B CURLOPT_SSLCERTPASSWD | ||||||
| Pass a pointer to a zero terminated string as parameter. It will be used as | Pass a pointer to a zero terminated string as parameter. It will be used as | ||||||
| the password required to use the CURLOPT_SSLCERT certificate. If the password | the password required to use the CURLOPT_SSLCERT certificate. If the password | ||||||
| is not supplied, you will be prompted for it. \fICURLOPT_PASSWDFUNCTION\fP can | is not supplied, you will be prompted for it. \fICURLOPT_PASSWDFUNCTION\fP can | ||||||
| be used to set your own prompt function. | be used to set your own prompt function. | ||||||
|  |  | ||||||
|  | \fBNOTE:\fPThis option is replaced by \fICURLOPT_SSLKEYPASSWD\fP and only | ||||||
|  | cept for backward compatibility. You never needed a pass phrase to load | ||||||
|  | a certificate but you need one to load your private key. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSLKEY | ||||||
|  | Pass a pointer to a zero terminated string as parameter. The string should be | ||||||
|  | the file name of your private key. The default format is "PEM" and can be | ||||||
|  | changed with \fICURLOPT_SSLKEYTYPE\fP. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSLKEYTYPE | ||||||
|  | Pass a pointer to a zero terminated string as parameter. The string should be | ||||||
|  | the format of your private key. Supported formats are "PEM", "DER" and "ENG". | ||||||
|  |  | ||||||
|  | \fBNOTE:\fPThe format "ENG" enables you to load the private key from a crypto | ||||||
|  | engine. in this case \fICURLOPT_SSLKEY\fP is used as an identifier passed to | ||||||
|  | the engine. You have to set the crypto engine with \fICURLOPT_SSL_ENGINE\fP. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSLKEYASSWD | ||||||
|  | Pass a pointer to a zero terminated string as parameter. It will be used as | ||||||
|  | the password required to use the \fICURLOPT_SSLKEY\fP private key. If the password | ||||||
|  | is not supplied, you will be prompted for it. \fICURLOPT_PASSWDFUNCTION\fP can | ||||||
|  | be used to set your own prompt function. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSL_ENGINE | ||||||
|  | Pass a pointer to a zero terminated string as parameter. It will be used as | ||||||
|  | the identifier for the crypto engine you want to use for your private key. | ||||||
|  |  | ||||||
|  | \fBNOTE:\fPIf the crypto device cannot be loaded, \fICURLE_SSL_ENGINE_NOTFOUND\fP | ||||||
|  | is returned. | ||||||
|  | .TP | ||||||
|  | .B CURLOPT_SSL_ENGINEDEFAULT | ||||||
|  | Sets the actual crypto engine as the default for (asymetric) crypto operations. | ||||||
|  |  | ||||||
|  | \fBNOTE:\fPIf the crypto device cannot be set, \fICURLE_SSL_ENGINE_SETFAILED\fP | ||||||
|  | is returned. | ||||||
| .TP | .TP | ||||||
| .B CURLOPT_CRLF | .B CURLOPT_CRLF | ||||||
| Convert Unix newlines to CRLF newlines on FTP uploads. | Convert Unix newlines to CRLF newlines on FTP uploads. | ||||||
|   | |||||||
| @@ -4,10 +4,10 @@ | |||||||
|  |  | ||||||
| AUTOMAKE_OPTIONS = foreign no-dependencies | AUTOMAKE_OPTIONS = foreign no-dependencies | ||||||
|  |  | ||||||
| EXTRA_DIST = README curlgtk.c sepheaders.c simple.c postit.c postit2.c \ | EXTRA_DIST = README curlgtk.c sepheaders.c simple.c postit2.c \ | ||||||
| 	     win32sockets.c persistant.c ftpget.c Makefile.example \ | 	     win32sockets.c persistant.c ftpget.c Makefile.example \ | ||||||
| 	     multithread.c getinmemory.c ftpupload.c httpput.c \ | 	     multithread.c getinmemory.c ftpupload.c httpput.c \ | ||||||
| 	     simplessl.c ftpgetresp.c | 	     simplessl.c ftpgetresp.c http-post.c | ||||||
|  |  | ||||||
| all: | all: | ||||||
| 	@echo "done" | 	@echo "done" | ||||||
|   | |||||||
| @@ -10,6 +10,10 @@ them for submission in future packages and on the web site. | |||||||
| The Makefile.example is an example makefile that could be used to build these | The Makefile.example is an example makefile that could be used to build these | ||||||
| examples. Just edit the file according to your system and requirements first. | examples. Just edit the file according to your system and requirements first. | ||||||
|  |  | ||||||
|  | Most examples should build fine using a command line like this: | ||||||
|  |  | ||||||
|  |   $ gcc `curl-config --cflags` `curl-config --libs` -o example example.c | ||||||
|  |  | ||||||
| Try the php/examples/ directory for PHP programming snippets! | Try the php/examples/ directory for PHP programming snippets! | ||||||
|  |  | ||||||
|   *PLEASE* do not use the curl.haxx.se site as a test target for your libcurl |   *PLEASE* do not use the curl.haxx.se site as a test target for your libcurl | ||||||
|   | |||||||
| @@ -14,31 +14,70 @@ | |||||||
| #include <curl/types.h> | #include <curl/types.h> | ||||||
| #include <curl/easy.h> | #include <curl/easy.h> | ||||||
|  |  | ||||||
| /* to make this work under windows, use the win32-functions from the | /* | ||||||
|    win32socket.c file as well */ |  * This is an example showing how to get a single file from an FTP server. | ||||||
|  |  * It delays the actual destination file creation until the first write | ||||||
|  |  * callback so that it won't create an empty file in case the remote file | ||||||
|  |  * doesn't exist or something else fails. | ||||||
|  |  */ | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | struct FtpFile { | ||||||
|  |   char *filename; | ||||||
|  |   FILE *stream; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) | ||||||
|  | { | ||||||
|  |   struct FtpFile *out=(struct FtpFile *)stream; | ||||||
|  |   if(out && !out->stream) { | ||||||
|  |     /* open file for writing */ | ||||||
|  |     out->stream=fopen(out->filename, "wb"); | ||||||
|  |     if(!out->stream) | ||||||
|  |       return -1; /* failure, can't open file to write */ | ||||||
|  |   } | ||||||
|  |   return fwrite(buffer, size, nmemb, out->stream); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int main(void) | ||||||
| { | { | ||||||
|   CURL *curl; |   CURL *curl; | ||||||
|   CURLcode res; |   CURLcode res; | ||||||
|   FILE *ftpfile; |   struct FtpFile ftpfile={ | ||||||
|  |     "curl.tar.gz", /* name to store the file as if succesful */ | ||||||
|  |     NULL | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   /* local file name to store the file as */ |   curl_global_init(CURL_GLOBAL_DEFAULT); | ||||||
|   ftpfile = fopen("curl.tar.gz", "wb"); /* b is binary for win */ |  | ||||||
|    |    | ||||||
|   curl = curl_easy_init(); |   curl = curl_easy_init(); | ||||||
|   if(curl) { |   if(curl) { | ||||||
|     /* Get curl 7.7 from sunet.se's FTP site: */ |     /* Get curl 7.9.2 from sunet.se's FTP site: */ | ||||||
|     curl_easy_setopt(curl, CURLOPT_URL, |     curl_easy_setopt(curl, CURLOPT_URL, | ||||||
|                      "ftp://ftp.sunet.se/pub/www/utilities/curl/curl-7.7.tar.gz"); |                      "ftp://ftp.sunet.se/pub/www/utilities/curl/curl-7.9.2.tar.gz"); | ||||||
|     curl_easy_setopt(curl, CURLOPT_FILE, ftpfile); |     /* Define our callback to get called when there's data to be written */ | ||||||
|  |     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); | ||||||
|  |     /* Set a pointer to our struct to pass to the callback */ | ||||||
|  |     curl_easy_setopt(curl, CURLOPT_FILE, &ftpfile); | ||||||
|  |  | ||||||
|  |     /* Switch on full protocol/debug output */ | ||||||
|  |     curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE); | ||||||
|  |  | ||||||
|     res = curl_easy_perform(curl); |     res = curl_easy_perform(curl); | ||||||
|  |  | ||||||
|     /* always cleanup */ |     /* always cleanup */ | ||||||
|     curl_easy_cleanup(curl); |     curl_easy_cleanup(curl); | ||||||
|  |  | ||||||
|  |     if(CURLE_OK != res) { | ||||||
|  |       /* we failed */ | ||||||
|  |       fprintf(stderr, "curl told us %d\n", res); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   fclose(ftpfile); /* close the local file */ |   if(ftpfile.stream) | ||||||
|  |     fclose(ftpfile.stream); /* close the local file */ | ||||||
|  |  | ||||||
|  |   curl_global_cleanup(); | ||||||
|  |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								docs/examples/http-post.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								docs/examples/http-post.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | /***************************************************************************** | ||||||
|  |  *                                  _   _ ____  _      | ||||||
|  |  *  Project                     ___| | | |  _ \| |     | ||||||
|  |  *                             / __| | | | |_) | |     | ||||||
|  |  *                            | (__| |_| |  _ <| |___  | ||||||
|  |  *                             \___|\___/|_| \_\_____| | ||||||
|  |  * | ||||||
|  |  * $Id$ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <curl/curl.h> | ||||||
|  |  | ||||||
|  | int main(void) | ||||||
|  | { | ||||||
|  |   CURL *curl; | ||||||
|  |   CURLcode res; | ||||||
|  |  | ||||||
|  |   curl = curl_easy_init(); | ||||||
|  |   if(curl) { | ||||||
|  |     /* First set the URL that is about to receive our POST. This URL can | ||||||
|  |        just as well be a https:// URL if that is what should receive the | ||||||
|  |        data. */ | ||||||
|  |     curl_easy_setopt(curl, CURLOPT_URL, "http://postit.example.com/moo.cgi"); | ||||||
|  |     /* Now specify the POST data */ | ||||||
|  |     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl"); | ||||||
|  |  | ||||||
|  |     /* Perform the request, res will get the return code */ | ||||||
|  |     res = curl_easy_perform(curl); | ||||||
|  |  | ||||||
|  |     /* always cleanup */ | ||||||
|  |     curl_easy_cleanup(curl); | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| /***************************************************************************** |  | ||||||
|  *                                  _   _ ____  _      |  | ||||||
|  *  Project                     ___| | | |  _ \| |     |  | ||||||
|  *                             / __| | | | |_) | |     |  | ||||||
|  *                            | (__| |_| |  _ <| |___  |  | ||||||
|  *                             \___|\___/|_| \_\_____| |  | ||||||
|  * |  | ||||||
|  * $Id$ |  | ||||||
|  * |  | ||||||
|  * Example code that uploads a file name 'foo' to a remote script that accepts |  | ||||||
|  * "HTML form based" (as described in RFC1738) uploads using HTTP POST. |  | ||||||
|  * |  | ||||||
|  * The imaginary form we'll fill in looks like: |  | ||||||
|  * |  | ||||||
|  * <form method="post" enctype="multipart/form-data" action="examplepost.cgi"> |  | ||||||
|  * Enter file: <input type="file" name="sendfile" size="40"> |  | ||||||
|  * Enter file name: <input type="text" name="filename" size="30"> |  | ||||||
|  * <input type="submit" value="send" name="submit"> |  | ||||||
|  * </form> |  | ||||||
|  * |  | ||||||
|  * This exact source code has not been verified to work. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /* to make this work under windows, use the win32-functions from the |  | ||||||
|    win32socket.c file as well */ |  | ||||||
|  |  | ||||||
| #include <stdio.h> |  | ||||||
|  |  | ||||||
| #include <curl/curl.h> |  | ||||||
| #include <curl/types.h> |  | ||||||
| #include <curl/easy.h> |  | ||||||
|  |  | ||||||
| int main(int argc, char **argv) |  | ||||||
| { |  | ||||||
|   CURL *curl; |  | ||||||
|   CURLcode res; |  | ||||||
|  |  | ||||||
|   struct HttpPost *formpost=NULL; |  | ||||||
|   struct HttpPost *lastptr=NULL; |  | ||||||
|  |  | ||||||
|   /* Fill in the file upload field */ |  | ||||||
|   curl_formparse("sendfile=@foo", |  | ||||||
|                  &formpost, |  | ||||||
|                  &lastptr); |  | ||||||
|  |  | ||||||
|   /* Fill in the filename field */ |  | ||||||
|   curl_formparse("filename=foo", |  | ||||||
|                  &formpost, |  | ||||||
|                  &lastptr); |  | ||||||
|    |  | ||||||
|  |  | ||||||
|   /* Fill in the submit field too, even if this is rarely needed */ |  | ||||||
|   curl_formparse("submit=send", |  | ||||||
|                  &formpost, |  | ||||||
|                  &lastptr); |  | ||||||
|  |  | ||||||
|   curl = curl_easy_init(); |  | ||||||
|   if(curl) { |  | ||||||
|     /* what URL that receives this POST */ |  | ||||||
|     curl_easy_setopt(curl, CURLOPT_URL, "http://curl.haxx.se/examplepost.cgi"); |  | ||||||
|     curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); |  | ||||||
|     res = curl_easy_perform(curl); |  | ||||||
|  |  | ||||||
|     /* always cleanup */ |  | ||||||
|     curl_easy_cleanup(curl); |  | ||||||
|  |  | ||||||
|     /* then cleanup the formpost chain */ |  | ||||||
|     curl_formfree(formpost); |  | ||||||
|   } |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| @@ -9,27 +9,16 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  |  | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
| #include <curl/types.h> |  | ||||||
| #include <curl/easy.h> |  | ||||||
|  |  | ||||||
| /* to make this work under windows, use the win32-functions from the | int main(void) | ||||||
|    win32socket.c file as well */ |  | ||||||
|  |  | ||||||
| int main(int argc, char **argv) |  | ||||||
| { | { | ||||||
|   CURL *curl; |   CURL *curl; | ||||||
|   CURLcode res; |   CURLcode res; | ||||||
|   FILE *headerfile; |  | ||||||
|  |  | ||||||
|   headerfile = fopen("dumpit", "w"); |  | ||||||
|  |  | ||||||
|   curl = curl_easy_init(); |   curl = curl_easy_init(); | ||||||
|   if(curl) { |   if(curl) { | ||||||
|     /* what call to write: */ |  | ||||||
|     curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se"); |     curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se"); | ||||||
|     curl_easy_setopt(curl, CURLOPT_WRITEHEADER, headerfile); |  | ||||||
|     res = curl_easy_perform(curl); |     res = curl_easy_perform(curl); | ||||||
|  |  | ||||||
|     /* always cleanup */ |     /* always cleanup */ | ||||||
|   | |||||||
| @@ -28,6 +28,9 @@ | |||||||
|    4.2. if the format of the key file is DER, set pKeyType to "DER" |    4.2. if the format of the key file is DER, set pKeyType to "DER" | ||||||
|  |  | ||||||
|    !! verify of the server certificate is not implemented here !! |    !! verify of the server certificate is not implemented here !! | ||||||
|  |  | ||||||
|  |    **** This example only works with libcurl 7.9.3 and later! **** | ||||||
|  |  | ||||||
| */ | */ | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| @@ -37,6 +40,7 @@ int main(int argc, char **argv) | |||||||
|   FILE *headerfile; |   FILE *headerfile; | ||||||
|  |  | ||||||
|   const char *pCertFile = "testcert.pem"; |   const char *pCertFile = "testcert.pem"; | ||||||
|  |   const char *pCACertFile="cacert.pem" | ||||||
|  |  | ||||||
|   const char *pKeyName; |   const char *pKeyName; | ||||||
|   const char *pKeyType; |   const char *pKeyType; | ||||||
| @@ -96,6 +100,10 @@ int main(int argc, char **argv) | |||||||
|        curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE,pKeyType); |        curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE,pKeyType); | ||||||
|                                 /* set the private key (file or ID in engine) */ |                                 /* set the private key (file or ID in engine) */ | ||||||
|        curl_easy_setopt(curl,CURLOPT_SSLKEY,pKeyName); |        curl_easy_setopt(curl,CURLOPT_SSLKEY,pKeyName); | ||||||
|  |                                 /* set the file with the certs vaildating the server */ | ||||||
|  |        curl_easy_setopt(curl,CURLOPT_CAINFO,pCACertFile); | ||||||
|  |                                 /* disconnect if we can't validate server's cert */ | ||||||
|  |        curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1); | ||||||
|         |         | ||||||
|        res = curl_easy_perform(curl); |        res = curl_easy_perform(curl); | ||||||
|        break;                   /* we are done... */ |        break;                   /* we are done... */ | ||||||
|   | |||||||
							
								
								
									
										824
									
								
								docs/libcurl-the-guide
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										824
									
								
								docs/libcurl-the-guide
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,824 @@ | |||||||
|  | $Id$ | ||||||
|  |                                   _   _ ____  _      | ||||||
|  |                               ___| | | |  _ \| |     | ||||||
|  |                              / __| | | | |_) | |     | ||||||
|  |                             | (__| |_| |  _ <| |___  | ||||||
|  |                              \___|\___/|_| \_\_____| | ||||||
|  |  | ||||||
|  | PROGRAMMING WITH LIBCURL | ||||||
|  |  | ||||||
|  | About this Document | ||||||
|  |  | ||||||
|  |  This document will attempt to describe the general principle and some basic | ||||||
|  |  approaches to consider when programming with libcurl. The text will focus | ||||||
|  |  mainly on the C interface but might apply fairly well on other interfaces as | ||||||
|  |  well as they usually follow the C one pretty closely. | ||||||
|  |  | ||||||
|  |  This document will refer to 'the user' as the person writing the source code | ||||||
|  |  that uses libcurl. That would probably be you or someone in your position. | ||||||
|  |  What will be generally refered to as 'the program' will be the collected | ||||||
|  |  source code that you write that is using libcurl for transfers. The program | ||||||
|  |  is outside libcurl and libcurl is outside of the program. | ||||||
|  |  | ||||||
|  |  To get the more details on all options and functions described herein, please | ||||||
|  |  refer to their respective man pages. | ||||||
|  |  | ||||||
|  | Building | ||||||
|  |  | ||||||
|  |  There are many different ways to build C programs. This chapter will assume a | ||||||
|  |  unix-style build process. If you use a different build system, you can still | ||||||
|  |  read this to get general information that may apply to your environment as | ||||||
|  |  well. | ||||||
|  |  | ||||||
|  |   Compiling the Program | ||||||
|  |  | ||||||
|  |     Your compiler needs to know where the libcurl headers are | ||||||
|  |     located. Therefore you must set your compiler's include path to point to | ||||||
|  |     the directory where you installed them. The 'curl-config'[3] tool can be | ||||||
|  |     used to get this information: | ||||||
|  |  | ||||||
|  |         $ curl-config --cflags | ||||||
|  |  | ||||||
|  |   Linking the Program with libcurl | ||||||
|  |  | ||||||
|  |     When having compiled the program, you need to link your object files to | ||||||
|  |     create a single executable. For that to succeed, you need to link with | ||||||
|  |     libcurl and possibly also with other libraries that libcurl itself depends | ||||||
|  |     on. Like OpenSSL librararies, but even some standard OS libraries may be | ||||||
|  |     needed on the command line. To figure out which flags to use, once again | ||||||
|  |     the 'curl-config' tool comes to the rescue: | ||||||
|  |  | ||||||
|  |         $ curl-config --libs | ||||||
|  |  | ||||||
|  |   SSL or Not | ||||||
|  |  | ||||||
|  |     libcurl can be built and customized in many ways. One of the things that | ||||||
|  |     varies from different libraries and builds is the support for SSL-based | ||||||
|  |     transfers, like HTTPS and FTPS. If OpenSSL was detected properly at | ||||||
|  |     build-time, libcurl will be built with SSL support. To figure out if an | ||||||
|  |     installed libcurl has been built with SSL support enabled, use | ||||||
|  |     'curl-config' like this: | ||||||
|  |  | ||||||
|  |         $ curl-config --feature | ||||||
|  |  | ||||||
|  |     And if SSL is supported, the keyword 'SSL' will be written to stdout, | ||||||
|  |     possibly together with a few other features that can be on and off on | ||||||
|  |     different libcurls. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Portable Code in a Portable World | ||||||
|  |  | ||||||
|  |  The people behind libcurl have put a considerable effort to make libcurl work | ||||||
|  |  on a large amount of different operating systems and environments. | ||||||
|  |  | ||||||
|  |  You program libcurl the same way on all platforms that libcurl runs on. There | ||||||
|  |  are only very few minor considerations that differs. If you just make sure to | ||||||
|  |  write your code portable enough, you may very well create yourself a very | ||||||
|  |  portable program. libcurl shouldn't stop you from that. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Global Preparation | ||||||
|  |  | ||||||
|  |  The program must initialize some of the libcurl functionality globally. That | ||||||
|  |  means it should be done exactly once, no matter how many times you intend to | ||||||
|  |  use the library. Once for your program's entire life time. This is done using | ||||||
|  |  | ||||||
|  |     curl_global_init() | ||||||
|  |  | ||||||
|  |  and it takes one parameter which is a bit pattern that tells libcurl what to | ||||||
|  |  intialize. Using CURL_GLOBAL_ALL will make it initialize all known internal | ||||||
|  |  sub modules, and might be a good default option. The current two bits that | ||||||
|  |  are specified are: | ||||||
|  |  | ||||||
|  |   CURL_GLOBAL_WIN32 which only does anything on Windows machines. When used on | ||||||
|  |   a Windows machine, it'll make libcurl intialize the win32 socket | ||||||
|  |   stuff. Without having that initialized properly, your program cannot use | ||||||
|  |   sockets properly. You should only do this once for each application, so if | ||||||
|  |   your program already does this or of another library in use does it, you | ||||||
|  |   should not tell libcurl to do this as well. | ||||||
|  |  | ||||||
|  |   CURL_GLOBAL_SSL which only does anything on libcurls compiled and built | ||||||
|  |   SSL-enabled. On these systems, this will make libcurl init OpenSSL properly | ||||||
|  |   for this application. This is only needed to do once for each application so | ||||||
|  |   if your program or another library already does this, this bit should not be | ||||||
|  |   needed. | ||||||
|  |  | ||||||
|  |  libcurl has a default protection mechanism that detects if curl_global_init() | ||||||
|  |  hasn't been called by the time curl_easy_perform() is called and if that is | ||||||
|  |  the case, libcurl runs the function itself with a guessed bit pattern. Please | ||||||
|  |  note that depending solely on this is not considered nice nor very good. | ||||||
|  |  | ||||||
|  |  When the program no longer uses libcurl, it should call | ||||||
|  |  curl_global_cleanup(), which is the opposite of the init call. It will then | ||||||
|  |  do the reversed operations to cleanup the resources the curl_global_init() | ||||||
|  |  call initialized. | ||||||
|  |  | ||||||
|  |  Repeated calls to curl_global_init() and curl_global_cleanup() should be | ||||||
|  |  avoided. They should be called once each. | ||||||
|  |  | ||||||
|  | Handle the Easy libcurl | ||||||
|  |  | ||||||
|  |  libcurl version 7 is oriented around the so called easy interface. All | ||||||
|  |  operations in the easy interface are prefixed with 'curl_easy'. | ||||||
|  |  | ||||||
|  |  Future libcurls will also offer the multi interface. More about that | ||||||
|  |  interface, what it is targeted for and how to use it is still only debated on | ||||||
|  |  the libcurl mailing list and developer web pages. Join up to discuss and | ||||||
|  |  figure out! | ||||||
|  |  | ||||||
|  |  To use the easy interface, you must first create yourself an easy handle. You | ||||||
|  |  need one handle for each easy session you want to perform. Basicly, you | ||||||
|  |  should use one handle for every thread you plan to use for transferring. You | ||||||
|  |  must never share the same handle in multiple threads. | ||||||
|  |  | ||||||
|  |  Get an easy handle with | ||||||
|  |  | ||||||
|  |     easyhandle = curl_easy_init(); | ||||||
|  |  | ||||||
|  |  It returns an easy handle. Using that you proceed to the next step: setting | ||||||
|  |  up your preferred actions. A handle is just a logic entity for the upcoming | ||||||
|  |  transfer or series of transfers. | ||||||
|  |  | ||||||
|  |  You set properties and options for this handle using curl_easy_setopt(). They | ||||||
|  |  control how the subsequent transfer or transfers will be made. Options remain | ||||||
|  |  set in the handle until set again to something different. Alas, multiple | ||||||
|  |  requests using the same handle will use the same options. | ||||||
|  |  | ||||||
|  |  Many of the informationals you set in libcurl are "strings", pointers to data | ||||||
|  |  terminated with a zero byte. Keep in mind that when you set strings with | ||||||
|  |  curl_easy_setopt(), libcurl will not copy the data. It will merely point to | ||||||
|  |  the data. You MUST make sure that the data remains available for libcurl to | ||||||
|  |  use until finished or until you use the same option again to point to | ||||||
|  |  something else. | ||||||
|  |  | ||||||
|  |  One of the most basic properties to set in the handle is the URL. You set | ||||||
|  |  your preferred URL to transfer with CURLOPT_URL in a manner similar to: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_URL, "http://curl.haxx.se/"); | ||||||
|  |  | ||||||
|  |  Let's assume for a while that you want to receive data as the URL indentifies | ||||||
|  |  a remote resource you want to get here. Since you write a sort of application | ||||||
|  |  that needs this transfer, I assume that you would like to get the data passed | ||||||
|  |  to you directly instead of simply getting it passed to stdout. So, you write | ||||||
|  |  your own function that matches this prototype: | ||||||
|  |  | ||||||
|  |     size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); | ||||||
|  |  | ||||||
|  |  You tell libcurl to pass all data to this function by issuing a function | ||||||
|  |  similar to this: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data); | ||||||
|  |  | ||||||
|  |  You can control what data your function get in the forth argument by setting | ||||||
|  |  another property: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_FILE, &internal_struct); | ||||||
|  |  | ||||||
|  |  Using that property, you can easily pass local data between your application | ||||||
|  |  and the function that gets invoked by libcurl. libcurl itself won't touch the | ||||||
|  |  data you pass with CURLOPT_FILE. | ||||||
|  |  | ||||||
|  |  libcurl offers its own default internal callback that'll take care of the | ||||||
|  |  data if you don't set the callback with CURLOPT_WRITEFUNCTION. It will then | ||||||
|  |  simply output the received data to stdout. You can have the default callback | ||||||
|  |  write the data to a different file handle by passing a 'FILE *' to a file | ||||||
|  |  opened for writing with the CURLOPT_FILE option. | ||||||
|  |  | ||||||
|  |  Now, we need to take a step back and have a deep breath. Here's one of those | ||||||
|  |  rare platform-dependent nitpicks. Did you spot it? On some platforms[2], | ||||||
|  |  libcurl won't be able to operate on files opened by the program. Thus, if you | ||||||
|  |  use the default callback and pass in a an open file with CURLOPT_FILE, it | ||||||
|  |  will crash. You should therefore avoid this to make your program run fine | ||||||
|  |  virtually everywhere. | ||||||
|  |  | ||||||
|  |  There are of course many more options you can set, and we'll get back to a | ||||||
|  |  few of them later. Let's instead continue to the actual transfer: | ||||||
|  |  | ||||||
|  |     success = curl_easy_perform(easyhandle); | ||||||
|  |  | ||||||
|  |  The curl_easy_perform() will connect to the remote site, do the necessary | ||||||
|  |  commands and receive the transfer. Whenever it receives data, it calls the | ||||||
|  |  callback function we previously set. The function may get one byte at a time, | ||||||
|  |  or it may get many kilobytes at once. libcurl delivers as much as possible as | ||||||
|  |  often as possible. Your callback function should return the number of bytes | ||||||
|  |  it "took care of". If that is not the exact same amount of bytes that was | ||||||
|  |  passed to it, libcurl will abort the operation and return with an error code. | ||||||
|  |  | ||||||
|  |  When the transfer is complete, the function returns a return code that | ||||||
|  |  informs you if it succeeded in its mission or not. If a return code isn't | ||||||
|  |  enough for you, you can use the CURLOPT_ERRORBUFFER to point libcurl to a | ||||||
|  |  buffer of yours where it'll store a human readable error message as well. | ||||||
|  |  | ||||||
|  |  If you then want to transfer another file, the handle is ready to be used | ||||||
|  |  again. Mind you, it is even preferred that you re-use an existing handle if | ||||||
|  |  you intend to make another transfer. libcurl will then attempt to re-use the | ||||||
|  |  previous | ||||||
|  |  | ||||||
|  |  | ||||||
|  | When It Doesn't Work | ||||||
|  |  | ||||||
|  |  There will always be times when the transfer fails for some reason. You might | ||||||
|  |  have set the wrong libcurl option or misunderstood what the libcurl option | ||||||
|  |  actually does, or the remote server might return non-standard replies that | ||||||
|  |  confuse the library which then confuses your program. | ||||||
|  |  | ||||||
|  |  There's one golden rule when these things occur: set the CURLOPT_VERBOSE | ||||||
|  |  option to TRUE. It'll cause the library to spew out the entire protocol | ||||||
|  |  details it sends, some internal info and some received protcol data as well | ||||||
|  |  (especially when using FTP). If you're using HTTP, adding the headers in the | ||||||
|  |  received output to study is also a clever way to get a better understanding | ||||||
|  |  wht the server behaves the way it does. Include headers in the normal body | ||||||
|  |  output with CURLOPT_HEADER set TRUE. | ||||||
|  |  | ||||||
|  |  Of course there are bugs left. We need to get to know about them to be able | ||||||
|  |  to fix them, so we're quite dependent on your bug reports! When you do report | ||||||
|  |  suspected bugs in libcurl, please include as much details you possibly can: a | ||||||
|  |  protocol dump that CURLOPT_VERBOSE produces, library version, as much as | ||||||
|  |  possible of your code that uses libcurl, operating system name and version, | ||||||
|  |  compiler name and version etc. | ||||||
|  |  | ||||||
|  |  Getting some in-depth knowledge about the protocols involved is never wrong, | ||||||
|  |  and if you're trying to do funny things, you might very well understand | ||||||
|  |  libcurl and how to use it better if you study the appropriate RFC documents | ||||||
|  |  at least briefly. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Upload Data to a Remote Site | ||||||
|  |  | ||||||
|  |  libcurl tries to keep a protocol independent approach to most transfers, thus | ||||||
|  |  uploading to a remote FTP site is very similar to uploading data to a HTTP | ||||||
|  |  server with a PUT request. | ||||||
|  |  | ||||||
|  |  Of course, first you either create an easy handle or you re-use one existing | ||||||
|  |  one. Then you set the URL to operate on just like before. This is the remote | ||||||
|  |  URL, that we now will upload. | ||||||
|  |  | ||||||
|  |  Since we write an application, we most likely want libcurl to get the upload | ||||||
|  |  data by asking us for it. To make it do that, we set the read callback and | ||||||
|  |  the custom pointer libcurl will pass to our read callback. The read callback | ||||||
|  |  should have a prototype similar to: | ||||||
|  |  | ||||||
|  |     size_t function(char *bufptr, size_t size, size_t nitems, void *userp); | ||||||
|  |  | ||||||
|  |  Where bufptr is the pointer to a buffer we fill in with data to upload and | ||||||
|  |  size*nitems is the size of the buffer and therefore also the maximum amount | ||||||
|  |  of data we can return to libcurl in this call. The 'userp' pointer is the | ||||||
|  |  custom pointer we set to point to a struct of ours to pass private data | ||||||
|  |  between the application and the callback. | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_INFILE, &filedata); | ||||||
|  |  | ||||||
|  |  Tell libcurl that we want to upload: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, TRUE); | ||||||
|  |  | ||||||
|  |  A few protocols won't behave properly when uploads are done without any prior | ||||||
|  |  knowledge of the expected file size. HTTP PUT is one example [1]. So, set the | ||||||
|  |  upload file size using the CURLOPT_INFILESIZE like this: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE, file_size); | ||||||
|  |  | ||||||
|  |  When you call curl_easy_perform() this time, it'll perform all the necessary | ||||||
|  |  operations and when it has invoked the upload it'll call your supplied | ||||||
|  |  callback to get the data to upload. The program should return as much data as | ||||||
|  |  possible in every invoke, as that is likely to make the upload perform as | ||||||
|  |  fast as possible. The callback should return the number of bytes it wrote in | ||||||
|  |  the buffer. Returning 0 will signal the end of the upload. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Passwords | ||||||
|  |  | ||||||
|  |  Many protocols use or even require that user name and password are provided | ||||||
|  |  to be able to download or upload the data of your choice. libcurl offers | ||||||
|  |  several ways to specify them. | ||||||
|  |  | ||||||
|  |  Most protocols support that you specify the name and password in the URL | ||||||
|  |  itself. libcurl will detect this and use them accordingly. This is written | ||||||
|  |  like this: | ||||||
|  |  | ||||||
|  |         protocol://user:password@example.com/path/ | ||||||
|  |  | ||||||
|  |  If you need any odd letters in your user name or password, you should enter | ||||||
|  |  them URL encoded, as %XX where XX is a two-digit hexadecimal number. | ||||||
|  |  | ||||||
|  |  libcurl also provides options to set various passwords. The user name and | ||||||
|  |  password as shown embedded in the URL can instead get set with the | ||||||
|  |  CURLOPT_USERPWD option. The argument passed to libcurl should be a char * to | ||||||
|  |  a string in the format "user:password:". In a manner like this: | ||||||
|  |  | ||||||
|  |         curl_easy_setopt(easyhandle, CURLOPT_USERPWD, "myname:thesecret"); | ||||||
|  |  | ||||||
|  |  Another case where name and password might be needed at times, is for those | ||||||
|  |  users who need to athenticate themselves to a proxy they use. libcurl offers | ||||||
|  |  another option for this, the CURLOPT_PROXYUSERPWD. It is used quite similar | ||||||
|  |  to the CURLOPT_USERPWD option like this: | ||||||
|  |  | ||||||
|  |         curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "myname:thesecret"); | ||||||
|  |   | ||||||
|  |  There's a long time unix "standard" way of storing ftp user names and | ||||||
|  |  passwords, namely in the $HOME/.netrc file. The file should be made private | ||||||
|  |  so that only the user may read it (see also the "Security Considerations" | ||||||
|  |  chapter), as it might contain the password in plain text. libcurl has the | ||||||
|  |  ability to use this file to figure out what set of user name and password to | ||||||
|  |  use for a particular host. As an extension to the normal functionality, | ||||||
|  |  libcurl also supports this file for non-FTP protocols such as HTTP. To make | ||||||
|  |  curl use this file, use the CURLOPT_NETRC option: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_NETRC, TRUE); | ||||||
|  |  | ||||||
|  |  And a very basic example of how such a .netrc file may look like: | ||||||
|  |  | ||||||
|  |     machine myhost.mydomain.com | ||||||
|  |     login userlogin | ||||||
|  |     password secretword | ||||||
|  |  | ||||||
|  |  All these examples have been cases where the password has been optional, or | ||||||
|  |  at least you could leave it out and have libcurl attempt to do its job | ||||||
|  |  without it. There are times when the password isn't optional, like when | ||||||
|  |  you're using an SSL private key for secure transfers. | ||||||
|  |  | ||||||
|  |  You can in this situation either pass a password to libcurl to use to unlock | ||||||
|  |  the private key, or you can let libcurl prompt the user for it. If you prefer | ||||||
|  |  to ask the user, then you can provide your own callback function that will be | ||||||
|  |  called when libcurl wants the password. That way, you can control how the | ||||||
|  |  question will appear to the user. | ||||||
|  |  | ||||||
|  |  To pass the known private key password to libcurl: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_SSLKEYPASSWD, "keypassword"); | ||||||
|  |  | ||||||
|  |  To make a password callback: | ||||||
|  |  | ||||||
|  |     int enter_passwd(void *ourp, const char *prompt, char *buffer, int len); | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_PASSWDFUNCTION, enter_passwd); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | HTTP POSTing | ||||||
|  |  | ||||||
|  |  We get many questions regarding how to issue HTTP POSTs with libcurl the | ||||||
|  |  proper way. This chapter will thus include examples using both different | ||||||
|  |  versions of HTTP POST that libcurl supports. | ||||||
|  |  | ||||||
|  |  The first version is the simple POST, the most common version, that most HTML | ||||||
|  |  pages using the <form> tag uses. We provide a pointer to the data and tell | ||||||
|  |  libcurl to post it all to the remote site: | ||||||
|  |  | ||||||
|  |     char *data="name=daniel&project=curl"; | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data); | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_URL, "http://posthere.com/"); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* post away! */ | ||||||
|  |  | ||||||
|  |  Simple enough, huh? Since you set the POST options with the | ||||||
|  |  CURLOPT_POSTFIELDS, this automaticly switches the handle to use POST in the | ||||||
|  |  upcoming request. | ||||||
|  |  | ||||||
|  |  Ok, so what if you want to post binary data that also requires you to set the | ||||||
|  |  Content-Type: header of the post? Well, binary posts prevents libcurl from | ||||||
|  |  being able to do strlen() on the data to figure out the size, so therefore we | ||||||
|  |  must tell libcurl the size of the post data. Setting headers in libcurl | ||||||
|  |  requests are done in a generic way, by building a list of our own headers and | ||||||
|  |  then passing that list to libcurl. | ||||||
|  |  | ||||||
|  |     struct curl_slist *headers=NULL; | ||||||
|  |     headers = curl_slist_append(headers, "Content-Type: text/xml"); | ||||||
|  |  | ||||||
|  |     /* post binary data */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_POSTFIELD, binaryptr); | ||||||
|  |  | ||||||
|  |     /* set the size of the postfields data */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23); | ||||||
|  |  | ||||||
|  |     /* pass our list of custom made headers */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* post away! */ | ||||||
|  |  | ||||||
|  |     curl_slist_free_all(headers); /* free the header list */ | ||||||
|  |  | ||||||
|  |  While the simple examples above cover the majority of all cases where HTTP | ||||||
|  |  POST operations are required, they don't do multipart formposts. Multipart | ||||||
|  |  formposts were introduced as a better way to post (possibly large) binary | ||||||
|  |  data and was first documented in the RFC1867. They're called multipart | ||||||
|  |  because they're built by a chain of parts, each being a single unit. Each | ||||||
|  |  part has its own name and contents. You can in fact create and post a | ||||||
|  |  multipart formpost with the regular libcurl POST support described above, but | ||||||
|  |  that would require that you build a formpost yourself and provide to | ||||||
|  |  libcurl. To make that easier, libcurl provides curl_formadd(). Using this | ||||||
|  |  function, you add parts to the form. When you're done adding parts, you post | ||||||
|  |  the whole form. | ||||||
|  |  | ||||||
|  |  The following example sets two simple text parts with plain textual contents, | ||||||
|  |  and then a file with binary contents and upload the whole thing. | ||||||
|  |  | ||||||
|  |     struct HttpPost *post=NULL; | ||||||
|  |     struct HttpPost *last=NULL; | ||||||
|  |     curl_formadd(&post, &last, | ||||||
|  |                  CURLFORM_COPYNAME, "name", | ||||||
|  |                  CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); | ||||||
|  |     curl_formadd(&post, &last, | ||||||
|  |                  CURLFORM_COPYNAME, "project", | ||||||
|  |                  CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); | ||||||
|  |     curl_formadd(&post, &last, | ||||||
|  |                  CURLFORM_COPYNAME, "logotype-image", | ||||||
|  |                  CURLFORM_FILECONTENT, "curl.png", CURLFORM_END); | ||||||
|  |  | ||||||
|  |     /* Set the form info */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_HTTPPOST, post); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* post away! */ | ||||||
|  |  | ||||||
|  |     /* free the post data again */ | ||||||
|  |     curl_formfree(post); | ||||||
|  |  | ||||||
|  |  Multipart formposts are chains of parts using MIME-style separators and | ||||||
|  |  headers. It means that each one of these separate parts get a few headers set | ||||||
|  |  that describe the individual content-type, size etc. To enable your | ||||||
|  |  application to handicraft this formpost even more, libcurl allows you to | ||||||
|  |  supply your own set of custom headers to such an individual form part. You | ||||||
|  |  can of course supply headers to as many parts you like, but this little | ||||||
|  |  example will show how you set headers to one specific part when you add that | ||||||
|  |  to the post handle: | ||||||
|  |  | ||||||
|  |     struct curl_slist *headers=NULL; | ||||||
|  |     headers = curl_slist_append(headers, "Content-Type: text/xml"); | ||||||
|  |  | ||||||
|  |     curl_formadd(&post, &last, | ||||||
|  |                  CURLFORM_COPYNAME, "logotype-image", | ||||||
|  |                  CURLFORM_FILECONTENT, "curl.xml", | ||||||
|  |                  CURLFORM_CONTENTHEADER, headers, | ||||||
|  |                  CURLFORM_END); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* post away! */ | ||||||
|  |  | ||||||
|  |     curl_formfree(post); /* free post */ | ||||||
|  |     curl_slist_free_all(post); /* free custom header list */ | ||||||
|  |  | ||||||
|  |  Since all options on an easyhandle are "sticky", they remain the same until | ||||||
|  |  changed even if you do call curl_easy_perform(), you may need to tell curl to | ||||||
|  |  go back to a plain GET request if you intend to do such a one as your next | ||||||
|  |  request. You force an easyhandle to back to GET by using the CURLOPT_HTTPGET | ||||||
|  |  option: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, TRUE); | ||||||
|  |  | ||||||
|  |  Just setting CURLOPT_POSTFIELDS to "" or NULL will *not* stop libcurl from | ||||||
|  |  doing a POST. It will just make it POST without any data to send! | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Showing Progress | ||||||
|  |  | ||||||
|  |  [ built-in progress meter, progress callback ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | libcurl with C++ | ||||||
|  |  | ||||||
|  |  There's basicly only one thing to keep in mind when using C++ instead of C | ||||||
|  |  when interfacing libcurl: | ||||||
|  |  | ||||||
|  |     "The Callbacks Must Be Plain C" | ||||||
|  |  | ||||||
|  |  So if you want a write callback set in libcurl, you should put it within | ||||||
|  |  'extern'. Similar to this: | ||||||
|  |  | ||||||
|  |      extern "C" { | ||||||
|  |        size_t write_data(void *ptr, size_t size, size_t nmemb, | ||||||
|  |                          void *ourpointer) | ||||||
|  |        { | ||||||
|  |          /* do what you want with the data */ | ||||||
|  |        } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  This will of course effectively turn the callback code into C. There won't be | ||||||
|  |  any "this" pointer available etc. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Proxies | ||||||
|  |  | ||||||
|  |  What "proxy" means according to Merriam-Webster: "a person authorized to act | ||||||
|  |  for another" but also "the agency, function, or office of a deputy who acts | ||||||
|  |  as a substitute for another". | ||||||
|  |  | ||||||
|  |  Proxies are exceedingly common these days. Companies often only offer | ||||||
|  |  internet access to employees through their HTTP proxies. Network clients or | ||||||
|  |  user-agents ask the proxy for docuements, the proxy does the actual request | ||||||
|  |  and then it returns them. | ||||||
|  |  | ||||||
|  |  libcurl has full support for HTTP proxies, so when a given URL is wanted, | ||||||
|  |  libcurl will ask the proxy for it instead of trying to connect to the actual | ||||||
|  |  host identified in the URL. | ||||||
|  |  | ||||||
|  |  The fact that the proxy is a HTTP proxy puts certain restrictions on what can | ||||||
|  |  actually happen. A requested URL that might not be a HTTP URL will be still | ||||||
|  |  be passed to the HTTP proxy to deliver back to libcurl. This happens | ||||||
|  |  transparantly, and an application may not need to know. I say "may", because | ||||||
|  |  at times it is very important to understand that all operations over a HTTP | ||||||
|  |  proxy is using the HTTP protocol. For example, you can't invoke your own | ||||||
|  |  custom FTP commands or even proper FTP directory listings. | ||||||
|  |  | ||||||
|  |   Proxy Options | ||||||
|  |  | ||||||
|  |     To tell libcurl to use a proxy at a given port number: | ||||||
|  |  | ||||||
|  |        curl_easy_setopt(easyhandle, CURLOPT_PROXY, "proxy-host.com:8080"); | ||||||
|  |  | ||||||
|  |     Some proxies require user authentication before allowing a request, and | ||||||
|  |     you pass that information similar to this: | ||||||
|  |  | ||||||
|  |        curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "user:password"); | ||||||
|  |  | ||||||
|  |     If you want to, you can specify the host name only in the CURLOPT_PROXY | ||||||
|  |     option, and set the port number separately with CURLOPT_PROXYPORT. | ||||||
|  |  | ||||||
|  |   Environment Variables | ||||||
|  |  | ||||||
|  |     libcurl automaticly checks and uses a set of environment variables to know | ||||||
|  |     what proxies to use for certain protocols. The names of the variables are | ||||||
|  |     following an ancient de facto standard and are built up as | ||||||
|  |     "[protocol]_proxy" (note the lower casing). Which makes the variable | ||||||
|  |     'http_proxy' checked for a name of a proxy to use when the input URL is | ||||||
|  |     HTTP. Following the same rule, the variable named 'ftp_proxy' is checked | ||||||
|  |     for FTP URLs. Again, the proxies are always HTTP proxies, the different | ||||||
|  |     names of the variables simply allows different HTTP proxies to be used. | ||||||
|  |  | ||||||
|  |     The proxy environment variable contents should be in the format | ||||||
|  |     "[protocol://]machine[:port]". Where the protocol:// part is simply | ||||||
|  |     ignored if present (so http://proxy and bluerk://proxy will do the same) | ||||||
|  |     and the optional port number specifies on which port the proxy operates on | ||||||
|  |     the host. If not specified, the internal default port number will be used | ||||||
|  |     and that is most likely *not* the one you would like it to be. | ||||||
|  |  | ||||||
|  |     There are two special environment variables. 'all_proxy' is what sets | ||||||
|  |     proxy for any URL in case the protocol specific variable wasn't set, and | ||||||
|  |     'no_proxy' defines a list of hosts that should not use a proxy even though | ||||||
|  |     a variable may say so. If 'no_proxy' is a plain asterisk ("*") it matches | ||||||
|  |     all hosts. | ||||||
|  |  | ||||||
|  |   SSL and Proxies | ||||||
|  |  | ||||||
|  |     SSL is for secure point-to-point connections. This involves strong | ||||||
|  |     encryption and similar things, which effectivly makes it impossible for a | ||||||
|  |     proxy to operate as a "man in between" which the proxy's task is, as | ||||||
|  |     previously discussed. Instead, the only way to have SSL work over a HTTP | ||||||
|  |     proxy is to ask the proxy to tunnel trough everything without being able | ||||||
|  |     to check or fiddle with the traffic. | ||||||
|  |  | ||||||
|  |     Opening an SSL connection over a HTTP proxy is therefor a matter of asking | ||||||
|  |     the proxy for a straight connection to the target host on a specified | ||||||
|  |     port. This is made with the HTTP request CONNECT. ("please mr proxy, | ||||||
|  |     connect me to that remote host"). | ||||||
|  |  | ||||||
|  |     Because of the nature of this operation, where the proxy has no idea what | ||||||
|  |     kind of data that is passed in and out through this tunnel, this breaks | ||||||
|  |     some of the very few advantages that come from using a proxy, such as | ||||||
|  |     caching.  Many organizations prevent this kind of tunneling to other | ||||||
|  |     destination port numbers than 443 (which is the default HTTPS port | ||||||
|  |     number). | ||||||
|  |  | ||||||
|  |   Tunneling Through Proxy | ||||||
|  |  | ||||||
|  |     As explained above, tunneling is required for SSL to work and often even | ||||||
|  |     restricted to the operation intended for SSL; HTTPS. | ||||||
|  |  | ||||||
|  |     This is however not the only time proxy-tunneling might offer benefits to | ||||||
|  |     you or your application. | ||||||
|  |  | ||||||
|  |     As tunneling opens a direct connection from your application to the remote | ||||||
|  |     machine, it suddenly also re-introduces the ability to do non-HTTP | ||||||
|  |     operations over a HTTP proxy. You can in fact use things such as FTP | ||||||
|  |     upload or FTP custom commands this way. | ||||||
|  |  | ||||||
|  |     Again, this is often prevented by the adminstrators of proxies and is | ||||||
|  |     rarely allowed. | ||||||
|  |  | ||||||
|  |     Tell libcurl to use proxy tunneling like this: | ||||||
|  |  | ||||||
|  |        curl_easy_setopt(easyhandle, CURLOPT_HTTPPROXYTUNNEL, TRUE); | ||||||
|  |  | ||||||
|  |     In fact, there might even be times when you want to do plain HTTP | ||||||
|  |     operations using a tunnel like this, as it then enables you to operate on | ||||||
|  |     the remote server instead of asking the proxy to do so. libcurl will not | ||||||
|  |     stand in the way for such innovative actions either! | ||||||
|  |  | ||||||
|  |   Proxy Auto-Config | ||||||
|  |  | ||||||
|  |     Netscape first came up with this. It is basicly a web page (usually using | ||||||
|  |     a .pac extension) with a javascript that when executed by the browser with | ||||||
|  |     the requested URL as input, returns information to the browser on how to | ||||||
|  |     connect to the URL. The returned information might be "DIRECT" (which | ||||||
|  |     means no proxy should be used), "PROXY host:port" (to tell the browser | ||||||
|  |     where the proxy for this particular URL is) or "SOCKS host:port" (to | ||||||
|  |     direct the brower to a SOCKS proxy). | ||||||
|  |  | ||||||
|  |     libcurl has no means to interpret or evaluate javascript and thus it | ||||||
|  |     doesn't support this. If you get yourself in a position where you face | ||||||
|  |     this nasty invention, the following advice have been mentioned and used in | ||||||
|  |     the past: | ||||||
|  |  | ||||||
|  |     - Depending on the javascript complexity, write up a script that | ||||||
|  |       translates it to another language and execute that. | ||||||
|  |  | ||||||
|  |     - Read the javascript code and rewrite the same logic in another language. | ||||||
|  |  | ||||||
|  |     - Implement a javascript interpreted, people have successfully used the | ||||||
|  |       Mozilla javascript engine in the past. | ||||||
|  |  | ||||||
|  |     - Ask your admins to stop this, for a static proxy setup or similar. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Persistancy Is The Way to Happiness | ||||||
|  |  | ||||||
|  |  Re-cycling the same easy handle several times when doing multiple requests is | ||||||
|  |  the way to go. | ||||||
|  |  | ||||||
|  |  After each single curl_easy_perform() operation, libcurl will keep the | ||||||
|  |  connection alive and open. A subsequent request using the same easy handle to | ||||||
|  |  the same host might just be able to use the already open connection! This | ||||||
|  |  reduces network impact a lot. | ||||||
|  |  | ||||||
|  |  Even if the connection is dropped, all connections involving SSL to the same | ||||||
|  |  host again, will benefit from libcurl's session ID cache that drasticly | ||||||
|  |  reduces re-connection time. | ||||||
|  |  | ||||||
|  |  FTP connections that are kept alive saves a lot of time, as the command- | ||||||
|  |  response roundtrips are skipped, and also you don't risk getting blocked | ||||||
|  |  without permission to login again like on many FTP servers only allowing N | ||||||
|  |  persons to be logged in at the same time. | ||||||
|  |  | ||||||
|  |  libcurl caches DNS name resolving results, to make lookups of a previously | ||||||
|  |  looked up name a lot faster. | ||||||
|  |  | ||||||
|  |  Other interesting details that improve performance for subsequent requests | ||||||
|  |  may also be added in the future. | ||||||
|  |  | ||||||
|  |  Each easy handle will attempt to keep the last few connections alive for a | ||||||
|  |  while in case they are to be used again. You can set the size of this "cache" | ||||||
|  |  with the CURLOPT_MAXCONNECTS option. Default is 5. It is very seldom any | ||||||
|  |  point in changing this value, and if you think of changing this it is often | ||||||
|  |  just a matter of thinking again. | ||||||
|  |  | ||||||
|  |  When the connection cache gets filled, libcurl must close an existing | ||||||
|  |  connection in order to get room for the new one. To know which connection to | ||||||
|  |  close, libcurl uses a "close policy" that you can affect with the | ||||||
|  |  CURLOPT_CLOSEPOLICY option. There's only two polices implemented as of this | ||||||
|  |  writing (libcurl 7.9.4) and they are: | ||||||
|  |  | ||||||
|  |   CURLCLOSEPOLICY_LEAST_RECENTLY_USED simply close the one that hasn't been | ||||||
|  |   used for the longest time. This is the default behavior. | ||||||
|  |  | ||||||
|  |   CURLCLOSEPOLICY_OLDEST closes the oldest connection, the one that was | ||||||
|  |   createst the longest time ago. | ||||||
|  |  | ||||||
|  |  There are, or at least were, plans to support a close policy that would call | ||||||
|  |  a user-specified callback to let the user be able to decide which connection | ||||||
|  |  to dump when this is necessary and therefor is the CURLOPT_CLOSEFUNCTION an | ||||||
|  |  existing option still today. Nothing ever uses this though and this will not | ||||||
|  |  be used within the forseeable future either. | ||||||
|  |  | ||||||
|  |  To force your upcoming request to not use an already existing connection (it | ||||||
|  |  will even close one first if there happens to be one alive to the same host | ||||||
|  |  you're about to operate on), you can do that by setting CURLOPT_FRESH_CONNECT | ||||||
|  |  to TRUE. In a similar spirit, you can also forbid the upcoming request to be | ||||||
|  |  "lying" around and possibly get re-used after the request by setting | ||||||
|  |  CURLOPT_FORBID_REUSE to TRUE. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Customizing Operations | ||||||
|  |  | ||||||
|  |  There is an ongoing development today where more and more protocols are built | ||||||
|  |  upon HTTP for transport. This has obvious benefits as HTTP is a tested and | ||||||
|  |  reliable protocol that is widely deployed and have excellent proxy-support. | ||||||
|  |  | ||||||
|  |  When you use one of these protocols, and even when doing other kinds of | ||||||
|  |  programming you may need to change the traditional HTTP (or FTP or...) | ||||||
|  |  manners. You may need to change words, headers or various data. | ||||||
|  |  | ||||||
|  |  libcurl is your friend here too. | ||||||
|  |  | ||||||
|  |  If just changing the actual HTTP request keyword is what you want, like when | ||||||
|  |  GET, HEAD or POST is not good enough for you, CURLOPT_CUSTOMREQUEST is there | ||||||
|  |  for you. It is very simple to use: | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "MYOWNRUQUEST"); | ||||||
|  |  | ||||||
|  |  When using the custom request, you change the request keyword of the actual | ||||||
|  |  request you are performing. Thus, by default you make GET request but you can | ||||||
|  |  also make a POST operation (as described before) and then replace the POST | ||||||
|  |  keyword if you want to. You're the boss. | ||||||
|  |  | ||||||
|  |  HTTP-like protocols pass a series of headers to the server when doing the | ||||||
|  |  request, and you're free to pass any amount of extra headers that you think | ||||||
|  |  fit. Adding headers are this easy: | ||||||
|  |  | ||||||
|  |     struct curl_slist *headers; | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "Hey-server-hey: how are you?"); | ||||||
|  |     headers = curl_slist_append(headers, "X-silly-content: yes"); | ||||||
|  |  | ||||||
|  |     /* pass our list of custom made headers */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* transfer http */ | ||||||
|  |  | ||||||
|  |     curl_slist_free_all(headers); /* free the header list */ | ||||||
|  |  | ||||||
|  |  ... and if you think some of the internally generated headers, such as | ||||||
|  |  User-Agent:, Accept: or Host: don't contain the data you want them to | ||||||
|  |  contain, you can replace them by simply setting them too: | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "User-Agent: 007"); | ||||||
|  |     headers = curl_slist_append(headers, "Host: munged.host.line"); | ||||||
|  |  | ||||||
|  |  If you replace an existing header with one with no contents, you will prevent | ||||||
|  |  the header from being sent. Like if you want to completely prevent the | ||||||
|  |  "Accept:" header to be sent, you can disable it with code similar to this: | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "Accept:"); | ||||||
|  |  | ||||||
|  |  Both replacing and cancelling internal headers should be done with careful | ||||||
|  |  consideration and you should be aware that you may violate the HTTP protocol | ||||||
|  |  when doing so. | ||||||
|  |  | ||||||
|  |  Not all protocols are HTTP-like, and thus the above may not help you when you | ||||||
|  |  want to make for example your FTP transfers to behave differently. | ||||||
|  |  | ||||||
|  |  Sending custom commands to a FTP server means that you need to send the | ||||||
|  |  comands exactly as the FTP server expects them (RFC959 is a good guide here), | ||||||
|  |  and you can only use commands that work on the control-connection alone. All | ||||||
|  |  kinds of commands that requires data interchange and thus needs a | ||||||
|  |  data-connection must be left to libcurl's own judgement. Also be aware that | ||||||
|  |  libcurl will do its very best to change directory to the target directory | ||||||
|  |  before doing any transfer, so if you change directory (with CWD or similar) | ||||||
|  |  you might confuse libcurl and then it might not attempt to transfer the file | ||||||
|  |  in the correct remote directory. | ||||||
|  |  | ||||||
|  |  A little example that deletes a given file before an operation: | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "DELE file-to-remove"); | ||||||
|  |  | ||||||
|  |     /* pass the list of custom commands to the handle */ | ||||||
|  |     curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers); | ||||||
|  |  | ||||||
|  |     curl_easy_perform(easyhandle); /* transfer ftp data! */ | ||||||
|  |  | ||||||
|  |     curl_slist_free_all(headers); /* free the header list */ | ||||||
|  |  | ||||||
|  |  If you would instead want this operation (or chain of operations) to happen | ||||||
|  |  _after_ the data transfer took place the option to curl_easy_setopt() would | ||||||
|  |  instead be called CURLOPT_POSTQUOTE and used the exact same way. | ||||||
|  |  | ||||||
|  |  The custom FTP command will be issued to the server in the same order they | ||||||
|  |  are built in the list, and if a command gets an error code returned back from | ||||||
|  |  the server no more commands will be issued and libcurl will bail out with an | ||||||
|  |  error code. Note that if you use CURLOPT_QUOTE to send commands before a | ||||||
|  |  transfer, no transfer will actually take place then. | ||||||
|  |  | ||||||
|  |  [ custom FTP commands without transfer, FTP "header-only", HTTP 1.0 ] | ||||||
|  |  | ||||||
|  | Cookies Without Chocolate Chips | ||||||
|  |  | ||||||
|  |  [ set cookies, read cookies from file, cookie-jar ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Headers Equal Fun | ||||||
|  |  | ||||||
|  |  [ use the header callback for HTTP, FTP etc ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Post Transfer Information | ||||||
|  |  | ||||||
|  |  [ curl_easy_getinfo ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Security Considerations | ||||||
|  |  | ||||||
|  |  [ ps output, netrc plain text, plain text protocols / base64 ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SSL, Certificates and Other Tricks | ||||||
|  |  | ||||||
|  |  [ seeding, passwords, keys, certificates, ENGINE, ca certs ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Future | ||||||
|  |  | ||||||
|  |  [ multi interface, sharing between handles, mutexes, pipelining ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ----- | ||||||
|  | Footnotes: | ||||||
|  |  | ||||||
|  | [1] = HTTP PUT without knowing the size prior to transfer is indeed possible, | ||||||
|  |       but libcurl does not support the chunked transfers on uploading that is | ||||||
|  |       necessary for this feature to work. We'd gratefully appreciate patches | ||||||
|  |       that bring this functionality... | ||||||
|  |  | ||||||
|  | [2] = This happens on Windows machines when libcurl is built and used as a | ||||||
|  |       DLL. However, you can still do this on Windows if you link with a static | ||||||
|  |       library. | ||||||
|  |  | ||||||
|  | [3] = The curl-config tool is generated at build-time (on unix-like systems) | ||||||
|  |       and should be installed with the 'make install' or similar instruction | ||||||
|  |       that installs the library, header files, man pages etc. | ||||||
| @@ -6,8 +6,10 @@ | |||||||
| .SH NAME | .SH NAME | ||||||
| libcurl \- client-side URL transfers | libcurl \- client-side URL transfers | ||||||
| .SH DESCRIPTION | .SH DESCRIPTION | ||||||
| This is an overview on how to use libcurl in your c/c++ programs. There are | This is an overview on how to use libcurl in your C programs. There are | ||||||
| specific man pages for each function mentioned in here. | specific man pages for each function mentioned in here. There's also the | ||||||
|  | libcurl-the-guide document for a complete tutorial to programming with | ||||||
|  | libcurl. | ||||||
|  |  | ||||||
| libcurl can also be used directly from within your Java, PHP, Perl, Ruby or | libcurl can also be used directly from within your Java, PHP, Perl, Ruby or | ||||||
| Tcl programs as well, look elsewhere for documentation on this! | Tcl programs as well, look elsewhere for documentation on this! | ||||||
| @@ -56,9 +58,6 @@ get information about a performed transfer | |||||||
| .B curl_formadd() | .B curl_formadd() | ||||||
| helps building a HTTP form POST | helps building a HTTP form POST | ||||||
| .TP | .TP | ||||||
| .B curl_formparse() |  | ||||||
| helps building a HTTP form POST (deprecated since 7.9 use curl_formadd()!) |  | ||||||
| .TP |  | ||||||
| .B curl_formfree() | .B curl_formfree() | ||||||
| free a list built with curl_formparse()/curl_formadd() | free a list built with curl_formparse()/curl_formadd() | ||||||
| .TP | .TP | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|  *                            | (__| |_| |  _ <| |___  |  *                            | (__| |_| |  _ <| |___  | ||||||
|  *                             \___|\___/|_| \_\_____| |  *                             \___|\___/|_| \_\_____| | ||||||
|  * |  * | ||||||
|  * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. |  * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. | ||||||
|  * |  * | ||||||
|  * In order to be useful for every potential user, curl and libcurl are |  * In order to be useful for every potential user, curl and libcurl are | ||||||
|  * dual-licensed under the MPL and the MIT/X-derivate licenses. |  * dual-licensed under the MPL and the MIT/X-derivate licenses. | ||||||
| @@ -30,11 +30,11 @@ | |||||||
| # include <time.h> | # include <time.h> | ||||||
| #else | #else | ||||||
| # include <sys/types.h> | # include <sys/types.h> | ||||||
| # if TIME_WITH_SYS_TIME | # ifdef TIME_WITH_SYS_TIME | ||||||
| #  include <sys/time.h> | #  include <sys/time.h> | ||||||
| #  include <time.h> | #  include <time.h> | ||||||
| # else | # else | ||||||
| #  if HAVE_SYS_TIME_H | #  ifdef HAVE_SYS_TIME_H | ||||||
| #   include <sys/time.h> | #   include <sys/time.h> | ||||||
| #  else | #  else | ||||||
| #   include <time.h> | #   include <time.h> | ||||||
| @@ -272,7 +272,7 @@ typedef enum { | |||||||
|   /* Set cookie in request: */ |   /* Set cookie in request: */ | ||||||
|   CINIT(COOKIE, OBJECTPOINT, 22), |   CINIT(COOKIE, OBJECTPOINT, 22), | ||||||
|  |  | ||||||
|   /* This points to a linked list of headers, struct HttpHeader kind */ |   /* This points to a linked list of headers, struct curl_slist kind */ | ||||||
|   CINIT(HTTPHEADER, OBJECTPOINT, 23), |   CINIT(HTTPHEADER, OBJECTPOINT, 23), | ||||||
|  |  | ||||||
|   /* This points to a linked list of post entries, struct HttpPost */ |   /* This points to a linked list of post entries, struct HttpPost */ | ||||||
| @@ -613,8 +613,8 @@ CURLcode curl_global_init(long flags); | |||||||
| void curl_global_cleanup(void); | void curl_global_cleanup(void); | ||||||
|  |  | ||||||
| /* This is the version number */ | /* This is the version number */ | ||||||
| #define LIBCURL_VERSION "7.9.3-pre1" | #define LIBCURL_VERSION "7.9.4" | ||||||
| #define LIBCURL_VERSION_NUM 0x070903 | #define LIBCURL_VERSION_NUM 0x070904 | ||||||
|  |  | ||||||
| /* linked-list structure for the CURLOPT_QUOTE option (and other) */ | /* linked-list structure for the CURLOPT_QUOTE option (and other) */ | ||||||
| struct curl_slist { | struct curl_slist { | ||||||
| @@ -666,7 +666,11 @@ typedef enum { | |||||||
|  |  | ||||||
|   CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, |   CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, | ||||||
|  |  | ||||||
|   CURLINFO_LASTONE          = 18 |   CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, | ||||||
|  |  | ||||||
|  |   /* Fill in new entries here! */ | ||||||
|  |  | ||||||
|  |   CURLINFO_LASTONE          = 19 | ||||||
| } CURLINFO; | } CURLINFO; | ||||||
|  |  | ||||||
| /* unfortunately, the easy.h include file needs the options and info stuff | /* unfortunately, the easy.h include file needs the options and info stuff | ||||||
|   | |||||||
| @@ -35,14 +35,14 @@ libcurl_a_SOURCES = arpa_telnet.h file.c getpass.h netrc.h timeval.c base64.c \ | |||||||
| 	ldap.h ssluse.h escape.c getenv.h mprintf.c telnet.c escape.h getpass.c netrc.c	\ | 	ldap.h ssluse.h escape.c getenv.h mprintf.c telnet.c escape.h getpass.c netrc.c	\ | ||||||
| 	telnet.h getinfo.c strequal.c strequal.h easy.c security.h		\ | 	telnet.h getinfo.c strequal.c strequal.h easy.c security.h		\ | ||||||
| 	security.c krb4.h krb4.c memdebug.h memdebug.c inet_ntoa_r.h http_chunks.h http_chunks.c \ | 	security.c krb4.h krb4.c memdebug.h memdebug.c inet_ntoa_r.h http_chunks.h http_chunks.c \ | ||||||
| 	strtok.c connect.c | 	strtok.c connect.c hash.c llist.c | ||||||
|  |  | ||||||
| libcurl_a_OBJECTS =  file.o timeval.o base64.o hostip.o progress.o \ | libcurl_a_OBJECTS =  file.o timeval.o base64.o hostip.o progress.o \ | ||||||
| 	formdata.o cookie.o http.o sendf.o ftp.o url.o dict.o if2ip.o \ | 	formdata.o cookie.o http.o sendf.o ftp.o url.o dict.o if2ip.o \ | ||||||
| 	speedcheck.o getdate.o transfer.o ldap.o ssluse.o version.o \ | 	speedcheck.o getdate.o transfer.o ldap.o ssluse.o version.o \ | ||||||
| 	getenv.o escape.o mprintf.o telnet.o getpass.o netrc.o getinfo.o \ | 	getenv.o escape.o mprintf.o telnet.o getpass.o netrc.o getinfo.o \ | ||||||
| 	strequal.o easy.o security.o krb4.o memdebug.o http_chunks.o \ | 	strequal.o easy.o security.o krb4.o memdebug.o http_chunks.o \ | ||||||
| 	strtok.o connect.o | 	strtok.o connect.o hash.o llist.o | ||||||
|  |  | ||||||
| LIBRARIES =  $(libcurl_a_LIBRARIES) | LIBRARIES =  $(libcurl_a_LIBRARIES) | ||||||
| SOURCES = $(libcurl_a_SOURCES) | SOURCES = $(libcurl_a_SOURCES) | ||||||
|   | |||||||
| @@ -369,9 +369,11 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */ | |||||||
|     /* subtract the passed time */ |     /* subtract the passed time */ | ||||||
|     timeout_ms -= (long)has_passed; |     timeout_ms -= (long)has_passed; | ||||||
|  |  | ||||||
|     if(timeout_ms < 0) |     if(timeout_ms < 0) { | ||||||
|       /* a precaution, no need to continue if time already is up */ |       /* a precaution, no need to continue if time already is up */ | ||||||
|  |       failf(data, "Connection time-out"); | ||||||
|       return CURLE_OPERATION_TIMEOUTED; |       return CURLE_OPERATION_TIMEOUTED; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #ifdef ENABLE_IPV6 | #ifdef ENABLE_IPV6 | ||||||
|   | |||||||
| @@ -235,7 +235,6 @@ int FormParse(char *input, | |||||||
| 	     | 	     | ||||||
| 	    if(2 != sscanf(type, "%127[^/]/%127[^,\n]", | 	    if(2 != sscanf(type, "%127[^/]/%127[^,\n]", | ||||||
| 			   major, minor)) { | 			   major, minor)) { | ||||||
| 	      fprintf(stderr, "Illegally formatted content-type field!\n"); |  | ||||||
|               free(contents); |               free(contents); | ||||||
| 	      return 2; /* illegal content-type syntax! */ | 	      return 2; /* illegal content-type syntax! */ | ||||||
| 	    } | 	    } | ||||||
| @@ -371,7 +370,6 @@ int FormParse(char *input, | |||||||
|  |  | ||||||
|   } |   } | ||||||
|   else { |   else { | ||||||
|     fprintf(stderr, "Illegally formatted input field!\n"); |  | ||||||
|     free(contents); |     free(contents); | ||||||
|     return 1; |     return 1; | ||||||
|   } |   } | ||||||
| @@ -841,7 +839,6 @@ FORMcode FormAdd(struct HttpPost **httppost, | |||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|     default: |     default: | ||||||
|       fprintf (stderr, "got unknown CURLFORM_OPTION: %d\n", option); |  | ||||||
|       return_value = FORMADD_UNKNOWN_OPTION; |       return_value = FORMADD_UNKNOWN_OPTION; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -1069,7 +1066,7 @@ struct FormData *Curl_getFormData(struct HttpPost *post, | |||||||
|   do { |   do { | ||||||
|  |  | ||||||
|     /* boundary */ |     /* boundary */ | ||||||
|     size += AddFormDataf(&form, "\r\n--%s\r\n", boundary); |     size += AddFormDataf(&form, "--%s\r\n", boundary); | ||||||
|  |  | ||||||
|     size += AddFormData(&form, |     size += AddFormData(&form, | ||||||
|                         "Content-Disposition: form-data; name=\"", 0); |                         "Content-Disposition: form-data; name=\"", 0); | ||||||
| @@ -1155,10 +1152,13 @@ struct FormData *Curl_getFormData(struct HttpPost *post, | |||||||
| 	  } | 	  } | ||||||
|           if(fileread != stdin) |           if(fileread != stdin) | ||||||
|             fclose(fileread); |             fclose(fileread); | ||||||
| 	} else { |  | ||||||
| 	  size += AddFormData(&form, "[File wasn't found by client]", 0); |  | ||||||
| 	} | 	} | ||||||
|       } else { |         else { | ||||||
|  |           /* File wasn't found, add a nothing field! */ | ||||||
|  | 	  size += AddFormData(&form, "", 0); | ||||||
|  | 	} | ||||||
|  |       } | ||||||
|  |  else { | ||||||
| 	/* include the contents we got */ | 	/* include the contents we got */ | ||||||
| 	size += AddFormData(&form, post->contents, post->contentslength); | 	size += AddFormData(&form, post->contents, post->contentslength); | ||||||
|       } |       } | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								lib/ftp.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								lib/ftp.c
									
									
									
									
									
								
							| @@ -267,9 +267,16 @@ int Curl_GetFTPResponse(char *buf, | |||||||
|         ftp->cache = NULL;   /* clear the pointer */ |         ftp->cache = NULL;   /* clear the pointer */ | ||||||
|         ftp->cache_size = 0; /* zero the size just in case */ |         ftp->cache_size = 0; /* zero the size just in case */ | ||||||
|       } |       } | ||||||
|       else if(CURLE_OK != Curl_read(conn, sockfd, ptr, |       else { | ||||||
|                                     BUFSIZE-nread, &gotbytes)) |         int res = Curl_read(conn, sockfd, ptr, | ||||||
|         keepon = FALSE; |                             BUFSIZE-nread, &gotbytes); | ||||||
|  |         if(res < 0) | ||||||
|  |           /* EWOULDBLOCK */ | ||||||
|  |           continue; /* go looping again */ | ||||||
|  |  | ||||||
|  |         if(CURLE_OK != res) | ||||||
|  |           keepon = FALSE; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       if(!keepon) |       if(!keepon) | ||||||
|         ; |         ; | ||||||
| @@ -351,7 +358,7 @@ int Curl_GetFTPResponse(char *buf, | |||||||
|   if(!error) |   if(!error) | ||||||
|     code = atoi(buf); |     code = atoi(buf); | ||||||
|  |  | ||||||
| #if KRB4 | #ifdef KRB4 | ||||||
|   /* handle the security-oriented responses 6xx ***/ |   /* handle the security-oriented responses 6xx ***/ | ||||||
|   /* FIXME: some errorchecking perhaps... ***/ |   /* FIXME: some errorchecking perhaps... ***/ | ||||||
|   switch(code) { |   switch(code) { | ||||||
| @@ -904,7 +911,7 @@ ftp_pasv_verbose(struct connectdata *conn, | |||||||
| #  ifdef HAVE_GETHOSTBYADDR_R_7 | #  ifdef HAVE_GETHOSTBYADDR_R_7 | ||||||
|   /* Solaris and IRIX */ |   /* Solaris and IRIX */ | ||||||
|   answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET, |   answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET, | ||||||
|                            (struct hostent *)hostent_buf, |                            (struct hostent *)bigbuf, | ||||||
|                            hostent_buf + sizeof(*answer), |                            hostent_buf + sizeof(*answer), | ||||||
|                            sizeof(hostent_buf) - sizeof(*answer), |                            sizeof(hostent_buf) - sizeof(*answer), | ||||||
|                            &h_errnop); |                            &h_errnop); | ||||||
| @@ -2051,9 +2058,11 @@ CURLcode Curl_ftp(struct connectdata *conn) | |||||||
| CURLcode Curl_ftpsendf(struct connectdata *conn, | CURLcode Curl_ftpsendf(struct connectdata *conn, | ||||||
|                        const char *fmt, ...) |                        const char *fmt, ...) | ||||||
| { | { | ||||||
|   size_t bytes_written; |   ssize_t bytes_written; | ||||||
|   char s[256]; |   char s[256]; | ||||||
|   size_t write_len; |   ssize_t write_len; | ||||||
|  |   char *sptr=s; | ||||||
|  |   CURLcode res = CURLE_OK; | ||||||
|  |  | ||||||
|   va_list ap; |   va_list ap; | ||||||
|   va_start(ap, fmt); |   va_start(ap, fmt); | ||||||
| @@ -2067,9 +2076,23 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, | |||||||
|  |  | ||||||
|   bytes_written=0; |   bytes_written=0; | ||||||
|   write_len = strlen(s); |   write_len = strlen(s); | ||||||
|   Curl_write(conn, conn->firstsocket, s, write_len, &bytes_written); |  | ||||||
|  |  | ||||||
|   return (bytes_written==write_len)?CURLE_OK:CURLE_WRITE_ERROR; |   do { | ||||||
|  |     res = Curl_write(conn, conn->firstsocket, sptr, write_len, | ||||||
|  |                      &bytes_written); | ||||||
|  |  | ||||||
|  |     if(CURLE_OK != res) | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     if(bytes_written != write_len) { | ||||||
|  |       write_len -= bytes_written; | ||||||
|  |       sptr += bytes_written; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |       break; | ||||||
|  |   } while(1); | ||||||
|  |  | ||||||
|  |   return res; | ||||||
| } | } | ||||||
|  |  | ||||||
| /*********************************************************************** | /*********************************************************************** | ||||||
|   | |||||||
							
								
								
									
										530
									
								
								lib/getdate.c
									
									
									
									
									
								
							
							
						
						
									
										530
									
								
								lib/getdate.c
									
									
									
									
									
								
							| @@ -45,6 +45,11 @@ | |||||||
| # endif | # endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef YYDEBUG | ||||||
|  |   /* to satisfy gcc -Wundef, we set this to 0 */ | ||||||
|  | #define YYDEBUG 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* Since the code of getdate.y is not included in the Emacs executable | /* Since the code of getdate.y is not included in the Emacs executable | ||||||
|    itself, there is no need to #define static in this file.  Even if |    itself, there is no need to #define static in this file.  Even if | ||||||
|    the code were included in the Emacs executable, it probably |    the code were included in the Emacs executable, it probably | ||||||
| @@ -192,38 +197,40 @@ typedef enum _MERIDIAN { | |||||||
|     MERam, MERpm, MER24 |     MERam, MERpm, MER24 | ||||||
| } MERIDIAN; | } MERIDIAN; | ||||||
|  |  | ||||||
|  | /* parse results and input string */ | ||||||
|  | typedef struct _CONTEXT { | ||||||
|  |     const char	*yyInput; | ||||||
|  |     int		yyDayOrdinal; | ||||||
|  |     int		yyDayNumber; | ||||||
|  |     int		yyHaveDate; | ||||||
|  |     int		yyHaveDay; | ||||||
|  |     int		yyHaveRel; | ||||||
|  |     int		yyHaveTime; | ||||||
|  |     int		yyHaveZone; | ||||||
|  |     int		yyTimezone; | ||||||
|  |     int		yyDay; | ||||||
|  |     int		yyHour; | ||||||
|  |     int		yyMinutes; | ||||||
|  |     int		yyMonth; | ||||||
|  |     int		yySeconds; | ||||||
|  |     int		yyYear; | ||||||
|  |     MERIDIAN	yyMeridian; | ||||||
|  |     int		yyRelDay; | ||||||
|  |     int		yyRelHour; | ||||||
|  |     int		yyRelMinutes; | ||||||
|  |     int		yyRelMonth; | ||||||
|  |     int		yyRelSeconds; | ||||||
|  |     int		yyRelYear; | ||||||
|  | } CONTEXT; | ||||||
|  |  | ||||||
| /* | /* enable use of extra argument to yyparse and yylex which can be used to pass | ||||||
| **  Global variables.  We could get rid of most of these by using a good | **  in a user defined value (CONTEXT struct in our case) | ||||||
| **  union as the yacc stack.  (This routine was originally written before |  | ||||||
| **  yacc had the %union construct.)  Maybe someday; right now we only use |  | ||||||
| **  the %union very rarely. |  | ||||||
| */ | */ | ||||||
| static const char	*yyInput; | #define YYPARSE_PARAM cookie | ||||||
| static int	yyDayOrdinal; | #define YYLEX_PARAM cookie | ||||||
| static int	yyDayNumber; | #define context ((CONTEXT *) cookie) | ||||||
| static int	yyHaveDate; |  | ||||||
| static int	yyHaveDay; |  | ||||||
| static int	yyHaveRel; |  | ||||||
| static int	yyHaveTime; |  | ||||||
| static int	yyHaveZone; |  | ||||||
| static int	yyTimezone; |  | ||||||
| static int	yyDay; |  | ||||||
| static int	yyHour; |  | ||||||
| static int	yyMinutes; |  | ||||||
| static int	yyMonth; |  | ||||||
| static int	yySeconds; |  | ||||||
| static int	yyYear; |  | ||||||
| static MERIDIAN	yyMeridian; |  | ||||||
| static int	yyRelDay; |  | ||||||
| static int	yyRelHour; |  | ||||||
| static int	yyRelMinutes; |  | ||||||
| static int	yyRelMonth; |  | ||||||
| static int	yyRelSeconds; |  | ||||||
| static int	yyRelYear; |  | ||||||
|  |  | ||||||
|  | #line 218 "getdate.y" | ||||||
| #line 206 "getdate.y" |  | ||||||
| typedef union { | typedef union { | ||||||
|     int			Number; |     int			Number; | ||||||
|     enum _MERIDIAN	Meridian; |     enum _MERIDIAN	Meridian; | ||||||
| @@ -306,11 +313,11 @@ static const short yyrhs[] = {    -1, | |||||||
|  |  | ||||||
| #if YYDEBUG != 0 | #if YYDEBUG != 0 | ||||||
| static const short yyrline[] = { 0, | static const short yyrline[] = { 0, | ||||||
|    222,   223,   226,   229,   232,   235,   238,   241,   244,   250, |    234,   235,   238,   241,   244,   247,   250,   253,   256,   262, | ||||||
|    256,   265,   271,   283,   286,   289,   295,   299,   303,   309, |    268,   277,   283,   295,   298,   301,   307,   311,   315,   321, | ||||||
|    313,   331,   337,   343,   347,   352,   356,   363,   371,   374, |    325,   343,   349,   355,   359,   364,   368,   375,   383,   386, | ||||||
|    377,   380,   383,   386,   389,   392,   395,   398,   401,   404, |    389,   392,   395,   398,   401,   404,   407,   410,   413,   416, | ||||||
|    407,   410,   413,   416,   419,   422,   425,   430,   463,   467 |    419,   422,   425,   428,   431,   434,   437,   442,   476,   480 | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -390,6 +397,8 @@ static const short yycheck[] = {     0, | |||||||
|     11,    15,    13,    14,    16,    19,    17,    16,    21,     0, |     11,    15,    13,    14,    16,    19,    17,    16,    21,     0, | ||||||
|     56 |     56 | ||||||
| }; | }; | ||||||
|  | #define YYPURE 1 | ||||||
|  |  | ||||||
| /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */ | /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */ | ||||||
| #line 3 "/usr/local/share/bison.simple" | #line 3 "/usr/local/share/bison.simple" | ||||||
| /* This file comes from bison-1.28.  */ | /* This file comes from bison-1.28.  */ | ||||||
| @@ -934,135 +943,135 @@ yyreduce: | |||||||
|   switch (yyn) { |   switch (yyn) { | ||||||
|  |  | ||||||
| case 3: | case 3: | ||||||
| #line 226 "getdate.y" | #line 238 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHaveTime++; | 	    context->yyHaveTime++; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 4: | case 4: | ||||||
| #line 229 "getdate.y" | #line 241 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 5: | case 5: | ||||||
| #line 232 "getdate.y" | #line 244 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHaveDate++; | 	    context->yyHaveDate++; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 6: | case 6: | ||||||
| #line 235 "getdate.y" | #line 247 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHaveDay++; | 	    context->yyHaveDay++; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 7: | case 7: | ||||||
| #line 238 "getdate.y" | #line 250 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHaveRel++; | 	    context->yyHaveRel++; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 9: | case 9: | ||||||
| #line 244 "getdate.y" | #line 256 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHour = yyvsp[-1].Number; | 	    context->yyHour = yyvsp[-1].Number; | ||||||
| 	    yyMinutes = 0; | 	    context->yyMinutes = 0; | ||||||
| 	    yySeconds = 0; | 	    context->yySeconds = 0; | ||||||
| 	    yyMeridian = yyvsp[0].Meridian; | 	    context->yyMeridian = yyvsp[0].Meridian; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 10: | case 10: | ||||||
| #line 250 "getdate.y" | #line 262 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHour = yyvsp[-3].Number; | 	    context->yyHour = yyvsp[-3].Number; | ||||||
| 	    yyMinutes = yyvsp[-1].Number; | 	    context->yyMinutes = yyvsp[-1].Number; | ||||||
| 	    yySeconds = 0; | 	    context->yySeconds = 0; | ||||||
| 	    yyMeridian = yyvsp[0].Meridian; | 	    context->yyMeridian = yyvsp[0].Meridian; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 11: | case 11: | ||||||
| #line 256 "getdate.y" | #line 268 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHour = yyvsp[-3].Number; | 	    context->yyHour = yyvsp[-3].Number; | ||||||
| 	    yyMinutes = yyvsp[-1].Number; | 	    context->yyMinutes = yyvsp[-1].Number; | ||||||
| 	    yyMeridian = MER24; | 	    context->yyMeridian = MER24; | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	    yyTimezone = (yyvsp[0].Number < 0 | 	    context->yyTimezone = (yyvsp[0].Number < 0 | ||||||
| 			  ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 | 				   ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 | ||||||
| 			  : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); | 				   : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 12: | case 12: | ||||||
| #line 265 "getdate.y" | #line 277 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHour = yyvsp[-5].Number; | 	    context->yyHour = yyvsp[-5].Number; | ||||||
| 	    yyMinutes = yyvsp[-3].Number; | 	    context->yyMinutes = yyvsp[-3].Number; | ||||||
| 	    yySeconds = yyvsp[-1].Number; | 	    context->yySeconds = yyvsp[-1].Number; | ||||||
| 	    yyMeridian = yyvsp[0].Meridian; | 	    context->yyMeridian = yyvsp[0].Meridian; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 13: | case 13: | ||||||
| #line 271 "getdate.y" | #line 283 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyHour = yyvsp[-5].Number; | 	    context->yyHour = yyvsp[-5].Number; | ||||||
| 	    yyMinutes = yyvsp[-3].Number; | 	    context->yyMinutes = yyvsp[-3].Number; | ||||||
| 	    yySeconds = yyvsp[-1].Number; | 	    context->yySeconds = yyvsp[-1].Number; | ||||||
| 	    yyMeridian = MER24; | 	    context->yyMeridian = MER24; | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	    yyTimezone = (yyvsp[0].Number < 0 | 	    context->yyTimezone = (yyvsp[0].Number < 0 | ||||||
| 			  ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 | 				   ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 | ||||||
| 			  : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); | 				   : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 14: | case 14: | ||||||
| #line 283 "getdate.y" | #line 295 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyTimezone = yyvsp[0].Number; | 	    context->yyTimezone = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 15: | case 15: | ||||||
| #line 286 "getdate.y" | #line 298 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyTimezone = yyvsp[0].Number - 60; | 	    context->yyTimezone = yyvsp[0].Number - 60; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 16: | case 16: | ||||||
| #line 290 "getdate.y" | #line 302 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyTimezone = yyvsp[-1].Number - 60; | 	    context->yyTimezone = yyvsp[-1].Number - 60; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 17: | case 17: | ||||||
| #line 295 "getdate.y" | #line 307 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyDayOrdinal = 1; | 	    context->yyDayOrdinal = 1; | ||||||
| 	    yyDayNumber = yyvsp[0].Number; | 	    context->yyDayNumber = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 18: | case 18: | ||||||
| #line 299 "getdate.y" | #line 311 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyDayOrdinal = 1; | 	    context->yyDayOrdinal = 1; | ||||||
| 	    yyDayNumber = yyvsp[-1].Number; | 	    context->yyDayNumber = yyvsp[-1].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 19: | case 19: | ||||||
| #line 303 "getdate.y" | #line 315 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyDayOrdinal = yyvsp[-1].Number; | 	    context->yyDayOrdinal = yyvsp[-1].Number; | ||||||
| 	    yyDayNumber = yyvsp[0].Number; | 	    context->yyDayNumber = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 20: | case 20: | ||||||
| #line 309 "getdate.y" | #line 321 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyMonth = yyvsp[-2].Number; | 	    context->yyMonth = yyvsp[-2].Number; | ||||||
| 	    yyDay = yyvsp[0].Number; | 	    context->yyDay = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 21: | case 21: | ||||||
| #line 313 "getdate.y" | #line 325 "getdate.y" | ||||||
| { | { | ||||||
| 	  /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. | 	  /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. | ||||||
| 	     The goal in recognizing YYYY/MM/DD is solely to support legacy | 	     The goal in recognizing YYYY/MM/DD is solely to support legacy | ||||||
| @@ -1070,226 +1079,227 @@ case 21: | |||||||
| 	     you want portability, use the ISO 8601 format.  */ | 	     you want portability, use the ISO 8601 format.  */ | ||||||
| 	  if (yyvsp[-4].Number >= 1000) | 	  if (yyvsp[-4].Number >= 1000) | ||||||
| 	    { | 	    { | ||||||
| 	      yyYear = yyvsp[-4].Number; | 	      context->yyYear = yyvsp[-4].Number; | ||||||
| 	      yyMonth = yyvsp[-2].Number; | 	      context->yyMonth = yyvsp[-2].Number; | ||||||
| 	      yyDay = yyvsp[0].Number; | 	      context->yyDay = yyvsp[0].Number; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    { | 	    { | ||||||
| 	      yyMonth = yyvsp[-4].Number; | 	      context->yyMonth = yyvsp[-4].Number; | ||||||
| 	      yyDay = yyvsp[-2].Number; | 	      context->yyDay = yyvsp[-2].Number; | ||||||
| 	      yyYear = yyvsp[0].Number; | 	      context->yyYear = yyvsp[0].Number; | ||||||
| 	    } | 	    } | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 22: | case 22: | ||||||
| #line 331 "getdate.y" | #line 343 "getdate.y" | ||||||
| { | { | ||||||
| 	    /* ISO 8601 format.  yyyy-mm-dd.  */ | 	    /* ISO 8601 format.  yyyy-mm-dd.  */ | ||||||
| 	    yyYear = yyvsp[-2].Number; | 	    context->yyYear = yyvsp[-2].Number; | ||||||
| 	    yyMonth = -yyvsp[-1].Number; | 	    context->yyMonth = -yyvsp[-1].Number; | ||||||
| 	    yyDay = -yyvsp[0].Number; | 	    context->yyDay = -yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 23: | case 23: | ||||||
| #line 337 "getdate.y" | #line 349 "getdate.y" | ||||||
| { | { | ||||||
| 	    /* e.g. 17-JUN-1992.  */ | 	    /* e.g. 17-JUN-1992.  */ | ||||||
| 	    yyDay = yyvsp[-2].Number; | 	    context->yyDay = yyvsp[-2].Number; | ||||||
| 	    yyMonth = yyvsp[-1].Number; | 	    context->yyMonth = yyvsp[-1].Number; | ||||||
| 	    yyYear = -yyvsp[0].Number; | 	    context->yyYear = -yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 24: | case 24: | ||||||
| #line 343 "getdate.y" | #line 355 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyMonth = yyvsp[-1].Number; | 	    context->yyMonth = yyvsp[-1].Number; | ||||||
| 	    yyDay = yyvsp[0].Number; | 	    context->yyDay = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 25: | case 25: | ||||||
| #line 347 "getdate.y" | #line 359 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyMonth = yyvsp[-3].Number; | 	    context->yyMonth = yyvsp[-3].Number; | ||||||
| 	    yyDay = yyvsp[-2].Number; | 	    context->yyDay = yyvsp[-2].Number; | ||||||
| 	    yyYear = yyvsp[0].Number; | 	    context->yyYear = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 26: | case 26: | ||||||
| #line 352 "getdate.y" | #line 364 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyMonth = yyvsp[0].Number; | 	    context->yyMonth = yyvsp[0].Number; | ||||||
| 	    yyDay = yyvsp[-1].Number; | 	    context->yyDay = yyvsp[-1].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 27: | case 27: | ||||||
| #line 356 "getdate.y" | #line 368 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyMonth = yyvsp[-1].Number; | 	    context->yyMonth = yyvsp[-1].Number; | ||||||
| 	    yyDay = yyvsp[-2].Number; | 	    context->yyDay = yyvsp[-2].Number; | ||||||
| 	    yyYear = yyvsp[0].Number; | 	    context->yyYear = yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 28: | case 28: | ||||||
| #line 363 "getdate.y" | #line 375 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelSeconds = -yyRelSeconds; | 	    context->yyRelSeconds = -context->yyRelSeconds; | ||||||
| 	    yyRelMinutes = -yyRelMinutes; | 	    context->yyRelMinutes = -context->yyRelMinutes; | ||||||
| 	    yyRelHour = -yyRelHour; | 	    context->yyRelHour = -context->yyRelHour; | ||||||
| 	    yyRelDay = -yyRelDay; | 	    context->yyRelDay = -context->yyRelDay; | ||||||
| 	    yyRelMonth = -yyRelMonth; | 	    context->yyRelMonth = -context->yyRelMonth; | ||||||
| 	    yyRelYear = -yyRelYear; | 	    context->yyRelYear = -context->yyRelYear; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 30: | case 30: | ||||||
| #line 374 "getdate.y" | #line 386 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 31: | case 31: | ||||||
| #line 377 "getdate.y" | #line 389 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 32: | case 32: | ||||||
| #line 380 "getdate.y" | #line 392 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelYear += yyvsp[0].Number; | 	    context->yyRelYear += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 33: | case 33: | ||||||
| #line 383 "getdate.y" | #line 395 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 34: | case 34: | ||||||
| #line 386 "getdate.y" | #line 398 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 35: | case 35: | ||||||
| #line 389 "getdate.y" | #line 401 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMonth += yyvsp[0].Number; | 	    context->yyRelMonth += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 36: | case 36: | ||||||
| #line 392 "getdate.y" | #line 404 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 37: | case 37: | ||||||
| #line 395 "getdate.y" | #line 407 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 38: | case 38: | ||||||
| #line 398 "getdate.y" | #line 410 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelDay += yyvsp[0].Number; | 	    context->yyRelDay += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 39: | case 39: | ||||||
| #line 401 "getdate.y" | #line 413 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 40: | case 40: | ||||||
| #line 404 "getdate.y" | #line 416 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 41: | case 41: | ||||||
| #line 407 "getdate.y" | #line 419 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelHour += yyvsp[0].Number; | 	    context->yyRelHour += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 42: | case 42: | ||||||
| #line 410 "getdate.y" | #line 422 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 43: | case 43: | ||||||
| #line 413 "getdate.y" | #line 425 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 44: | case 44: | ||||||
| #line 416 "getdate.y" | #line 428 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelMinutes += yyvsp[0].Number; | 	    context->yyRelMinutes += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 45: | case 45: | ||||||
| #line 419 "getdate.y" | #line 431 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 46: | case 46: | ||||||
| #line 422 "getdate.y" | #line 434 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; | 	    context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 47: | case 47: | ||||||
| #line 425 "getdate.y" | #line 437 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyRelSeconds += yyvsp[0].Number; | 	    context->yyRelSeconds += yyvsp[0].Number; | ||||||
| 	; | 	; | ||||||
|     break;} |     break;} | ||||||
| case 48: | case 48: | ||||||
| #line 431 "getdate.y" | #line 443 "getdate.y" | ||||||
| { | { | ||||||
| 	    if (yyHaveTime && yyHaveDate && !yyHaveRel) | 	    if (context->yyHaveTime && context->yyHaveDate && | ||||||
| 	      yyYear = yyvsp[0].Number; | 		!context->yyHaveRel) | ||||||
|  | 	      context->yyYear = yyvsp[0].Number; | ||||||
| 	    else | 	    else | ||||||
| 	      { | 	      { | ||||||
| 		if (yyvsp[0].Number>10000) | 		if (yyvsp[0].Number>10000) | ||||||
| 		  { | 		  { | ||||||
| 		    yyHaveDate++; | 		    context->yyHaveDate++; | ||||||
| 		    yyDay= (yyvsp[0].Number)%100; | 		    context->yyDay= (yyvsp[0].Number)%100; | ||||||
| 		    yyMonth= (yyvsp[0].Number/100)%100; | 		    context->yyMonth= (yyvsp[0].Number/100)%100; | ||||||
| 		    yyYear = yyvsp[0].Number/10000; | 		    context->yyYear = yyvsp[0].Number/10000; | ||||||
| 		  } | 		  } | ||||||
| 		else | 		else | ||||||
| 		  { | 		  { | ||||||
| 		    yyHaveTime++; | 		    context->yyHaveTime++; | ||||||
| 		    if (yyvsp[0].Number < 100) | 		    if (yyvsp[0].Number < 100) | ||||||
| 		      { | 		      { | ||||||
| 			yyHour = yyvsp[0].Number; | 			context->yyHour = yyvsp[0].Number; | ||||||
| 			yyMinutes = 0; | 			context->yyMinutes = 0; | ||||||
| 		      } | 		      } | ||||||
| 		    else | 		    else | ||||||
| 		      { | 		      { | ||||||
| 		    	yyHour = yyvsp[0].Number / 100; | 		    	context->yyHour = yyvsp[0].Number / 100; | ||||||
| 		    	yyMinutes = yyvsp[0].Number % 100; | 		    	context->yyMinutes = yyvsp[0].Number % 100; | ||||||
| 		      } | 		      } | ||||||
| 		    yySeconds = 0; | 		    context->yySeconds = 0; | ||||||
| 		    yyMeridian = MER24; | 		    context->yyMeridian = MER24; | ||||||
| 		  } | 		  } | ||||||
| 	      } | 	      } | ||||||
| 	  ; | 	  ; | ||||||
|     break;} |     break;} | ||||||
| case 49: | case 49: | ||||||
| #line 464 "getdate.y" | #line 477 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyval.Meridian = MER24; | 	    yyval.Meridian = MER24; | ||||||
| 	  ; | 	  ; | ||||||
|     break;} |     break;} | ||||||
| case 50: | case 50: | ||||||
| #line 468 "getdate.y" | #line 481 "getdate.y" | ||||||
| { | { | ||||||
| 	    yyval.Meridian = yyvsp[0].Meridian; | 	    yyval.Meridian = yyvsp[0].Meridian; | ||||||
| 	  ; | 	  ; | ||||||
| @@ -1516,7 +1526,7 @@ yyerrhandle: | |||||||
|     } |     } | ||||||
|   return 1; |   return 1; | ||||||
| } | } | ||||||
| #line 473 "getdate.y" | #line 486 "getdate.y" | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Include this file down here because bison inserts code above which | /* Include this file down here because bison inserts code above which | ||||||
| @@ -1772,7 +1782,8 @@ ToYear (Year) | |||||||
| } | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| LookupWord (buff) | LookupWord (yylval, buff) | ||||||
|  |      YYSTYPE *yylval; | ||||||
|      char *buff; |      char *buff; | ||||||
| { | { | ||||||
|   register char *p; |   register char *p; | ||||||
| @@ -1788,12 +1799,12 @@ LookupWord (buff) | |||||||
|  |  | ||||||
|   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) |   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) | ||||||
|     { |     { | ||||||
|       yylval.Meridian = MERam; |       yylval->Meridian = MERam; | ||||||
|       return tMERIDIAN; |       return tMERIDIAN; | ||||||
|     } |     } | ||||||
|   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) |   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) | ||||||
|     { |     { | ||||||
|       yylval.Meridian = MERpm; |       yylval->Meridian = MERpm; | ||||||
|       return tMERIDIAN; |       return tMERIDIAN; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1814,13 +1825,13 @@ LookupWord (buff) | |||||||
| 	{ | 	{ | ||||||
| 	  if (strncmp (buff, tp->name, 3) == 0) | 	  if (strncmp (buff, tp->name, 3) == 0) | ||||||
| 	    { | 	    { | ||||||
| 	      yylval.Number = tp->value; | 	      yylval->Number = tp->value; | ||||||
| 	      return tp->type; | 	      return tp->type; | ||||||
| 	    } | 	    } | ||||||
| 	} | 	} | ||||||
|       else if (strcmp (buff, tp->name) == 0) |       else if (strcmp (buff, tp->name) == 0) | ||||||
| 	{ | 	{ | ||||||
| 	  yylval.Number = tp->value; | 	  yylval->Number = tp->value; | ||||||
| 	  return tp->type; | 	  return tp->type; | ||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
| @@ -1828,7 +1839,7 @@ LookupWord (buff) | |||||||
|   for (tp = TimezoneTable; tp->name; tp++) |   for (tp = TimezoneTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -1838,7 +1849,7 @@ LookupWord (buff) | |||||||
|   for (tp = UnitsTable; tp->name; tp++) |   for (tp = UnitsTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -1850,7 +1861,7 @@ LookupWord (buff) | |||||||
|       for (tp = UnitsTable; tp->name; tp++) |       for (tp = UnitsTable; tp->name; tp++) | ||||||
| 	if (strcmp (buff, tp->name) == 0) | 	if (strcmp (buff, tp->name) == 0) | ||||||
| 	  { | 	  { | ||||||
| 	    yylval.Number = tp->value; | 	    yylval->Number = tp->value; | ||||||
| 	    return tp->type; | 	    return tp->type; | ||||||
| 	  } | 	  } | ||||||
|       buff[i] = 's';		/* Put back for "this" in OtherTable. */ |       buff[i] = 's';		/* Put back for "this" in OtherTable. */ | ||||||
| @@ -1859,7 +1870,7 @@ LookupWord (buff) | |||||||
|   for (tp = OtherTable; tp->name; tp++) |   for (tp = OtherTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -1869,7 +1880,7 @@ LookupWord (buff) | |||||||
|       for (tp = MilitaryTable; tp->name; tp++) |       for (tp = MilitaryTable; tp->name; tp++) | ||||||
| 	if (strcmp (buff, tp->name) == 0) | 	if (strcmp (buff, tp->name) == 0) | ||||||
| 	  { | 	  { | ||||||
| 	    yylval.Number = tp->value; | 	    yylval->Number = tp->value; | ||||||
| 	    return tp->type; | 	    return tp->type; | ||||||
| 	  } | 	  } | ||||||
|     } |     } | ||||||
| @@ -1885,7 +1896,7 @@ LookupWord (buff) | |||||||
|     for (tp = TimezoneTable; tp->name; tp++) |     for (tp = TimezoneTable; tp->name; tp++) | ||||||
|       if (strcmp (buff, tp->name) == 0) |       if (strcmp (buff, tp->name) == 0) | ||||||
| 	{ | 	{ | ||||||
| 	  yylval.Number = tp->value; | 	  yylval->Number = tp->value; | ||||||
| 	  return tp->type; | 	  return tp->type; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -1893,7 +1904,9 @@ LookupWord (buff) | |||||||
| } | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| yylex () | yylex (yylval, cookie) | ||||||
|  |      YYSTYPE *yylval; | ||||||
|  |      void *cookie; | ||||||
| { | { | ||||||
|   register unsigned char c; |   register unsigned char c; | ||||||
|   register char *p; |   register char *p; | ||||||
| @@ -1903,42 +1916,42 @@ yylex () | |||||||
|  |  | ||||||
|   for (;;) |   for (;;) | ||||||
|     { |     { | ||||||
|       while (ISSPACE ((unsigned char) *yyInput)) |       while (ISSPACE ((unsigned char) *context->yyInput)) | ||||||
| 	yyInput++; | 	context->yyInput++; | ||||||
|  |  | ||||||
|       if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') |       if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+') | ||||||
| 	{ | 	{ | ||||||
| 	  if (c == '-' || c == '+') | 	  if (c == '-' || c == '+') | ||||||
| 	    { | 	    { | ||||||
| 	      sign = c == '-' ? -1 : 1; | 	      sign = c == '-' ? -1 : 1; | ||||||
| 	      if (!ISDIGIT (*++yyInput)) | 	      if (!ISDIGIT (*++context->yyInput)) | ||||||
| 		/* skip the '-' sign */ | 		/* skip the '-' sign */ | ||||||
| 		continue; | 		continue; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    sign = 0; | 	    sign = 0; | ||||||
| 	  for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) | 	  for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);) | ||||||
| 	    yylval.Number = 10 * yylval.Number + c - '0'; | 	    yylval->Number = 10 * yylval->Number + c - '0'; | ||||||
| 	  yyInput--; | 	  context->yyInput--; | ||||||
| 	  if (sign < 0) | 	  if (sign < 0) | ||||||
| 	    yylval.Number = -yylval.Number; | 	    yylval->Number = -yylval->Number; | ||||||
| 	  return sign ? tSNUMBER : tUNUMBER; | 	  return sign ? tSNUMBER : tUNUMBER; | ||||||
| 	} | 	} | ||||||
|       if (ISALPHA (c)) |       if (ISALPHA (c)) | ||||||
| 	{ | 	{ | ||||||
| 	  for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) | 	  for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';) | ||||||
| 	    if (p < &buff[sizeof buff - 1]) | 	    if (p < &buff[sizeof buff - 1]) | ||||||
| 	      *p++ = c; | 	      *p++ = c; | ||||||
| 	  *p = '\0'; | 	  *p = '\0'; | ||||||
| 	  yyInput--; | 	  context->yyInput--; | ||||||
| 	  return LookupWord (buff); | 	  return LookupWord (yylval, buff); | ||||||
| 	} | 	} | ||||||
|       if (c != '(') |       if (c != '(') | ||||||
| 	return *yyInput++; | 	return *context->yyInput++; | ||||||
|       Count = 0; |       Count = 0; | ||||||
|       do |       do | ||||||
| 	{ | 	{ | ||||||
| 	  c = *yyInput++; | 	  c = *context->yyInput++; | ||||||
| 	  if (c == '\0') | 	  if (c == '\0') | ||||||
| 	    return c; | 	    return c; | ||||||
| 	  if (c == '(') | 	  if (c == '(') | ||||||
| @@ -1978,10 +1991,11 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| { | { | ||||||
|   struct tm tm, tm0, *tmp; |   struct tm tm, tm0, *tmp; | ||||||
|   time_t Start; |   time_t Start; | ||||||
|  |   CONTEXT cookie; | ||||||
| #ifdef HAVE_LOCALTIME_R | #ifdef HAVE_LOCALTIME_R | ||||||
|   struct tm keeptime; |   struct tm keeptime; | ||||||
| #endif | #endif | ||||||
|   yyInput = p; |   cookie.yyInput = p; | ||||||
|   Start = now ? *now : time ((time_t *) NULL); |   Start = now ? *now : time ((time_t *) NULL); | ||||||
| #ifdef HAVE_LOCALTIME_R | #ifdef HAVE_LOCALTIME_R | ||||||
|   tmp = (struct tm *)localtime_r(&Start, &keeptime); |   tmp = (struct tm *)localtime_r(&Start, &keeptime); | ||||||
| @@ -1990,52 +2004,55 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| #endif | #endif | ||||||
|   if (!tmp) |   if (!tmp) | ||||||
|     return -1; |     return -1; | ||||||
|   yyYear = tmp->tm_year + TM_YEAR_ORIGIN; |   cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN; | ||||||
|   yyMonth = tmp->tm_mon + 1; |   cookie.yyMonth = tmp->tm_mon + 1; | ||||||
|   yyDay = tmp->tm_mday; |   cookie.yyDay = tmp->tm_mday; | ||||||
|   yyHour = tmp->tm_hour; |   cookie.yyHour = tmp->tm_hour; | ||||||
|   yyMinutes = tmp->tm_min; |   cookie.yyMinutes = tmp->tm_min; | ||||||
|   yySeconds = tmp->tm_sec; |   cookie.yySeconds = tmp->tm_sec; | ||||||
|   tm.tm_isdst = tmp->tm_isdst; |   tm.tm_isdst = tmp->tm_isdst; | ||||||
|   yyMeridian = MER24; |   cookie.yyMeridian = MER24; | ||||||
|   yyRelSeconds = 0; |   cookie.yyRelSeconds = 0; | ||||||
|   yyRelMinutes = 0; |   cookie.yyRelMinutes = 0; | ||||||
|   yyRelHour = 0; |   cookie.yyRelHour = 0; | ||||||
|   yyRelDay = 0; |   cookie.yyRelDay = 0; | ||||||
|   yyRelMonth = 0; |   cookie.yyRelMonth = 0; | ||||||
|   yyRelYear = 0; |   cookie.yyRelYear = 0; | ||||||
|   yyHaveDate = 0; |   cookie.yyHaveDate = 0; | ||||||
|   yyHaveDay = 0; |   cookie.yyHaveDay = 0; | ||||||
|   yyHaveRel = 0; |   cookie.yyHaveRel = 0; | ||||||
|   yyHaveTime = 0; |   cookie.yyHaveTime = 0; | ||||||
|   yyHaveZone = 0; |   cookie.yyHaveZone = 0; | ||||||
|  |  | ||||||
|   if (yyparse () |   if (yyparse (&cookie) | ||||||
|       || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) |       || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 || | ||||||
|  |       cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1) | ||||||
|     return -1; |     return -1; | ||||||
|  |  | ||||||
|   tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; |   tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear; | ||||||
|   tm.tm_mon = yyMonth - 1 + yyRelMonth; |   tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth; | ||||||
|   tm.tm_mday = yyDay + yyRelDay; |   tm.tm_mday = cookie.yyDay + cookie.yyRelDay; | ||||||
|   if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) |   if (cookie.yyHaveTime || | ||||||
|  |       (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay)) | ||||||
|     { |     { | ||||||
|       tm.tm_hour = ToHour (yyHour, yyMeridian); |       tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian); | ||||||
|       if (tm.tm_hour < 0) |       if (tm.tm_hour < 0) | ||||||
| 	return -1; | 	return -1; | ||||||
|       tm.tm_min = yyMinutes; |       tm.tm_min = cookie.yyMinutes; | ||||||
|       tm.tm_sec = yySeconds; |       tm.tm_sec = cookie.yySeconds; | ||||||
|     } |     } | ||||||
|   else |   else | ||||||
|     { |     { | ||||||
|       tm.tm_hour = tm.tm_min = tm.tm_sec = 0; |       tm.tm_hour = tm.tm_min = tm.tm_sec = 0; | ||||||
|     } |     } | ||||||
|   tm.tm_hour += yyRelHour; |   tm.tm_hour += cookie.yyRelHour; | ||||||
|   tm.tm_min += yyRelMinutes; |   tm.tm_min += cookie.yyRelMinutes; | ||||||
|   tm.tm_sec += yyRelSeconds; |   tm.tm_sec += cookie.yyRelSeconds; | ||||||
|  |  | ||||||
|   /* Let mktime deduce tm_isdst if we have an absolute timestamp, |   /* Let mktime deduce tm_isdst if we have an absolute timestamp, | ||||||
|      or if the relative timestamp mentions days, months, or years.  */ |      or if the relative timestamp mentions days, months, or years.  */ | ||||||
|   if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) |   if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime | | ||||||
|  |       cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear) | ||||||
|     tm.tm_isdst = -1; |     tm.tm_isdst = -1; | ||||||
|  |  | ||||||
|   tm0 = tm; |   tm0 = tm; | ||||||
| @@ -2053,18 +2070,18 @@ curl_getdate (const char *p, const time_t *now) | |||||||
|          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time |          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time | ||||||
|          zone by 24 hours to compensate.  This algorithm assumes that |          zone by 24 hours to compensate.  This algorithm assumes that | ||||||
|          there is no DST transition within a day of the time_t boundaries.  */ |          there is no DST transition within a day of the time_t boundaries.  */ | ||||||
|       if (yyHaveZone) |       if (cookie.yyHaveZone) | ||||||
| 	{ | 	{ | ||||||
| 	  tm = tm0; | 	  tm = tm0; | ||||||
| 	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) | 	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) | ||||||
| 	    { | 	    { | ||||||
| 	      tm.tm_mday++; | 	      tm.tm_mday++; | ||||||
| 	      yyTimezone -= 24 * 60; | 	      cookie.yyTimezone -= 24 * 60; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    { | 	    { | ||||||
| 	      tm.tm_mday--; | 	      tm.tm_mday--; | ||||||
| 	      yyTimezone += 24 * 60; | 	      cookie.yyTimezone += 24 * 60; | ||||||
| 	    } | 	    } | ||||||
| 	  Start = mktime (&tm); | 	  Start = mktime (&tm); | ||||||
| 	} | 	} | ||||||
| @@ -2073,22 +2090,29 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| 	return Start; | 	return Start; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   if (yyHaveDay && !yyHaveDate) |   if (cookie.yyHaveDay && !cookie.yyHaveDate) | ||||||
|     { |     { | ||||||
|       tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 |       tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7 | ||||||
| 		     + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); | 		     + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal))); | ||||||
|       Start = mktime (&tm); |       Start = mktime (&tm); | ||||||
|       if (Start == (time_t) -1) |       if (Start == (time_t) -1) | ||||||
| 	return Start; | 	return Start; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   if (yyHaveZone) |   if (cookie.yyHaveZone) | ||||||
|     { |     { | ||||||
|       long delta; |       long delta; | ||||||
|       struct tm *gmt = gmtime (&Start); |       struct tm *gmt; | ||||||
|  | #ifdef HAVE_GMTIME_R | ||||||
|  |       /* thread-safe version */ | ||||||
|  |       struct tm keeptime; | ||||||
|  |       gmt = (struct tm *)gmtime_r(&Start, &keeptime); | ||||||
|  | #else | ||||||
|  |       gmt = gmtime(&Start); | ||||||
|  | #endif | ||||||
|       if (!gmt) |       if (!gmt) | ||||||
| 	return -1; | 	return -1; | ||||||
|       delta = yyTimezone * 60L + difftm (&tm, gmt); |       delta = cookie.yyTimezone * 60L + difftm (&tm, gmt); | ||||||
|       if ((Start + delta < Start) != (delta < 0)) |       if ((Start + delta < Start) != (delta < 0)) | ||||||
| 	return -1;		/* time_t overflow */ | 	return -1;		/* time_t overflow */ | ||||||
|       Start += delta; |       Start += delta; | ||||||
| @@ -2126,11 +2150,3 @@ main (ac, av) | |||||||
|   /* NOTREACHED */ |   /* NOTREACHED */ | ||||||
| } | } | ||||||
| #endif /* defined (TEST) */ | #endif /* defined (TEST) */ | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * local variables: |  | ||||||
|  * eval: (load-file "../curl-mode.el") |  | ||||||
|  * end: |  | ||||||
|  * vim600: fdm=marker |  | ||||||
|  * vim: et sw=2 ts=2 sts=2 tw=78 |  | ||||||
|  */ |  | ||||||
|   | |||||||
							
								
								
									
										419
									
								
								lib/getdate.y
									
									
									
									
									
								
							
							
						
						
									
										419
									
								
								lib/getdate.y
									
									
									
									
									
								
							| @@ -21,6 +21,11 @@ | |||||||
| # endif | # endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef YYDEBUG | ||||||
|  |   /* to satisfy gcc -Wundef, we set this to 0 */ | ||||||
|  | #define YYDEBUG 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* Since the code of getdate.y is not included in the Emacs executable | /* Since the code of getdate.y is not included in the Emacs executable | ||||||
|    itself, there is no need to #define static in this file.  Even if |    itself, there is no need to #define static in this file.  Even if | ||||||
|    the code were included in the Emacs executable, it probably |    the code were included in the Emacs executable, it probably | ||||||
| @@ -168,41 +173,48 @@ typedef enum _MERIDIAN { | |||||||
|     MERam, MERpm, MER24 |     MERam, MERpm, MER24 | ||||||
| } MERIDIAN; | } MERIDIAN; | ||||||
|  |  | ||||||
|  | /* parse results and input string */ | ||||||
|  | typedef struct _CONTEXT { | ||||||
|  |     const char	*yyInput; | ||||||
|  |     int		yyDayOrdinal; | ||||||
|  |     int		yyDayNumber; | ||||||
|  |     int		yyHaveDate; | ||||||
|  |     int		yyHaveDay; | ||||||
|  |     int		yyHaveRel; | ||||||
|  |     int		yyHaveTime; | ||||||
|  |     int		yyHaveZone; | ||||||
|  |     int		yyTimezone; | ||||||
|  |     int		yyDay; | ||||||
|  |     int		yyHour; | ||||||
|  |     int		yyMinutes; | ||||||
|  |     int		yyMonth; | ||||||
|  |     int		yySeconds; | ||||||
|  |     int		yyYear; | ||||||
|  |     MERIDIAN	yyMeridian; | ||||||
|  |     int		yyRelDay; | ||||||
|  |     int		yyRelHour; | ||||||
|  |     int		yyRelMinutes; | ||||||
|  |     int		yyRelMonth; | ||||||
|  |     int		yyRelSeconds; | ||||||
|  |     int		yyRelYear; | ||||||
|  | } CONTEXT; | ||||||
|  |  | ||||||
| /* | /* enable use of extra argument to yyparse and yylex which can be used to pass | ||||||
| **  Global variables.  We could get rid of most of these by using a good | **  in a user defined value (CONTEXT struct in our case) | ||||||
| **  union as the yacc stack.  (This routine was originally written before |  | ||||||
| **  yacc had the %union construct.)  Maybe someday; right now we only use |  | ||||||
| **  the %union very rarely. |  | ||||||
| */ | */ | ||||||
| static const char	*yyInput; | #define YYPARSE_PARAM cookie | ||||||
| static int	yyDayOrdinal; | #define YYLEX_PARAM cookie | ||||||
| static int	yyDayNumber; | #define context ((CONTEXT *) cookie) | ||||||
| static int	yyHaveDate; |  | ||||||
| static int	yyHaveDay; |  | ||||||
| static int	yyHaveRel; |  | ||||||
| static int	yyHaveTime; |  | ||||||
| static int	yyHaveZone; |  | ||||||
| static int	yyTimezone; |  | ||||||
| static int	yyDay; |  | ||||||
| static int	yyHour; |  | ||||||
| static int	yyMinutes; |  | ||||||
| static int	yyMonth; |  | ||||||
| static int	yySeconds; |  | ||||||
| static int	yyYear; |  | ||||||
| static MERIDIAN	yyMeridian; |  | ||||||
| static int	yyRelDay; |  | ||||||
| static int	yyRelHour; |  | ||||||
| static int	yyRelMinutes; |  | ||||||
| static int	yyRelMonth; |  | ||||||
| static int	yyRelSeconds; |  | ||||||
| static int	yyRelYear; |  | ||||||
|  |  | ||||||
| %} | %} | ||||||
|  |  | ||||||
| /* This grammar has 13 shift/reduce conflicts. */ | /* This grammar has 13 shift/reduce conflicts. */ | ||||||
| %expect 13 | %expect 13 | ||||||
|  |  | ||||||
|  | /* turn global variables into locals, additionally enable extra arguments | ||||||
|  | ** for yylex (pointer to yylval and user defined value) | ||||||
|  | */ | ||||||
|  | %pure_parser | ||||||
|  |  | ||||||
| %union { | %union { | ||||||
|     int			Number; |     int			Number; | ||||||
|     enum _MERIDIAN	Meridian; |     enum _MERIDIAN	Meridian; | ||||||
| @@ -224,91 +236,91 @@ spec	: /* NULL */ | |||||||
| 	; | 	; | ||||||
|  |  | ||||||
| item	: time { | item	: time { | ||||||
| 	    yyHaveTime++; | 	    context->yyHaveTime++; | ||||||
| 	} | 	} | ||||||
| 	| zone { | 	| zone { | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	} | 	} | ||||||
| 	| date { | 	| date { | ||||||
| 	    yyHaveDate++; | 	    context->yyHaveDate++; | ||||||
| 	} | 	} | ||||||
| 	| day { | 	| day { | ||||||
| 	    yyHaveDay++; | 	    context->yyHaveDay++; | ||||||
| 	} | 	} | ||||||
| 	| rel { | 	| rel { | ||||||
| 	    yyHaveRel++; | 	    context->yyHaveRel++; | ||||||
| 	} | 	} | ||||||
| 	| number | 	| number | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| time	: tUNUMBER tMERIDIAN { | time	: tUNUMBER tMERIDIAN { | ||||||
| 	    yyHour = $1; | 	    context->yyHour = $1; | ||||||
| 	    yyMinutes = 0; | 	    context->yyMinutes = 0; | ||||||
| 	    yySeconds = 0; | 	    context->yySeconds = 0; | ||||||
| 	    yyMeridian = $2; | 	    context->yyMeridian = $2; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER ':' tUNUMBER o_merid { | 	| tUNUMBER ':' tUNUMBER o_merid { | ||||||
| 	    yyHour = $1; | 	    context->yyHour = $1; | ||||||
| 	    yyMinutes = $3; | 	    context->yyMinutes = $3; | ||||||
| 	    yySeconds = 0; | 	    context->yySeconds = 0; | ||||||
| 	    yyMeridian = $4; | 	    context->yyMeridian = $4; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER ':' tUNUMBER tSNUMBER { | 	| tUNUMBER ':' tUNUMBER tSNUMBER { | ||||||
| 	    yyHour = $1; | 	    context->yyHour = $1; | ||||||
| 	    yyMinutes = $3; | 	    context->yyMinutes = $3; | ||||||
| 	    yyMeridian = MER24; | 	    context->yyMeridian = MER24; | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	    yyTimezone = ($4 < 0 | 	    context->yyTimezone = ($4 < 0 | ||||||
| 			  ? -$4 % 100 + (-$4 / 100) * 60 | 				   ? -$4 % 100 + (-$4 / 100) * 60 | ||||||
| 			  : - ($4 % 100 + ($4 / 100) * 60)); | 				   : - ($4 % 100 + ($4 / 100) * 60)); | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { | 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { | ||||||
| 	    yyHour = $1; | 	    context->yyHour = $1; | ||||||
| 	    yyMinutes = $3; | 	    context->yyMinutes = $3; | ||||||
| 	    yySeconds = $5; | 	    context->yySeconds = $5; | ||||||
| 	    yyMeridian = $6; | 	    context->yyMeridian = $6; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { | 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { | ||||||
| 	    yyHour = $1; | 	    context->yyHour = $1; | ||||||
| 	    yyMinutes = $3; | 	    context->yyMinutes = $3; | ||||||
| 	    yySeconds = $5; | 	    context->yySeconds = $5; | ||||||
| 	    yyMeridian = MER24; | 	    context->yyMeridian = MER24; | ||||||
| 	    yyHaveZone++; | 	    context->yyHaveZone++; | ||||||
| 	    yyTimezone = ($6 < 0 | 	    context->yyTimezone = ($6 < 0 | ||||||
| 			  ? -$6 % 100 + (-$6 / 100) * 60 | 				   ? -$6 % 100 + (-$6 / 100) * 60 | ||||||
| 			  : - ($6 % 100 + ($6 / 100) * 60)); | 				   : - ($6 % 100 + ($6 / 100) * 60)); | ||||||
| 	} | 	} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| zone	: tZONE { | zone	: tZONE { | ||||||
| 	    yyTimezone = $1; | 	    context->yyTimezone = $1; | ||||||
| 	} | 	} | ||||||
| 	| tDAYZONE { | 	| tDAYZONE { | ||||||
| 	    yyTimezone = $1 - 60; | 	    context->yyTimezone = $1 - 60; | ||||||
| 	} | 	} | ||||||
| 	| | 	| | ||||||
| 	  tZONE tDST { | 	  tZONE tDST { | ||||||
| 	    yyTimezone = $1 - 60; | 	    context->yyTimezone = $1 - 60; | ||||||
| 	} | 	} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| day	: tDAY { | day	: tDAY { | ||||||
| 	    yyDayOrdinal = 1; | 	    context->yyDayOrdinal = 1; | ||||||
| 	    yyDayNumber = $1; | 	    context->yyDayNumber = $1; | ||||||
| 	} | 	} | ||||||
| 	| tDAY ',' { | 	| tDAY ',' { | ||||||
| 	    yyDayOrdinal = 1; | 	    context->yyDayOrdinal = 1; | ||||||
| 	    yyDayNumber = $1; | 	    context->yyDayNumber = $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tDAY { | 	| tUNUMBER tDAY { | ||||||
| 	    yyDayOrdinal = $1; | 	    context->yyDayOrdinal = $1; | ||||||
| 	    yyDayNumber = $2; | 	    context->yyDayNumber = $2; | ||||||
| 	} | 	} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| date	: tUNUMBER '/' tUNUMBER { | date	: tUNUMBER '/' tUNUMBER { | ||||||
| 	    yyMonth = $1; | 	    context->yyMonth = $1; | ||||||
| 	    yyDay = $3; | 	    context->yyDay = $3; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER { | 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER { | ||||||
| 	  /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. | 	  /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. | ||||||
| @@ -317,144 +329,145 @@ date	: tUNUMBER '/' tUNUMBER { | |||||||
| 	     you want portability, use the ISO 8601 format.  */ | 	     you want portability, use the ISO 8601 format.  */ | ||||||
| 	  if ($1 >= 1000) | 	  if ($1 >= 1000) | ||||||
| 	    { | 	    { | ||||||
| 	      yyYear = $1; | 	      context->yyYear = $1; | ||||||
| 	      yyMonth = $3; | 	      context->yyMonth = $3; | ||||||
| 	      yyDay = $5; | 	      context->yyDay = $5; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    { | 	    { | ||||||
| 	      yyMonth = $1; | 	      context->yyMonth = $1; | ||||||
| 	      yyDay = $3; | 	      context->yyDay = $3; | ||||||
| 	      yyYear = $5; | 	      context->yyYear = $5; | ||||||
| 	    } | 	    } | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tSNUMBER tSNUMBER { | 	| tUNUMBER tSNUMBER tSNUMBER { | ||||||
| 	    /* ISO 8601 format.  yyyy-mm-dd.  */ | 	    /* ISO 8601 format.  yyyy-mm-dd.  */ | ||||||
| 	    yyYear = $1; | 	    context->yyYear = $1; | ||||||
| 	    yyMonth = -$2; | 	    context->yyMonth = -$2; | ||||||
| 	    yyDay = -$3; | 	    context->yyDay = -$3; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tMONTH tSNUMBER { | 	| tUNUMBER tMONTH tSNUMBER { | ||||||
| 	    /* e.g. 17-JUN-1992.  */ | 	    /* e.g. 17-JUN-1992.  */ | ||||||
| 	    yyDay = $1; | 	    context->yyDay = $1; | ||||||
| 	    yyMonth = $2; | 	    context->yyMonth = $2; | ||||||
| 	    yyYear = -$3; | 	    context->yyYear = -$3; | ||||||
| 	} | 	} | ||||||
| 	| tMONTH tUNUMBER { | 	| tMONTH tUNUMBER { | ||||||
| 	    yyMonth = $1; | 	    context->yyMonth = $1; | ||||||
| 	    yyDay = $2; | 	    context->yyDay = $2; | ||||||
| 	} | 	} | ||||||
| 	| tMONTH tUNUMBER ',' tUNUMBER { | 	| tMONTH tUNUMBER ',' tUNUMBER { | ||||||
| 	    yyMonth = $1; | 	    context->yyMonth = $1; | ||||||
| 	    yyDay = $2; | 	    context->yyDay = $2; | ||||||
| 	    yyYear = $4; | 	    context->yyYear = $4; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tMONTH { | 	| tUNUMBER tMONTH { | ||||||
| 	    yyMonth = $2; | 	    context->yyMonth = $2; | ||||||
| 	    yyDay = $1; | 	    context->yyDay = $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tMONTH tUNUMBER { | 	| tUNUMBER tMONTH tUNUMBER { | ||||||
| 	    yyMonth = $2; | 	    context->yyMonth = $2; | ||||||
| 	    yyDay = $1; | 	    context->yyDay = $1; | ||||||
| 	    yyYear = $3; | 	    context->yyYear = $3; | ||||||
| 	} | 	} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| rel	: relunit tAGO { | rel	: relunit tAGO { | ||||||
| 	    yyRelSeconds = -yyRelSeconds; | 	    context->yyRelSeconds = -context->yyRelSeconds; | ||||||
| 	    yyRelMinutes = -yyRelMinutes; | 	    context->yyRelMinutes = -context->yyRelMinutes; | ||||||
| 	    yyRelHour = -yyRelHour; | 	    context->yyRelHour = -context->yyRelHour; | ||||||
| 	    yyRelDay = -yyRelDay; | 	    context->yyRelDay = -context->yyRelDay; | ||||||
| 	    yyRelMonth = -yyRelMonth; | 	    context->yyRelMonth = -context->yyRelMonth; | ||||||
| 	    yyRelYear = -yyRelYear; | 	    context->yyRelYear = -context->yyRelYear; | ||||||
| 	} | 	} | ||||||
| 	| relunit | 	| relunit | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| relunit	: tUNUMBER tYEAR_UNIT { | relunit	: tUNUMBER tYEAR_UNIT { | ||||||
| 	    yyRelYear += $1 * $2; | 	    context->yyRelYear += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tYEAR_UNIT { | 	| tSNUMBER tYEAR_UNIT { | ||||||
| 	    yyRelYear += $1 * $2; | 	    context->yyRelYear += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tYEAR_UNIT { | 	| tYEAR_UNIT { | ||||||
| 	    yyRelYear += $1; | 	    context->yyRelYear += $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tMONTH_UNIT { | 	| tUNUMBER tMONTH_UNIT { | ||||||
| 	    yyRelMonth += $1 * $2; | 	    context->yyRelMonth += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tMONTH_UNIT { | 	| tSNUMBER tMONTH_UNIT { | ||||||
| 	    yyRelMonth += $1 * $2; | 	    context->yyRelMonth += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tMONTH_UNIT { | 	| tMONTH_UNIT { | ||||||
| 	    yyRelMonth += $1; | 	    context->yyRelMonth += $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tDAY_UNIT { | 	| tUNUMBER tDAY_UNIT { | ||||||
| 	    yyRelDay += $1 * $2; | 	    context->yyRelDay += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tDAY_UNIT { | 	| tSNUMBER tDAY_UNIT { | ||||||
| 	    yyRelDay += $1 * $2; | 	    context->yyRelDay += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tDAY_UNIT { | 	| tDAY_UNIT { | ||||||
| 	    yyRelDay += $1; | 	    context->yyRelDay += $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tHOUR_UNIT { | 	| tUNUMBER tHOUR_UNIT { | ||||||
| 	    yyRelHour += $1 * $2; | 	    context->yyRelHour += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tHOUR_UNIT { | 	| tSNUMBER tHOUR_UNIT { | ||||||
| 	    yyRelHour += $1 * $2; | 	    context->yyRelHour += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tHOUR_UNIT { | 	| tHOUR_UNIT { | ||||||
| 	    yyRelHour += $1; | 	    context->yyRelHour += $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tMINUTE_UNIT { | 	| tUNUMBER tMINUTE_UNIT { | ||||||
| 	    yyRelMinutes += $1 * $2; | 	    context->yyRelMinutes += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tMINUTE_UNIT { | 	| tSNUMBER tMINUTE_UNIT { | ||||||
| 	    yyRelMinutes += $1 * $2; | 	    context->yyRelMinutes += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tMINUTE_UNIT { | 	| tMINUTE_UNIT { | ||||||
| 	    yyRelMinutes += $1; | 	    context->yyRelMinutes += $1; | ||||||
| 	} | 	} | ||||||
| 	| tUNUMBER tSEC_UNIT { | 	| tUNUMBER tSEC_UNIT { | ||||||
| 	    yyRelSeconds += $1 * $2; | 	    context->yyRelSeconds += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSNUMBER tSEC_UNIT { | 	| tSNUMBER tSEC_UNIT { | ||||||
| 	    yyRelSeconds += $1 * $2; | 	    context->yyRelSeconds += $1 * $2; | ||||||
| 	} | 	} | ||||||
| 	| tSEC_UNIT { | 	| tSEC_UNIT { | ||||||
| 	    yyRelSeconds += $1; | 	    context->yyRelSeconds += $1; | ||||||
| 	} | 	} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| number	: tUNUMBER | number	: tUNUMBER | ||||||
|           { |           { | ||||||
| 	    if (yyHaveTime && yyHaveDate && !yyHaveRel) | 	    if (context->yyHaveTime && context->yyHaveDate && | ||||||
| 	      yyYear = $1; | 		!context->yyHaveRel) | ||||||
|  | 	      context->yyYear = $1; | ||||||
| 	    else | 	    else | ||||||
| 	      { | 	      { | ||||||
| 		if ($1>10000) | 		if ($1>10000) | ||||||
| 		  { | 		  { | ||||||
| 		    yyHaveDate++; | 		    context->yyHaveDate++; | ||||||
| 		    yyDay= ($1)%100; | 		    context->yyDay= ($1)%100; | ||||||
| 		    yyMonth= ($1/100)%100; | 		    context->yyMonth= ($1/100)%100; | ||||||
| 		    yyYear = $1/10000; | 		    context->yyYear = $1/10000; | ||||||
| 		  } | 		  } | ||||||
| 		else | 		else | ||||||
| 		  { | 		  { | ||||||
| 		    yyHaveTime++; | 		    context->yyHaveTime++; | ||||||
| 		    if ($1 < 100) | 		    if ($1 < 100) | ||||||
| 		      { | 		      { | ||||||
| 			yyHour = $1; | 			context->yyHour = $1; | ||||||
| 			yyMinutes = 0; | 			context->yyMinutes = 0; | ||||||
| 		      } | 		      } | ||||||
| 		    else | 		    else | ||||||
| 		      { | 		      { | ||||||
| 		    	yyHour = $1 / 100; | 		    	context->yyHour = $1 / 100; | ||||||
| 		    	yyMinutes = $1 % 100; | 		    	context->yyMinutes = $1 % 100; | ||||||
| 		      } | 		      } | ||||||
| 		    yySeconds = 0; | 		    context->yySeconds = 0; | ||||||
| 		    yyMeridian = MER24; | 		    context->yyMeridian = MER24; | ||||||
| 		  } | 		  } | ||||||
| 	      } | 	      } | ||||||
| 	  } | 	  } | ||||||
| @@ -725,7 +738,8 @@ ToYear (Year) | |||||||
| } | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| LookupWord (buff) | LookupWord (yylval, buff) | ||||||
|  |      YYSTYPE *yylval; | ||||||
|      char *buff; |      char *buff; | ||||||
| { | { | ||||||
|   register char *p; |   register char *p; | ||||||
| @@ -741,12 +755,12 @@ LookupWord (buff) | |||||||
|  |  | ||||||
|   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) |   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) | ||||||
|     { |     { | ||||||
|       yylval.Meridian = MERam; |       yylval->Meridian = MERam; | ||||||
|       return tMERIDIAN; |       return tMERIDIAN; | ||||||
|     } |     } | ||||||
|   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) |   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) | ||||||
|     { |     { | ||||||
|       yylval.Meridian = MERpm; |       yylval->Meridian = MERpm; | ||||||
|       return tMERIDIAN; |       return tMERIDIAN; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -767,13 +781,13 @@ LookupWord (buff) | |||||||
| 	{ | 	{ | ||||||
| 	  if (strncmp (buff, tp->name, 3) == 0) | 	  if (strncmp (buff, tp->name, 3) == 0) | ||||||
| 	    { | 	    { | ||||||
| 	      yylval.Number = tp->value; | 	      yylval->Number = tp->value; | ||||||
| 	      return tp->type; | 	      return tp->type; | ||||||
| 	    } | 	    } | ||||||
| 	} | 	} | ||||||
|       else if (strcmp (buff, tp->name) == 0) |       else if (strcmp (buff, tp->name) == 0) | ||||||
| 	{ | 	{ | ||||||
| 	  yylval.Number = tp->value; | 	  yylval->Number = tp->value; | ||||||
| 	  return tp->type; | 	  return tp->type; | ||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
| @@ -781,7 +795,7 @@ LookupWord (buff) | |||||||
|   for (tp = TimezoneTable; tp->name; tp++) |   for (tp = TimezoneTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -791,7 +805,7 @@ LookupWord (buff) | |||||||
|   for (tp = UnitsTable; tp->name; tp++) |   for (tp = UnitsTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -803,7 +817,7 @@ LookupWord (buff) | |||||||
|       for (tp = UnitsTable; tp->name; tp++) |       for (tp = UnitsTable; tp->name; tp++) | ||||||
| 	if (strcmp (buff, tp->name) == 0) | 	if (strcmp (buff, tp->name) == 0) | ||||||
| 	  { | 	  { | ||||||
| 	    yylval.Number = tp->value; | 	    yylval->Number = tp->value; | ||||||
| 	    return tp->type; | 	    return tp->type; | ||||||
| 	  } | 	  } | ||||||
|       buff[i] = 's';		/* Put back for "this" in OtherTable. */ |       buff[i] = 's';		/* Put back for "this" in OtherTable. */ | ||||||
| @@ -812,7 +826,7 @@ LookupWord (buff) | |||||||
|   for (tp = OtherTable; tp->name; tp++) |   for (tp = OtherTable; tp->name; tp++) | ||||||
|     if (strcmp (buff, tp->name) == 0) |     if (strcmp (buff, tp->name) == 0) | ||||||
|       { |       { | ||||||
| 	yylval.Number = tp->value; | 	yylval->Number = tp->value; | ||||||
| 	return tp->type; | 	return tp->type; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -822,7 +836,7 @@ LookupWord (buff) | |||||||
|       for (tp = MilitaryTable; tp->name; tp++) |       for (tp = MilitaryTable; tp->name; tp++) | ||||||
| 	if (strcmp (buff, tp->name) == 0) | 	if (strcmp (buff, tp->name) == 0) | ||||||
| 	  { | 	  { | ||||||
| 	    yylval.Number = tp->value; | 	    yylval->Number = tp->value; | ||||||
| 	    return tp->type; | 	    return tp->type; | ||||||
| 	  } | 	  } | ||||||
|     } |     } | ||||||
| @@ -838,7 +852,7 @@ LookupWord (buff) | |||||||
|     for (tp = TimezoneTable; tp->name; tp++) |     for (tp = TimezoneTable; tp->name; tp++) | ||||||
|       if (strcmp (buff, tp->name) == 0) |       if (strcmp (buff, tp->name) == 0) | ||||||
| 	{ | 	{ | ||||||
| 	  yylval.Number = tp->value; | 	  yylval->Number = tp->value; | ||||||
| 	  return tp->type; | 	  return tp->type; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -846,7 +860,9 @@ LookupWord (buff) | |||||||
| } | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| yylex () | yylex (yylval, cookie) | ||||||
|  |      YYSTYPE *yylval; | ||||||
|  |      void *cookie; | ||||||
| { | { | ||||||
|   register unsigned char c; |   register unsigned char c; | ||||||
|   register char *p; |   register char *p; | ||||||
| @@ -856,42 +872,42 @@ yylex () | |||||||
|  |  | ||||||
|   for (;;) |   for (;;) | ||||||
|     { |     { | ||||||
|       while (ISSPACE ((unsigned char) *yyInput)) |       while (ISSPACE ((unsigned char) *context->yyInput)) | ||||||
| 	yyInput++; | 	context->yyInput++; | ||||||
|  |  | ||||||
|       if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') |       if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+') | ||||||
| 	{ | 	{ | ||||||
| 	  if (c == '-' || c == '+') | 	  if (c == '-' || c == '+') | ||||||
| 	    { | 	    { | ||||||
| 	      sign = c == '-' ? -1 : 1; | 	      sign = c == '-' ? -1 : 1; | ||||||
| 	      if (!ISDIGIT (*++yyInput)) | 	      if (!ISDIGIT (*++context->yyInput)) | ||||||
| 		/* skip the '-' sign */ | 		/* skip the '-' sign */ | ||||||
| 		continue; | 		continue; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    sign = 0; | 	    sign = 0; | ||||||
| 	  for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) | 	  for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);) | ||||||
| 	    yylval.Number = 10 * yylval.Number + c - '0'; | 	    yylval->Number = 10 * yylval->Number + c - '0'; | ||||||
| 	  yyInput--; | 	  context->yyInput--; | ||||||
| 	  if (sign < 0) | 	  if (sign < 0) | ||||||
| 	    yylval.Number = -yylval.Number; | 	    yylval->Number = -yylval->Number; | ||||||
| 	  return sign ? tSNUMBER : tUNUMBER; | 	  return sign ? tSNUMBER : tUNUMBER; | ||||||
| 	} | 	} | ||||||
|       if (ISALPHA (c)) |       if (ISALPHA (c)) | ||||||
| 	{ | 	{ | ||||||
| 	  for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) | 	  for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';) | ||||||
| 	    if (p < &buff[sizeof buff - 1]) | 	    if (p < &buff[sizeof buff - 1]) | ||||||
| 	      *p++ = c; | 	      *p++ = c; | ||||||
| 	  *p = '\0'; | 	  *p = '\0'; | ||||||
| 	  yyInput--; | 	  context->yyInput--; | ||||||
| 	  return LookupWord (buff); | 	  return LookupWord (yylval, buff); | ||||||
| 	} | 	} | ||||||
|       if (c != '(') |       if (c != '(') | ||||||
| 	return *yyInput++; | 	return *context->yyInput++; | ||||||
|       Count = 0; |       Count = 0; | ||||||
|       do |       do | ||||||
| 	{ | 	{ | ||||||
| 	  c = *yyInput++; | 	  c = *context->yyInput++; | ||||||
| 	  if (c == '\0') | 	  if (c == '\0') | ||||||
| 	    return c; | 	    return c; | ||||||
| 	  if (c == '(') | 	  if (c == '(') | ||||||
| @@ -931,10 +947,11 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| { | { | ||||||
|   struct tm tm, tm0, *tmp; |   struct tm tm, tm0, *tmp; | ||||||
|   time_t Start; |   time_t Start; | ||||||
|  |   CONTEXT cookie; | ||||||
| #ifdef HAVE_LOCALTIME_R | #ifdef HAVE_LOCALTIME_R | ||||||
|   struct tm keeptime; |   struct tm keeptime; | ||||||
| #endif | #endif | ||||||
|   yyInput = p; |   cookie.yyInput = p; | ||||||
|   Start = now ? *now : time ((time_t *) NULL); |   Start = now ? *now : time ((time_t *) NULL); | ||||||
| #ifdef HAVE_LOCALTIME_R | #ifdef HAVE_LOCALTIME_R | ||||||
|   tmp = (struct tm *)localtime_r(&Start, &keeptime); |   tmp = (struct tm *)localtime_r(&Start, &keeptime); | ||||||
| @@ -943,52 +960,55 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| #endif | #endif | ||||||
|   if (!tmp) |   if (!tmp) | ||||||
|     return -1; |     return -1; | ||||||
|   yyYear = tmp->tm_year + TM_YEAR_ORIGIN; |   cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN; | ||||||
|   yyMonth = tmp->tm_mon + 1; |   cookie.yyMonth = tmp->tm_mon + 1; | ||||||
|   yyDay = tmp->tm_mday; |   cookie.yyDay = tmp->tm_mday; | ||||||
|   yyHour = tmp->tm_hour; |   cookie.yyHour = tmp->tm_hour; | ||||||
|   yyMinutes = tmp->tm_min; |   cookie.yyMinutes = tmp->tm_min; | ||||||
|   yySeconds = tmp->tm_sec; |   cookie.yySeconds = tmp->tm_sec; | ||||||
|   tm.tm_isdst = tmp->tm_isdst; |   tm.tm_isdst = tmp->tm_isdst; | ||||||
|   yyMeridian = MER24; |   cookie.yyMeridian = MER24; | ||||||
|   yyRelSeconds = 0; |   cookie.yyRelSeconds = 0; | ||||||
|   yyRelMinutes = 0; |   cookie.yyRelMinutes = 0; | ||||||
|   yyRelHour = 0; |   cookie.yyRelHour = 0; | ||||||
|   yyRelDay = 0; |   cookie.yyRelDay = 0; | ||||||
|   yyRelMonth = 0; |   cookie.yyRelMonth = 0; | ||||||
|   yyRelYear = 0; |   cookie.yyRelYear = 0; | ||||||
|   yyHaveDate = 0; |   cookie.yyHaveDate = 0; | ||||||
|   yyHaveDay = 0; |   cookie.yyHaveDay = 0; | ||||||
|   yyHaveRel = 0; |   cookie.yyHaveRel = 0; | ||||||
|   yyHaveTime = 0; |   cookie.yyHaveTime = 0; | ||||||
|   yyHaveZone = 0; |   cookie.yyHaveZone = 0; | ||||||
|  |  | ||||||
|   if (yyparse () |   if (yyparse (&cookie) | ||||||
|       || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) |       || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 || | ||||||
|  |       cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1) | ||||||
|     return -1; |     return -1; | ||||||
|  |  | ||||||
|   tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; |   tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear; | ||||||
|   tm.tm_mon = yyMonth - 1 + yyRelMonth; |   tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth; | ||||||
|   tm.tm_mday = yyDay + yyRelDay; |   tm.tm_mday = cookie.yyDay + cookie.yyRelDay; | ||||||
|   if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) |   if (cookie.yyHaveTime || | ||||||
|  |       (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay)) | ||||||
|     { |     { | ||||||
|       tm.tm_hour = ToHour (yyHour, yyMeridian); |       tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian); | ||||||
|       if (tm.tm_hour < 0) |       if (tm.tm_hour < 0) | ||||||
| 	return -1; | 	return -1; | ||||||
|       tm.tm_min = yyMinutes; |       tm.tm_min = cookie.yyMinutes; | ||||||
|       tm.tm_sec = yySeconds; |       tm.tm_sec = cookie.yySeconds; | ||||||
|     } |     } | ||||||
|   else |   else | ||||||
|     { |     { | ||||||
|       tm.tm_hour = tm.tm_min = tm.tm_sec = 0; |       tm.tm_hour = tm.tm_min = tm.tm_sec = 0; | ||||||
|     } |     } | ||||||
|   tm.tm_hour += yyRelHour; |   tm.tm_hour += cookie.yyRelHour; | ||||||
|   tm.tm_min += yyRelMinutes; |   tm.tm_min += cookie.yyRelMinutes; | ||||||
|   tm.tm_sec += yyRelSeconds; |   tm.tm_sec += cookie.yyRelSeconds; | ||||||
|  |  | ||||||
|   /* Let mktime deduce tm_isdst if we have an absolute timestamp, |   /* Let mktime deduce tm_isdst if we have an absolute timestamp, | ||||||
|      or if the relative timestamp mentions days, months, or years.  */ |      or if the relative timestamp mentions days, months, or years.  */ | ||||||
|   if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) |   if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime | | ||||||
|  |       cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear) | ||||||
|     tm.tm_isdst = -1; |     tm.tm_isdst = -1; | ||||||
|  |  | ||||||
|   tm0 = tm; |   tm0 = tm; | ||||||
| @@ -1006,18 +1026,18 @@ curl_getdate (const char *p, const time_t *now) | |||||||
|          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time |          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time | ||||||
|          zone by 24 hours to compensate.  This algorithm assumes that |          zone by 24 hours to compensate.  This algorithm assumes that | ||||||
|          there is no DST transition within a day of the time_t boundaries.  */ |          there is no DST transition within a day of the time_t boundaries.  */ | ||||||
|       if (yyHaveZone) |       if (cookie.yyHaveZone) | ||||||
| 	{ | 	{ | ||||||
| 	  tm = tm0; | 	  tm = tm0; | ||||||
| 	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) | 	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) | ||||||
| 	    { | 	    { | ||||||
| 	      tm.tm_mday++; | 	      tm.tm_mday++; | ||||||
| 	      yyTimezone -= 24 * 60; | 	      cookie.yyTimezone -= 24 * 60; | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  else | ||||||
| 	    { | 	    { | ||||||
| 	      tm.tm_mday--; | 	      tm.tm_mday--; | ||||||
| 	      yyTimezone += 24 * 60; | 	      cookie.yyTimezone += 24 * 60; | ||||||
| 	    } | 	    } | ||||||
| 	  Start = mktime (&tm); | 	  Start = mktime (&tm); | ||||||
| 	} | 	} | ||||||
| @@ -1026,22 +1046,29 @@ curl_getdate (const char *p, const time_t *now) | |||||||
| 	return Start; | 	return Start; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   if (yyHaveDay && !yyHaveDate) |   if (cookie.yyHaveDay && !cookie.yyHaveDate) | ||||||
|     { |     { | ||||||
|       tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 |       tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7 | ||||||
| 		     + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); | 		     + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal))); | ||||||
|       Start = mktime (&tm); |       Start = mktime (&tm); | ||||||
|       if (Start == (time_t) -1) |       if (Start == (time_t) -1) | ||||||
| 	return Start; | 	return Start; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   if (yyHaveZone) |   if (cookie.yyHaveZone) | ||||||
|     { |     { | ||||||
|       long delta; |       long delta; | ||||||
|       struct tm *gmt = gmtime (&Start); |       struct tm *gmt; | ||||||
|  | #ifdef HAVE_GMTIME_R | ||||||
|  |       /* thread-safe version */ | ||||||
|  |       struct tm keeptime; | ||||||
|  |       gmt = (struct tm *)gmtime_r(&Start, &keeptime); | ||||||
|  | #else | ||||||
|  |       gmt = gmtime(&Start); | ||||||
|  | #endif | ||||||
|       if (!gmt) |       if (!gmt) | ||||||
| 	return -1; | 	return -1; | ||||||
|       delta = yyTimezone * 60L + difftm (&tm, gmt); |       delta = cookie.yyTimezone * 60L + difftm (&tm, gmt); | ||||||
|       if ((Start + delta < Start) != (delta < 0)) |       if ((Start + delta < Start) != (delta < 0)) | ||||||
| 	return -1;		/* time_t overflow */ | 	return -1;		/* time_t overflow */ | ||||||
|       Start += delta; |       Start += delta; | ||||||
|   | |||||||
| @@ -49,6 +49,12 @@ CURLcode Curl_initinfo(struct SessionHandle *data) | |||||||
|   info->httpversion=0; |   info->httpversion=0; | ||||||
|   info->filetime=-1; /* -1 is an illegal time and thus means unknown */ |   info->filetime=-1; /* -1 is an illegal time and thus means unknown */ | ||||||
|    |    | ||||||
|  |   if (info->contenttype) | ||||||
|  |     free(info->contenttype); | ||||||
|  |   info->contenttype = NULL; | ||||||
|  |  | ||||||
|  |   info->header_size = 0; | ||||||
|  |   info->request_size = 0; | ||||||
|   return CURLE_OK; |   return CURLE_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -132,6 +138,9 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) | |||||||
|   case CURLINFO_CONTENT_LENGTH_UPLOAD: |   case CURLINFO_CONTENT_LENGTH_UPLOAD: | ||||||
|     *param_doublep = data->progress.size_ul; |     *param_doublep = data->progress.size_ul; | ||||||
|     break; |     break; | ||||||
|  |   case CURLINFO_CONTENT_TYPE: | ||||||
|  |     *param_charp = data->info.contenttype; | ||||||
|  |     break; | ||||||
|   default: |   default: | ||||||
|     return CURLE_BAD_FUNCTION_ARGUMENT; |     return CURLE_BAD_FUNCTION_ARGUMENT; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -101,7 +101,10 @@ curl_hash_alloc(int slots, curl_hash_dtor dtor) | |||||||
| { | { | ||||||
|   curl_hash *h; |   curl_hash *h; | ||||||
|  |  | ||||||
|   h = malloc(sizeof(curl_hash)); |   h = (curl_hash *)malloc(sizeof(curl_hash)); | ||||||
|  |   if(NULL == h) | ||||||
|  |     return NULL; | ||||||
|  |  | ||||||
|   curl_hash_init(h, slots, dtor); |   curl_hash_init(h, slots, dtor); | ||||||
|  |  | ||||||
|   return h; |   return h; | ||||||
|   | |||||||
| @@ -25,8 +25,6 @@ | |||||||
|  |  | ||||||
| #include "setup.h" | #include "setup.h" | ||||||
|  |  | ||||||
| #include "setup.h" |  | ||||||
|  |  | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
|  |  | ||||||
| #include "llist.h" | #include "llist.h" | ||||||
|   | |||||||
							
								
								
									
										95
									
								
								lib/hostip.c
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								lib/hostip.c
									
									
									
									
									
								
							| @@ -60,6 +60,9 @@ | |||||||
| #include "hostip.h" | #include "hostip.h" | ||||||
| #include "hash.h" | #include "hash.h" | ||||||
|  |  | ||||||
|  | #define _MPRINTF_REPLACE /* use our functions only */ | ||||||
|  | #include <curl/mprintf.h> | ||||||
|  |  | ||||||
| #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) | #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) | ||||||
| #include "inet_ntoa_r.h" | #include "inet_ntoa_r.h" | ||||||
| #endif | #endif | ||||||
| @@ -98,13 +101,71 @@ struct curl_dns_cache_entry { | |||||||
|   time_t timestamp; |   time_t timestamp; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* count the number of characters that an integer takes up */ | ||||||
|  | static int _num_chars(int i) | ||||||
|  | { | ||||||
|  |   int chars = 0; | ||||||
|  |  | ||||||
|  |   /* While the number divided by 10 is greater than one,  | ||||||
|  |    * re-divide the number by 10, and increment the number of  | ||||||
|  |    * characters by 1. | ||||||
|  |    * | ||||||
|  |    * this relies on the fact that for every multiple of 10,  | ||||||
|  |    * a new digit is added onto every number | ||||||
|  |    */ | ||||||
|  |   do { | ||||||
|  |     chars++; | ||||||
|  |  | ||||||
|  |     i = (int) i / 10; | ||||||
|  |   } while (i >= 1); | ||||||
|  |  | ||||||
|  |   return chars; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Create a hostcache id */ | ||||||
|  | static char * | ||||||
|  | _create_hostcache_id(char *server, int port, ssize_t *entry_len) | ||||||
|  | { | ||||||
|  |   char *id = NULL; | ||||||
|  |  | ||||||
|  |   /* Get the length of the new entry id */ | ||||||
|  |   *entry_len = *entry_len +      /* Hostname length */ | ||||||
|  |                1 +               /* The ':' seperator */ | ||||||
|  |                _num_chars(port); /* The number of characters the port will take up */ | ||||||
|  |    | ||||||
|  |   /* Allocate the new entry id */ | ||||||
|  |   id = malloc(*entry_len + 1); | ||||||
|  |   if (!id) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /* Create the new entry */ | ||||||
|  |   /* If sprintf() doesn't return the entry length, that signals failure */ | ||||||
|  |   if (sprintf(id, "%s:%d", server, port) != *entry_len) { | ||||||
|  |     /* Free the allocated id, set length to zero and return NULL */ | ||||||
|  |     *entry_len = 0; | ||||||
|  |     free(id); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Macro to save redundant free'ing of entry_id */ | ||||||
|  | #define _hostcache_return(__v) \ | ||||||
|  | { \ | ||||||
|  |   free(entry_id); \ | ||||||
|  |   return (__v); \ | ||||||
|  | } | ||||||
|  |  | ||||||
| Curl_addrinfo *Curl_resolv(struct SessionHandle *data, | Curl_addrinfo *Curl_resolv(struct SessionHandle *data, | ||||||
|                            char *hostname, |                            char *hostname, | ||||||
|                            int port, |                            int port, | ||||||
|                            char **bufp) |                            char **bufp) | ||||||
| { | { | ||||||
|  |   char *entry_id = NULL; | ||||||
|   struct curl_dns_cache_entry *p = NULL; |   struct curl_dns_cache_entry *p = NULL; | ||||||
|   size_t hostname_len; |   ssize_t entry_len; | ||||||
|   time_t now; |   time_t now; | ||||||
|  |  | ||||||
|   /* If the host cache timeout is 0, we don't do DNS cach'ing |   /* If the host cache timeout is 0, we don't do DNS cach'ing | ||||||
| @@ -113,39 +174,47 @@ Curl_addrinfo *Curl_resolv(struct SessionHandle *data, | |||||||
|     return Curl_getaddrinfo(data, hostname, port, bufp); |     return Curl_getaddrinfo(data, hostname, port, bufp); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   hostname_len = strlen(hostname)+1; |   /* Create an entry id, based upon the hostname and port */ | ||||||
|  |   entry_len = strlen(hostname); | ||||||
|  |   entry_id = _create_hostcache_id(hostname, port, &entry_len); | ||||||
|  |   /* If we can't create the entry id, don't cache, just fall-through | ||||||
|  |      to the plain Curl_getaddrinfo() */ | ||||||
|  |   if (!entry_id) { | ||||||
|  |     return Curl_getaddrinfo(data, hostname, port, bufp); | ||||||
|  |   } | ||||||
|    |    | ||||||
|   time(&now); |   time(&now); | ||||||
|   /* See if its already in our dns cache */ |   /* See if its already in our dns cache */ | ||||||
|   if (curl_hash_find(data->hostcache, hostname, hostname_len, (void **) &p)) { |   if (entry_id && curl_hash_find(data->hostcache, entry_id, entry_len+1, (void **) &p)) { | ||||||
|     /* Do we need to check for a cache timeout? */ |     /* Do we need to check for a cache timeout? */ | ||||||
|     if (data->set.dns_cache_timeout != -1) { |     if (data->set.dns_cache_timeout != -1) { | ||||||
|       /* Return if the entry has not timed out */ |       /* Return if the entry has not timed out */ | ||||||
|       if ((now - p->timestamp) < data->set.dns_cache_timeout) { |       if ((now - p->timestamp) < data->set.dns_cache_timeout) { | ||||||
|         return p->addr; |         _hostcache_return(p->addr); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|       return p->addr; |       _hostcache_return(p->addr); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* Create a new cache entry */ |   /* Create a new cache entry */ | ||||||
|   p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry)); |   p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry)); | ||||||
|   if (!p) { |   if (!p) { | ||||||
|     return NULL; |    _hostcache_return(NULL); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   p->addr = Curl_getaddrinfo(data, hostname, port, bufp); |   p->addr = Curl_getaddrinfo(data, hostname, port, bufp); | ||||||
|   if (!p->addr) { |   if (!p->addr) { | ||||||
|     return NULL; |     free(p); | ||||||
|  |     _hostcache_return(NULL); | ||||||
|   } |   } | ||||||
|   p->timestamp = now; |   p->timestamp = now; | ||||||
|  |  | ||||||
|   /* Save it in our host cache */ |   /* Save it in our host cache */ | ||||||
|   curl_hash_update(data->hostcache, hostname, hostname_len, (const void *) p); |   curl_hash_update(data->hostcache, entry_id, entry_len+1, (const void *) p); | ||||||
|  |  | ||||||
|   return p->addr; |   _hostcache_return(p->addr); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -357,10 +426,10 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, | |||||||
|    * everything. OSF1 is known to require at least 8872 bytes. The buffer |    * everything. OSF1 is known to require at least 8872 bytes. The buffer | ||||||
|    * required for storing all possible aliases and IP numbers is according to |    * required for storing all possible aliases and IP numbers is according to | ||||||
|    * Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */ |    * Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */ | ||||||
|   char *buf = (char *)malloc(CURL_NAMELOOKUP_SIZE); |   int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); | ||||||
|   if(!buf) |   if(!buf) | ||||||
|     return NULL; /* major failure */ |     return NULL; /* major failure */ | ||||||
|   *bufp = buf; |   *bufp = (char *)buf; | ||||||
|  |  | ||||||
|   port=0; /* unused in IPv4 code */ |   port=0; /* unused in IPv4 code */ | ||||||
|   ret = 0; /* to prevent the compiler warning */ |   ret = 0; /* to prevent the compiler warning */ | ||||||
| @@ -390,7 +459,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, | |||||||
|     /* Solaris, IRIX and more */ |     /* Solaris, IRIX and more */ | ||||||
|     if ((h = gethostbyname_r(hostname, |     if ((h = gethostbyname_r(hostname, | ||||||
|                              (struct hostent *)buf, |                              (struct hostent *)buf, | ||||||
|                              buf + sizeof(struct hostent), |                              (char *)buf + sizeof(struct hostent), | ||||||
|                              CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), |                              CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), | ||||||
|                              &h_errnop)) == NULL ) |                              &h_errnop)) == NULL ) | ||||||
| #endif | #endif | ||||||
| @@ -398,7 +467,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, | |||||||
|     /* Linux */ |     /* Linux */ | ||||||
|     if( gethostbyname_r(hostname, |     if( gethostbyname_r(hostname, | ||||||
|                         (struct hostent *)buf, |                         (struct hostent *)buf, | ||||||
|                         buf + sizeof(struct hostent), |                         (char *)buf + sizeof(struct hostent), | ||||||
|                         CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), |                         CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), | ||||||
|                         &h, /* DIFFERENCE */ |                         &h, /* DIFFERENCE */ | ||||||
|                         &h_errnop)) |                         &h_errnop)) | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								lib/http.c
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								lib/http.c
									
									
									
									
									
								
							| @@ -128,8 +128,10 @@ static | |||||||
| CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, | CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, | ||||||
|                          long *bytes_written) |                          long *bytes_written) | ||||||
| { | { | ||||||
|   size_t amount; |   ssize_t amount; | ||||||
|   CURLcode result; |   CURLcode res; | ||||||
|  |   char *ptr; | ||||||
|  |   int size; | ||||||
|  |  | ||||||
|   if(conn->data->set.verbose) { |   if(conn->data->set.verbose) { | ||||||
|     fputs("> ", conn->data->set.err); |     fputs("> ", conn->data->set.err); | ||||||
| @@ -137,7 +139,25 @@ CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, | |||||||
|     fwrite(in->buffer, in->size_used, 1, conn->data->set.err); |     fwrite(in->buffer, in->size_used, 1, conn->data->set.err); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   result = Curl_write(conn, sockfd, in->buffer, in->size_used, &amount); |   /* The looping below is required since we use non-blocking sockets, but due | ||||||
|  |      to the circumstances we will just loop and try again and again etc */ | ||||||
|  |  | ||||||
|  |   ptr = in->buffer; | ||||||
|  |   size = in->size_used; | ||||||
|  |   do { | ||||||
|  |     res = Curl_write(conn, sockfd, ptr, size, &amount); | ||||||
|  |  | ||||||
|  |     if(CURLE_OK != res) | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     if(amount != size) { | ||||||
|  |       size += amount; | ||||||
|  |       ptr += amount; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |   } while(1); | ||||||
|  |  | ||||||
|   if(in->buffer) |   if(in->buffer) | ||||||
|     free(in->buffer); |     free(in->buffer); | ||||||
| @@ -145,7 +165,7 @@ CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, | |||||||
|  |  | ||||||
|   *bytes_written = amount; |   *bytes_written = amount; | ||||||
|  |  | ||||||
|   return result; |   return res; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -235,6 +255,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, | |||||||
|   int subversion=0; |   int subversion=0; | ||||||
|   struct SessionHandle *data=conn->data; |   struct SessionHandle *data=conn->data; | ||||||
|   CURLcode result; |   CURLcode result; | ||||||
|  |   int res; | ||||||
|  |  | ||||||
|   int nread;   /* total size read */ |   int nread;   /* total size read */ | ||||||
|   int perline; /* count bytes per line */ |   int perline; /* count bytes per line */ | ||||||
| @@ -317,8 +338,12 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, | |||||||
|        * to read, but when we use Curl_read() it may do so. Do confirm |        * to read, but when we use Curl_read() it may do so. Do confirm | ||||||
|        * that this is still ok and then remove this comment! |        * that this is still ok and then remove this comment! | ||||||
|        */ |        */ | ||||||
|       if(CURLE_OK != Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, |       res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, | ||||||
|                                &gotbytes)) |                      &gotbytes); | ||||||
|  |       if(res< 0) | ||||||
|  |         /* EWOULDBLOCK */ | ||||||
|  |         continue; /* go loop yourself */ | ||||||
|  |       else if(res) | ||||||
|         keepon = FALSE; |         keepon = FALSE; | ||||||
|       else if(gotbytes <= 0) { |       else if(gotbytes <= 0) { | ||||||
|         keepon = FALSE; |         keepon = FALSE; | ||||||
| @@ -828,6 +853,9 @@ CURLcode Curl_http(struct connectdata *conn) | |||||||
|           return CURLE_HTTP_POST_ERROR; |           return CURLE_HTTP_POST_ERROR; | ||||||
|         } |         } | ||||||
|         add_buffer(req_buffer, contentType, linelength); |         add_buffer(req_buffer, contentType, linelength); | ||||||
|  |  | ||||||
|  |         /* make the request end in a true CRLF */ | ||||||
|  |         add_buffer(req_buffer, "\r\n", 2); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       /* set upload size to the progress meter */ |       /* set upload size to the progress meter */ | ||||||
| @@ -910,12 +938,11 @@ CURLcode Curl_http(struct connectdata *conn) | |||||||
|           add_buffer(req_buffer, "\r\n", 2); |           add_buffer(req_buffer, "\r\n", 2); | ||||||
|           add_buffer(req_buffer, data->set.postfields, |           add_buffer(req_buffer, data->set.postfields, | ||||||
|                      data->set.postfieldsize); |                      data->set.postfieldsize); | ||||||
|           add_buffer(req_buffer, "\r\n", 2); |  | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|           add_bufferf(req_buffer, |           add_bufferf(req_buffer, | ||||||
|                       "\r\n" |                       "\r\n" | ||||||
|                       "%s\r\n", |                       "%s", | ||||||
|                       data->set.postfields ); |                       data->set.postfields ); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -46,7 +46,10 @@ curl_llist_alloc(curl_llist_dtor dtor) | |||||||
| { | { | ||||||
|   curl_llist *list; |   curl_llist *list; | ||||||
|  |  | ||||||
|   list = malloc(sizeof(curl_llist)); |   list = (curl_llist *)malloc(sizeof(curl_llist)); | ||||||
|  |   if(NULL == list) | ||||||
|  |     return NULL; | ||||||
|  |  | ||||||
|   curl_llist_init(list, dtor); |   curl_llist_init(list, dtor); | ||||||
|  |  | ||||||
|   return list; |   return list; | ||||||
|   | |||||||
| @@ -24,73 +24,11 @@ | |||||||
|  * - Max 128 parameters |  * - Max 128 parameters | ||||||
|  * - No 'long double' support. |  * - No 'long double' support. | ||||||
|  * |  * | ||||||
|  ************************************************************************* |  * If you ever want truly portable and good *printf() clones, the project that | ||||||
|  * |  * took on from here is named 'Trio' and you find more details on the trio web | ||||||
|  * |  * page at http://daniel.haxx.se/trio/ | ||||||
|  * 1998/01/10  (v2.8) |  */ | ||||||
|  *   Daniel |  | ||||||
|  *   - Updated version number. |  | ||||||
|  *   - Corrected a static non-zero prefixed width problem. |  | ||||||
|  * |  | ||||||
|  * 1998/11/17 - Daniel |  | ||||||
|  *   Added daprintf() and dvaprintf() for allocated printf() and vprintf(). |  | ||||||
|  *   They return an allocated buffer with the result inside. The result must |  | ||||||
|  *   be free()ed! |  | ||||||
|  * |  | ||||||
|  * 1998/08/23 - breese |  | ||||||
|  * |  | ||||||
|  *   Converted all non-printable (and non-whitespace) characters into |  | ||||||
|  *   their decimal ASCII value preceeded by a '\' character |  | ||||||
|  *   (this only applies to snprintf family so far) |  | ||||||
|  * |  | ||||||
|  *   Added %S (which is the same as %#s) |  | ||||||
|  * |  | ||||||
|  * 1998/05/05 (v2.7) |  | ||||||
|  * |  | ||||||
|  *   Fixed precision and width qualifiers (%.*s) |  | ||||||
|  * |  | ||||||
|  *   Added support for snprintf() |  | ||||||
|  * |  | ||||||
|  *   Quoting (%#s) is disabled for the (nil) pointer |  | ||||||
|  * |  | ||||||
|  * 1997/06/09 (v2.6) |  | ||||||
|  * |  | ||||||
|  *   %#s means that the string will be quoted with " |  | ||||||
|  *   (I was getting tired of writing \"%s\" all the time) |  | ||||||
|  * |  | ||||||
|  *   [ERR] for strings changed to (nil) |  | ||||||
|  * |  | ||||||
|  * v2.5 |  | ||||||
|  * - Added C++ support |  | ||||||
|  * - Prepended all internal functions with dprintf_ |  | ||||||
|  * - Defined the booleans |  | ||||||
|  * |  | ||||||
|  * v2.4 |  | ||||||
|  * - Added dvsprintf(), dvfprintf() and dvprintf(). |  | ||||||
|  * - Made the formatting function available with the name _formatf() to enable |  | ||||||
|  *   other *printf()-inspired functions. (I considered adding a dmsprintf() |  | ||||||
|  *   that works like sprintf() but allocates the destination string and |  | ||||||
|  *   possibly enlarges it itself, but things like that should be done with the |  | ||||||
|  *   new _formatf() instead.) |  | ||||||
|  * |  | ||||||
|  * v2.3 |  | ||||||
|  * - Small modifications to make it compile nicely at both Daniel's and |  | ||||||
|  *   Bjorn's place. |  | ||||||
|  * |  | ||||||
|  * v2.2 |  | ||||||
|  * - Made it work with text to the right of the last %! |  | ||||||
|  * - Introduced dprintf(), dsprintf() and dfprintf(). |  | ||||||
|  * - Float/double support enabled. This system is currently using the ordinary |  | ||||||
|  *   sprintf() function. NOTE that positional parameters, widths and precisions |  | ||||||
|  *   will still work like it should since the d-system takes care of that and |  | ||||||
|  *   passes that information re-formatted to the old sprintf(). |  | ||||||
|  * |  | ||||||
|  * v2.1 |  | ||||||
|  * - Fixed space padding (i.e %d was extra padded previously) |  | ||||||
|  * - long long output is supported |  | ||||||
|  * - alternate output is done correct like in %#08x |  | ||||||
|  * |  | ||||||
|  ****************************************************************************/ |  | ||||||
|  |  | ||||||
| #include "setup.h" | #include "setup.h" | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| @@ -100,6 +38,15 @@ | |||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|  | #ifndef SIZEOF_LONG_LONG | ||||||
|  | /* prevents warnings on picky compilers */ | ||||||
|  | #define SIZEOF_LONG_LONG 0 | ||||||
|  | #endif | ||||||
|  | #ifndef SIZEOF_LONG_DOUBLE | ||||||
|  | #define SIZEOF_LONG_DOUBLE 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| /* The last #include file should be: */ | /* The last #include file should be: */ | ||||||
| #ifdef MALLOCDEBUG | #ifdef MALLOCDEBUG | ||||||
| #include "memdebug.h" | #include "memdebug.h" | ||||||
| @@ -1191,7 +1138,7 @@ int main() | |||||||
| { | { | ||||||
|   char buffer[129]; |   char buffer[129]; | ||||||
|   char *ptr; |   char *ptr; | ||||||
| #ifdef SIZEOF_LONG_LONG | #if SIZEOF_LONG_LONG>0 | ||||||
|   long long hullo; |   long long hullo; | ||||||
|   dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65); |   dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65); | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -157,7 +157,7 @@ int Curl_parsenetrc(char *host, | |||||||
| 	  } | 	  } | ||||||
| 	  else if(state_password) { | 	  else if(state_password) { | ||||||
| 	    strncpy(password, tok, PASSWORDSIZE-1); | 	    strncpy(password, tok, PASSWORDSIZE-1); | ||||||
| #if _NETRC_DEBUG | #ifdef _NETRC_DEBUG | ||||||
| 	    printf("PASSWORD: %s\n", password); | 	    printf("PASSWORD: %s\n", password); | ||||||
| #endif | #endif | ||||||
| 	    state_password=0; | 	    state_password=0; | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								lib/sendf.c
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								lib/sendf.c
									
									
									
									
									
								
							| @@ -5,7 +5,7 @@ | |||||||
|  *                            | (__| |_| |  _ <| |___  |  *                            | (__| |_| |  _ <| |___  | ||||||
|  *                             \___|\___/|_| \_\_____| |  *                             \___|\___/|_| \_\_____| | ||||||
|  * |  * | ||||||
|  * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. |  * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. | ||||||
|  * |  * | ||||||
|  * In order to be useful for every potential user, curl and libcurl are |  * In order to be useful for every potential user, curl and libcurl are | ||||||
|  * dual-licensed under the MPL and the MIT/X-derivate licenses. |  * dual-licensed under the MPL and the MIT/X-derivate licenses. | ||||||
| @@ -26,6 +26,8 @@ | |||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
| #ifdef HAVE_SYS_TYPES_H | #ifdef HAVE_SYS_TYPES_H | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #endif | #endif | ||||||
| @@ -160,7 +162,7 @@ CURLcode Curl_sendf(int sockfd, struct connectdata *conn, | |||||||
|                     const char *fmt, ...) |                     const char *fmt, ...) | ||||||
| { | { | ||||||
|   struct SessionHandle *data = conn->data; |   struct SessionHandle *data = conn->data; | ||||||
|   size_t bytes_written; |   ssize_t bytes_written; | ||||||
|   CURLcode result; |   CURLcode result; | ||||||
|   char *s; |   char *s; | ||||||
|   va_list ap; |   va_list ap; | ||||||
| @@ -187,26 +189,32 @@ CURLcode Curl_sendf(int sockfd, struct connectdata *conn, | |||||||
|  */ |  */ | ||||||
| CURLcode Curl_write(struct connectdata *conn, int sockfd, | CURLcode Curl_write(struct connectdata *conn, int sockfd, | ||||||
|                     void *mem, size_t len, |                     void *mem, size_t len, | ||||||
|                     size_t *written) |                     ssize_t *written) | ||||||
| { | { | ||||||
|   size_t bytes_written; |   ssize_t bytes_written; | ||||||
|  |  | ||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
|   /* SSL_write() is said to return 'int' while write() and send() returns |   /* SSL_write() is said to return 'int' while write() and send() returns | ||||||
|      'size_t' */ |      'size_t' */ | ||||||
|   int ssl_bytes; |  | ||||||
|   if (conn->ssl.use) { |   if (conn->ssl.use) { | ||||||
|     int loop=100; /* just a precaution to never loop endlessly */ |     int err; | ||||||
|     while(loop--) { |     int rc = SSL_write(conn->ssl.handle, mem, len); | ||||||
|       ssl_bytes = SSL_write(conn->ssl.handle, mem, len); |  | ||||||
|       if((0 >= ssl_bytes) || |     if(rc < 0) { | ||||||
|          (SSL_ERROR_WANT_WRITE != SSL_get_error(conn->ssl.handle, |       err = SSL_get_error(conn->ssl.handle, rc); | ||||||
|                                                 ssl_bytes) )) { |      | ||||||
|         /* this converts from signed to unsigned... */ |       switch(err) { | ||||||
|         bytes_written = ssl_bytes; |       case SSL_ERROR_WANT_READ: | ||||||
|         break; |       case SSL_ERROR_WANT_WRITE: | ||||||
|  |         /* this is basicly the EWOULDBLOCK equivalent */ | ||||||
|  |         *written = 0; | ||||||
|  |         return CURLE_OK; | ||||||
|       } |       } | ||||||
|  |       /* a true error */ | ||||||
|  |       failf(conn->data, "SSL_write() return error %d\n", err); | ||||||
|  |       return CURLE_WRITE_ERROR; | ||||||
|     } |     } | ||||||
|  |     bytes_written = rc; | ||||||
|   } |   } | ||||||
|   else { |   else { | ||||||
| #endif | #endif | ||||||
| @@ -216,13 +224,27 @@ CURLcode Curl_write(struct connectdata *conn, int sockfd, | |||||||
|     } |     } | ||||||
|     else |     else | ||||||
| #endif /* KRB4 */ | #endif /* KRB4 */ | ||||||
|  |     { | ||||||
|       bytes_written = swrite(sockfd, mem, len); |       bytes_written = swrite(sockfd, mem, len); | ||||||
|  |     } | ||||||
|  |     if(-1 == bytes_written) { | ||||||
|  | #ifdef WIN32 | ||||||
|  |       if(WSAEWOULDBLOCK == GetLastError()) | ||||||
|  | #else | ||||||
|  |       if(EWOULDBLOCK == errno) | ||||||
|  | #endif | ||||||
|  |       { | ||||||
|  |         /* this is just a case of EWOULDBLOCK */ | ||||||
|  |         *written=0; | ||||||
|  |         return CURLE_OK; | ||||||
|  |       } | ||||||
|  |     } | ||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   *written = bytes_written; |   *written = bytes_written; | ||||||
|   return (bytes_written==len)?CURLE_OK:CURLE_WRITE_ERROR; |   return (-1 != bytes_written)?CURLE_OK:CURLE_WRITE_ERROR; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* client_write() sends data to the write callback(s) | /* client_write() sends data to the write callback(s) | ||||||
| @@ -266,26 +288,47 @@ CURLcode Curl_client_write(struct SessionHandle *data, | |||||||
|   return CURLE_OK; |   return CURLE_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Internal read-from-socket function. This is meant to deal with plain |  * Internal read-from-socket function. This is meant to deal with plain | ||||||
|  * sockets, SSL sockets and kerberos sockets. |  * sockets, SSL sockets and kerberos sockets. | ||||||
|  |  * | ||||||
|  |  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return | ||||||
|  |  * a regular CURLcode value. | ||||||
|  */ |  */ | ||||||
| CURLcode Curl_read(struct connectdata *conn, int sockfd, | int Curl_read(struct connectdata *conn, | ||||||
|                    char *buf, size_t buffersize, |               int sockfd, | ||||||
|                    ssize_t *n) |               char *buf, | ||||||
|  |               size_t buffersize, | ||||||
|  |               ssize_t *n) | ||||||
| { | { | ||||||
|   ssize_t nread; |   ssize_t nread; | ||||||
|  |  | ||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
|   if (conn->ssl.use) { |   if (conn->ssl.use) { | ||||||
|     int loop=100; /* just a precaution to never loop endlessly */ |     bool loop=TRUE; | ||||||
|     while(loop--) { |     int err; | ||||||
|  |     do { | ||||||
|       nread = SSL_read(conn->ssl.handle, buf, buffersize); |       nread = SSL_read(conn->ssl.handle, buf, buffersize); | ||||||
|       if((-1 != nread) || |  | ||||||
|          (SSL_ERROR_WANT_READ != SSL_get_error(conn->ssl.handle, nread) )) |       if(nread > 0) | ||||||
|  |         /* successful read */ | ||||||
|         break; |         break; | ||||||
|     } |  | ||||||
|  |       err = SSL_get_error(conn->ssl.handle, nread); | ||||||
|  |  | ||||||
|  |       switch(err) { | ||||||
|  |       case SSL_ERROR_NONE: /* this is not an error */ | ||||||
|  |       case SSL_ERROR_ZERO_RETURN: /* no more data */ | ||||||
|  |         loop=0; /* get out of loop */ | ||||||
|  |         break; | ||||||
|  |       case SSL_ERROR_WANT_READ: | ||||||
|  |       case SSL_ERROR_WANT_WRITE: | ||||||
|  |         /* if there's data pending, then we re-invoke SSL_read() */ | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } while(loop); | ||||||
|  |     if(loop && SSL_pending(conn->ssl.handle)) | ||||||
|  |       return -1; /* basicly EWOULDBLOCK */ | ||||||
|   } |   } | ||||||
|   else { |   else { | ||||||
| #endif | #endif | ||||||
| @@ -295,6 +338,16 @@ CURLcode Curl_read(struct connectdata *conn, int sockfd, | |||||||
|     else |     else | ||||||
| #endif | #endif | ||||||
|       nread = sread (sockfd, buf, buffersize); |       nread = sread (sockfd, buf, buffersize); | ||||||
|  |  | ||||||
|  |     if(-1 == nread) { | ||||||
|  | #ifdef WIN32 | ||||||
|  |       if(WSAEWOULDBLOCK == GetLastError()) | ||||||
|  | #else | ||||||
|  |       if(EWOULDBLOCK == errno) | ||||||
|  | #endif | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
|   } |   } | ||||||
| #endif /* USE_SSLEAY */ | #endif /* USE_SSLEAY */ | ||||||
|   | |||||||
| @@ -45,12 +45,12 @@ CURLcode Curl_client_write(struct SessionHandle *data, int type, char *ptr, | |||||||
|                            size_t len); |                            size_t len); | ||||||
|  |  | ||||||
| /* internal read-function, does plain socket, SSL and krb4 */ | /* internal read-function, does plain socket, SSL and krb4 */ | ||||||
| CURLcode Curl_read(struct connectdata *conn, int sockfd, | int Curl_read(struct connectdata *conn, int sockfd, | ||||||
|                    char *buf, size_t buffersize, |               char *buf, size_t buffersize, | ||||||
|                    ssize_t *n); |               ssize_t *n); | ||||||
| /* internal write-function, does plain socket, SSL and krb4 */ | /* internal write-function, does plain socket, SSL and krb4 */ | ||||||
| CURLcode Curl_write(struct connectdata *conn, int sockfd, | CURLcode Curl_write(struct connectdata *conn, int sockfd, | ||||||
|                     void *mem, size_t len, |                     void *mem, size_t len, | ||||||
|                     size_t *written); |                     ssize_t *written); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										61
									
								
								lib/ssluse.c
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								lib/ssluse.c
									
									
									
									
									
								
							| @@ -43,6 +43,12 @@ | |||||||
| #include "memdebug.h" | #include "memdebug.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #if OPENSSL_VERSION_NUMBER >= 0x0090581fL | ||||||
|  | #define HAVE_SSL_GET1_SESSION 1 | ||||||
|  | #else | ||||||
|  | #undef HAVE_SSL_GET1_SESSION | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #if OPENSSL_VERSION_NUMBER >= 0x00904100L | #if OPENSSL_VERSION_NUMBER >= 0x00904100L | ||||||
| #define HAVE_USERDATA_IN_PWD_CALLBACK 1 | #define HAVE_USERDATA_IN_PWD_CALLBACK 1 | ||||||
| #else | #else | ||||||
| @@ -74,10 +80,8 @@ static int passwd_callback(char *buf, int num, int verify | |||||||
| } | } | ||||||
|  |  | ||||||
| static | static | ||||||
| bool seed_enough(struct connectdata *conn, /* unused for now */ | bool seed_enough(int nread) | ||||||
|                  int nread) |  | ||||||
| { | { | ||||||
|   conn = NULL; /* to prevent compiler warnings */ |  | ||||||
| #ifdef HAVE_RAND_STATUS | #ifdef HAVE_RAND_STATUS | ||||||
|   nread = 0; /* to prevent compiler warnings */ |   nread = 0; /* to prevent compiler warnings */ | ||||||
|  |  | ||||||
| @@ -93,11 +97,10 @@ bool seed_enough(struct connectdata *conn, /* unused for now */ | |||||||
| } | } | ||||||
|  |  | ||||||
| static | static | ||||||
| int random_the_seed(struct connectdata *conn) | int random_the_seed(struct SessionHandle *data) | ||||||
| { | { | ||||||
|   char *buf = conn->data->state.buffer; /* point to the big buffer */ |   char *buf = data->state.buffer; /* point to the big buffer */ | ||||||
|   int nread=0; |   int nread=0; | ||||||
|   struct SessionHandle *data=conn->data; |  | ||||||
|  |  | ||||||
|   /* Q: should we add support for a random file name as a libcurl option? |   /* Q: should we add support for a random file name as a libcurl option? | ||||||
|      A: Yes, it is here */ |      A: Yes, it is here */ | ||||||
| @@ -113,7 +116,7 @@ int random_the_seed(struct connectdata *conn) | |||||||
|     nread += RAND_load_file((data->set.ssl.random_file? |     nread += RAND_load_file((data->set.ssl.random_file? | ||||||
|                              data->set.ssl.random_file:RANDOM_FILE), |                              data->set.ssl.random_file:RANDOM_FILE), | ||||||
|                             16384); |                             16384); | ||||||
|     if(seed_enough(conn, nread)) |     if(seed_enough(nread)) | ||||||
|       return nread; |       return nread; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -132,7 +135,7 @@ int random_the_seed(struct connectdata *conn) | |||||||
|     int ret = RAND_egd(data->set.ssl.egdsocket?data->set.ssl.egdsocket:EGD_SOCKET); |     int ret = RAND_egd(data->set.ssl.egdsocket?data->set.ssl.egdsocket:EGD_SOCKET); | ||||||
|     if(-1 != ret) { |     if(-1 != ret) { | ||||||
|       nread += ret; |       nread += ret; | ||||||
|       if(seed_enough(conn, nread)) |       if(seed_enough(nread)) | ||||||
|         return nread; |         return nread; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -164,11 +167,11 @@ int random_the_seed(struct connectdata *conn) | |||||||
|   if ( buf[0] ) { |   if ( buf[0] ) { | ||||||
|     /* we got a file name to try */ |     /* we got a file name to try */ | ||||||
|     nread += RAND_load_file(buf, 16384); |     nread += RAND_load_file(buf, 16384); | ||||||
|     if(seed_enough(conn, nread)) |     if(seed_enough(nread)) | ||||||
|       return nread; |       return nread; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   infof(conn->data, "Your connection is using a weak random seed!\n"); |   infof(data, "libcurl is now using a weak random seed!\n"); | ||||||
|   return nread; |   return nread; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -357,6 +360,10 @@ int cert_verify_callback(int ok, X509_STORE_CTX *ctx) | |||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
| /* "global" init done? */ | /* "global" init done? */ | ||||||
| static int init_ssl=0; | static int init_ssl=0; | ||||||
|  |  | ||||||
|  | /* we have the "SSL is seeded" boolean global for the application to | ||||||
|  |    prevent multiple time-consuming seedings in vain */ | ||||||
|  | static bool ssl_seeded = FALSE; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| /* Global init */ | /* Global init */ | ||||||
| @@ -552,23 +559,39 @@ int Curl_SSL_Close_All(struct SessionHandle *data) | |||||||
| static int Store_SSL_Session(struct connectdata *conn) | static int Store_SSL_Session(struct connectdata *conn) | ||||||
| { | { | ||||||
|   SSL_SESSION *ssl_sessionid; |   SSL_SESSION *ssl_sessionid; | ||||||
|   struct curl_ssl_session *store; |  | ||||||
|   int i; |   int i; | ||||||
|   struct SessionHandle *data=conn->data; /* the mother of all structs */ |   struct SessionHandle *data=conn->data; /* the mother of all structs */ | ||||||
|  |   struct curl_ssl_session *store = &data->state.session[0]; | ||||||
|   int oldest_age=data->state.session[0].age; /* zero if unused */ |   int oldest_age=data->state.session[0].age; /* zero if unused */ | ||||||
|  |  | ||||||
|   /* ask OpenSSL, say please */ |   /* ask OpenSSL, say please */ | ||||||
|  |  | ||||||
|  | #ifdef HAVE_SSL_GET1_SESSION | ||||||
|   ssl_sessionid = SSL_get1_session(conn->ssl.handle); |   ssl_sessionid = SSL_get1_session(conn->ssl.handle); | ||||||
|  |  | ||||||
|   /* SSL_get1_session() will increment the reference |   /* SSL_get1_session() will increment the reference | ||||||
|      count and the session will stay in memory until explicitly freed with |      count and the session will stay in memory until explicitly freed with | ||||||
|      SSL_SESSION_free(3), regardless of its state. */ |      SSL_SESSION_free(3), regardless of its state.  | ||||||
|  |      This function was introduced in openssl 0.9.5a. */ | ||||||
|  | #else | ||||||
|  |   ssl_sessionid = SSL_get_session(conn->ssl.handle); | ||||||
|  |  | ||||||
|  |   /* if SSL_get1_session() is unavailable, use SSL_get_session(). | ||||||
|  |      This is an inferior option because the session can be flushed | ||||||
|  |      at any time by openssl. It is included only so curl compiles | ||||||
|  |      under versions of openssl < 0.9.5a. | ||||||
|  |       | ||||||
|  |      WARNING: How curl behaves if it's session is flushed is | ||||||
|  |      untested.  | ||||||
|  |   */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   /* Now we should add the session ID and the host name to the cache, (remove |   /* Now we should add the session ID and the host name to the cache, (remove | ||||||
|      the oldest if necessary) */ |      the oldest if necessary) */ | ||||||
|  |  | ||||||
|   /* find an empty slot for us, or find the oldest */ |   /* find an empty slot for us, or find the oldest */ | ||||||
|   for(i=0; (i<data->set.ssl.numsessions) && data->state.session[i].sessionid; i++) { |   for(i=1; (i<data->set.ssl.numsessions) && | ||||||
|  |         data->state.session[i].sessionid; i++) { | ||||||
|     if(data->state.session[i].age < oldest_age) { |     if(data->state.session[i].age < oldest_age) { | ||||||
|       oldest_age = data->state.session[i].age; |       oldest_age = data->state.session[i].age; | ||||||
|       store = &data->state.session[i]; |       store = &data->state.session[i]; | ||||||
| @@ -655,8 +678,12 @@ Curl_SSLConnect(struct connectdata *conn) | |||||||
|   /* mark this is being ssl enabled from here on out. */ |   /* mark this is being ssl enabled from here on out. */ | ||||||
|   conn->ssl.use = TRUE; |   conn->ssl.use = TRUE; | ||||||
|  |  | ||||||
|   /* Make funny stuff to get random input */ |   if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { | ||||||
|   random_the_seed(conn); |     /* Make funny stuff to get random input */ | ||||||
|  |     random_the_seed(data); | ||||||
|  |  | ||||||
|  |     ssl_seeded = TRUE; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* check to see if we've been told to use an explicit SSL/TLS version */ |   /* check to see if we've been told to use an explicit SSL/TLS version */ | ||||||
|   switch(data->set.ssl.version) { |   switch(data->set.ssl.version) { | ||||||
| @@ -783,9 +810,11 @@ Curl_SSLConnect(struct connectdata *conn) | |||||||
|       /* subtract the passed time */ |       /* subtract the passed time */ | ||||||
|       timeout_ms -= (long)has_passed; |       timeout_ms -= (long)has_passed; | ||||||
|        |        | ||||||
|       if(timeout_ms < 0) |       if(timeout_ms < 0) { | ||||||
|         /* a precaution, no need to continue if time already is up */ |         /* a precaution, no need to continue if time already is up */ | ||||||
|  |         failf(data, "SSL connection timeout"); | ||||||
|         return CURLE_OPERATION_TIMEOUTED; |         return CURLE_OPERATION_TIMEOUTED; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|       /* no particular time-out has been set */ |       /* no particular time-out has been set */ | ||||||
|   | |||||||
| @@ -1090,7 +1090,7 @@ CURLcode Curl_telnet(struct connectdata *conn) | |||||||
|       { |       { | ||||||
|         unsigned char outbuf[2]; |         unsigned char outbuf[2]; | ||||||
|         int out_count = 0; |         int out_count = 0; | ||||||
|         size_t bytes_written; |         ssize_t bytes_written; | ||||||
|         char *buffer = buf; |         char *buffer = buf; | ||||||
|                |                | ||||||
|         if(!ReadFile(stdin_handle, buf, 255, &nread, NULL)) { |         if(!ReadFile(stdin_handle, buf, 255, &nread, NULL)) { | ||||||
| @@ -1116,6 +1116,7 @@ CURLcode Curl_telnet(struct connectdata *conn) | |||||||
|         { |         { | ||||||
|           if(events.lNetworkEvents & FD_READ) |           if(events.lNetworkEvents & FD_READ) | ||||||
|           { |           { | ||||||
|  |             /* This reallu OUGHT to check its return code. */ | ||||||
|             Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); |             Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); | ||||||
|              |              | ||||||
|             telrcv(conn, (unsigned char *)buf, nread); |             telrcv(conn, (unsigned char *)buf, nread); | ||||||
| @@ -1159,7 +1160,7 @@ CURLcode Curl_telnet(struct connectdata *conn) | |||||||
|       if(FD_ISSET(0, &readfd)) { /* read from stdin */ |       if(FD_ISSET(0, &readfd)) { /* read from stdin */ | ||||||
|         unsigned char outbuf[2]; |         unsigned char outbuf[2]; | ||||||
|         int out_count = 0; |         int out_count = 0; | ||||||
|         size_t bytes_written; |         ssize_t bytes_written; | ||||||
|         char *buffer = buf; |         char *buffer = buf; | ||||||
|          |          | ||||||
|         nread = read(0, buf, 255); |         nread = read(0, buf, 255); | ||||||
| @@ -1176,6 +1177,7 @@ CURLcode Curl_telnet(struct connectdata *conn) | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       if(FD_ISSET(sockfd, &readfd)) { |       if(FD_ISSET(sockfd, &readfd)) { | ||||||
|  |         /* This OUGHT to check the return code... */ | ||||||
|         Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); |         Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); | ||||||
|  |  | ||||||
|         /* if we receive 0 or less here, the server closed the connection and |         /* if we receive 0 or less here, the server closed the connection and | ||||||
|   | |||||||
							
								
								
									
										169
									
								
								lib/transfer.c
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								lib/transfer.c
									
									
									
									
									
								
							| @@ -173,7 +173,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
| { | { | ||||||
|   struct Curl_transfer_keeper *k = &conn->keep; |   struct Curl_transfer_keeper *k = &conn->keep; | ||||||
|   struct SessionHandle *data = conn->data; |   struct SessionHandle *data = conn->data; | ||||||
|   CURLcode result; |   int result; | ||||||
|   ssize_t nread; /* number of bytes read */ |   ssize_t nread; /* number of bytes read */ | ||||||
|   int didwhat=0; |   int didwhat=0; | ||||||
|  |  | ||||||
| @@ -181,25 +181,27 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|     if((k->keepon & KEEP_READ) && |     if((k->keepon & KEEP_READ) && | ||||||
|        FD_ISSET(conn->sockfd, &k->readfd)) { |        FD_ISSET(conn->sockfd, &k->readfd)) { | ||||||
|  |  | ||||||
|  |       /* read! */ | ||||||
|  |       result = Curl_read(conn, conn->sockfd, k->buf, | ||||||
|  |                          BUFSIZE -1, &nread); | ||||||
|  |  | ||||||
|  |       if(0>result) | ||||||
|  |         break; /* get out of loop */ | ||||||
|  |       if(result>0) | ||||||
|  |         return result; | ||||||
|  |  | ||||||
|       if ((k->bytecount == 0) && (k->writebytecount == 0)) |       if ((k->bytecount == 0) && (k->writebytecount == 0)) | ||||||
|         Curl_pgrsTime(data, TIMER_STARTTRANSFER); |         Curl_pgrsTime(data, TIMER_STARTTRANSFER); | ||||||
|  |  | ||||||
|       didwhat |= KEEP_READ; |       didwhat |= KEEP_READ; | ||||||
|  |  | ||||||
|       /* read! */ |  | ||||||
|       result = Curl_read(conn, conn->sockfd, k->buf, |  | ||||||
|                          BUFSIZE -1, &nread); |  | ||||||
|  |  | ||||||
|       if(result) |  | ||||||
|         return result; |  | ||||||
|  |  | ||||||
|       /* NULL terminate, allowing string ops to be used */ |       /* NULL terminate, allowing string ops to be used */ | ||||||
|       if (0 < (signed int) nread) |       if (0 < nread) | ||||||
|         k->buf[nread] = 0; |         k->buf[nread] = 0; | ||||||
|  |  | ||||||
|       /* if we receive 0 or less here, the server closed the connection and |       /* if we receive 0 or less here, the server closed the connection and | ||||||
|          we bail out from this! */ |          we bail out from this! */ | ||||||
|       else if (0 >= (signed int) nread) { |       else if (0 >= nread) { | ||||||
|         k->keepon &= ~KEEP_READ; |         k->keepon &= ~KEEP_READ; | ||||||
|         FD_ZERO(&k->rkeepfd); |         FD_ZERO(&k->rkeepfd); | ||||||
|         break; |         break; | ||||||
| @@ -310,6 +312,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|               k->headerline = 0; /* restart the header line counter */ |               k->headerline = 0; /* restart the header line counter */ | ||||||
|               /* if we did wait for this do enable write now! */ |               /* if we did wait for this do enable write now! */ | ||||||
|               if (k->write_after_100_header) { |               if (k->write_after_100_header) { | ||||||
|  |  | ||||||
|                 k->write_after_100_header = FALSE; |                 k->write_after_100_header = FALSE; | ||||||
|                 FD_SET (conn->writesockfd, &k->writefd); /* write */ |                 FD_SET (conn->writesockfd, &k->writefd); /* write */ | ||||||
|                 k->keepon |= KEEP_WRITE; |                 k->keepon |= KEEP_WRITE; | ||||||
| @@ -353,10 +356,11 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|                * If we requested a "no body", this is a good time to get |                * If we requested a "no body", this is a good time to get | ||||||
|                * out and return home. |                * out and return home. | ||||||
|                */ |                */ | ||||||
|               if(data->set.no_body) |               bool stop_reading = FALSE; | ||||||
|                 return CURLE_OK; |  | ||||||
|  |  | ||||||
|               if(!conn->bits.close) { |               if(data->set.no_body) | ||||||
|  |                 stop_reading = TRUE; | ||||||
|  |               else if(!conn->bits.close) { | ||||||
|                 /* If this is not the last request before a close, we must |                 /* If this is not the last request before a close, we must | ||||||
|                    set the maximum download size to the size of the |                    set the maximum download size to the size of the | ||||||
|                    expected document or else, we won't know when to stop |                    expected document or else, we won't know when to stop | ||||||
| @@ -367,10 +371,18 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|                 /* If max download size is *zero* (nothing) we already |                 /* If max download size is *zero* (nothing) we already | ||||||
|                    have nothing and can safely return ok now! */ |                    have nothing and can safely return ok now! */ | ||||||
|                 if(0 == conn->maxdownload) |                 if(0 == conn->maxdownload) | ||||||
|                   return CURLE_OK; |                   stop_reading = TRUE; | ||||||
|                      |                      | ||||||
|                 /* What to do if the size is *not* known? */ |                 /* What to do if the size is *not* known? */ | ||||||
|               } |               } | ||||||
|  |  | ||||||
|  |               if(stop_reading) { | ||||||
|  |                 /* we make sure that this socket isn't read more now */ | ||||||
|  |                 k->keepon &= ~KEEP_READ; | ||||||
|  |                 FD_ZERO(&k->rkeepfd); | ||||||
|  |                 return CURLE_OK; | ||||||
|  |               } | ||||||
|  |  | ||||||
|               break;		/* exit header line loop */ |               break;		/* exit header line loop */ | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -454,6 +466,31 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|             conn->size = k->contentlength; |             conn->size = k->contentlength; | ||||||
|             Curl_pgrsSetDownloadSize(data, k->contentlength); |             Curl_pgrsSetDownloadSize(data, k->contentlength); | ||||||
|           } |           } | ||||||
|  |           /* check for Content-Type: header lines to get the mime-type */ | ||||||
|  |           else if (strnequal("Content-Type:", k->p, 13)) { | ||||||
|  |             char *start; | ||||||
|  |             char *end; | ||||||
|  |             int len; | ||||||
|  |  | ||||||
|  |             /* Find the first non-space letter */ | ||||||
|  |             for(start=k->p+14; | ||||||
|  |                 *start && isspace((int)*start); | ||||||
|  |                 start++); | ||||||
|  |  | ||||||
|  |             /* count all non-space letters following */ | ||||||
|  |             for(end=start+1, len=0; | ||||||
|  |                 *end && !isspace((int)*end); | ||||||
|  |                 end++, len++); | ||||||
|  |  | ||||||
|  |             /* allocate memory of a cloned copy */ | ||||||
|  |             data->info.contenttype = malloc(len + 1); | ||||||
|  |             if (NULL == data->info.contenttype) | ||||||
|  | 	      return CURLE_OUT_OF_MEMORY; | ||||||
|  |  | ||||||
|  |             /* copy the content-type string */ | ||||||
|  | 	    memcpy(data->info.contenttype, start, len); | ||||||
|  |             data->info.contenttype[len] = 0; /* zero terminate */ | ||||||
|  |           } | ||||||
|           else if((k->httpversion == 10) && |           else if((k->httpversion == 10) && | ||||||
|                   conn->bits.httpproxy && |                   conn->bits.httpproxy && | ||||||
|                   compareheader(k->p, "Proxy-Connection:", "keep-alive")) { |                   compareheader(k->p, "Proxy-Connection:", "keep-alive")) { | ||||||
| @@ -588,7 +625,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|       /* This is not an 'else if' since it may be a rest from the header |       /* This is not an 'else if' since it may be a rest from the header | ||||||
|          parsing, where the beginning of the buffer is headers and the end |          parsing, where the beginning of the buffer is headers and the end | ||||||
|          is non-headers. */ |          is non-headers. */ | ||||||
|       if (k->str && !k->header && ((signed int)nread > 0)) { |       if (k->str && !k->header && (nread > 0)) { | ||||||
|              |              | ||||||
|         if(0 == k->bodywrites) { |         if(0 == k->bodywrites) { | ||||||
|           /* These checks are only made the first time we are about to |           /* These checks are only made the first time we are about to | ||||||
| @@ -598,6 +635,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|             if (conn->newurl) { |             if (conn->newurl) { | ||||||
|               /* abort after the headers if "follow Location" is set */ |               /* abort after the headers if "follow Location" is set */ | ||||||
|               infof (data, "Follow to new URL: %s\n", conn->newurl); |               infof (data, "Follow to new URL: %s\n", conn->newurl); | ||||||
|  |                 k->keepon &= ~KEEP_READ; | ||||||
|  |                 FD_ZERO(&k->rkeepfd); | ||||||
|               return CURLE_OK; |               return CURLE_OK; | ||||||
|             } |             } | ||||||
|             else if (conn->resume_from && |             else if (conn->resume_from && | ||||||
| @@ -669,7 +708,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|         if((-1 != conn->maxdownload) && |         if((-1 != conn->maxdownload) && | ||||||
|            (k->bytecount + nread >= conn->maxdownload)) { |            (k->bytecount + nread >= conn->maxdownload)) { | ||||||
|           nread = conn->maxdownload - k->bytecount; |           nread = conn->maxdownload - k->bytecount; | ||||||
|           if((signed int)nread < 0 ) /* this should be unusual */ |           if(nread < 0 ) /* this should be unusual */ | ||||||
|             nread = 0; |             nread = 0; | ||||||
|  |  | ||||||
|           k->keepon &= ~KEEP_READ; /* we're done reading */ |           k->keepon &= ~KEEP_READ; /* we're done reading */ | ||||||
| @@ -696,53 +735,82 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|       /* write */ |       /* write */ | ||||||
|  |  | ||||||
|       int i, si; |       int i, si; | ||||||
|       size_t bytes_written; |       ssize_t bytes_written; | ||||||
|  |  | ||||||
|       if ((k->bytecount == 0) && (k->writebytecount == 0)) |       if ((k->bytecount == 0) && (k->writebytecount == 0)) | ||||||
|         Curl_pgrsTime(data, TIMER_STARTTRANSFER); |         Curl_pgrsTime(data, TIMER_STARTTRANSFER); | ||||||
|  |  | ||||||
|       didwhat |= KEEP_WRITE; |       didwhat |= KEEP_WRITE; | ||||||
|  |  | ||||||
|       nread = data->set.fread(k->buf, 1, conn->upload_bufsize, |       /* only read more data if there's no upload data already | ||||||
|                               data->set.in); |          present in the upload buffer */ | ||||||
|  |       if(0 == conn->upload_present) { | ||||||
|  |         /* init the "upload from here" pointer */ | ||||||
|  |         conn->upload_fromhere = k->uploadbuf; | ||||||
|  |  | ||||||
|       /* the signed int typecase of nread of for systems that has |         nread = data->set.fread(conn->upload_fromhere, 1, | ||||||
|          unsigned size_t */ |                                 BUFSIZE, data->set.in); | ||||||
|       if ((signed int)nread<=0) { |  | ||||||
|         /* done */ |  | ||||||
|         k->keepon &= ~KEEP_WRITE; /* we're done writing */ |  | ||||||
|         FD_ZERO(&k->wkeepfd); |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       k->writebytecount += nread; |  | ||||||
|       Curl_pgrsSetUploadCounter(data, (double)k->writebytecount); |  | ||||||
|  |  | ||||||
|       /* convert LF to CRLF if so asked */ |         /* the signed int typecase of nread of for systems that has | ||||||
|       if (data->set.crlf) { |            unsigned size_t */ | ||||||
|         for(i = 0, si = 0; i < (int)nread; i++, si++) { |         if (nread<=0) { | ||||||
|           if (k->buf[i] == 0x0a) { |           /* done */ | ||||||
|             data->state.scratch[si++] = 0x0d; |           k->keepon &= ~KEEP_WRITE; /* we're done writing */ | ||||||
|             data->state.scratch[si] = 0x0a; |           FD_ZERO(&k->wkeepfd); | ||||||
|           } |           break; | ||||||
|           else { |  | ||||||
|             data->state.scratch[si] = k->buf[i]; |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|         nread = si; |  | ||||||
|         k->buf = data->state.scratch; /* point to the new buffer */ |         /* store number of bytes available for upload */ | ||||||
|  |         conn->upload_present = nread; | ||||||
|  |  | ||||||
|  |         /* convert LF to CRLF if so asked */ | ||||||
|  |         if (data->set.crlf) { | ||||||
|  |           for(i = 0, si = 0; i < nread; i++, si++) { | ||||||
|  |             if (k->buf[i] == 0x0a) { | ||||||
|  |               data->state.scratch[si++] = 0x0d; | ||||||
|  |               data->state.scratch[si] = 0x0a; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |               data->state.scratch[si] = k->uploadbuf[i]; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           nread = si; | ||||||
|  |           k->buf = data->state.scratch; /* point to the new buffer */ | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         /* We have a partial buffer left from a previous "round". Use | ||||||
|  |            that instead of reading more data */ | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       /* write to socket */ |       /* write to socket */ | ||||||
|       result = Curl_write(conn, conn->writesockfd, k->buf, nread, |       result = Curl_write(conn, | ||||||
|  |                           conn->writesockfd, | ||||||
|  |                           conn->upload_fromhere, | ||||||
|  |                           conn->upload_present, | ||||||
|                           &bytes_written); |                           &bytes_written); | ||||||
|       if(result) |       if(result) | ||||||
|         return result; |         return result; | ||||||
|       else if(nread != (int)bytes_written) { |       else if(conn->upload_present != bytes_written) { | ||||||
|         failf(data, "Failed uploading data"); |         /* we only wrote a part of the buffer (if anything), deal with it! */ | ||||||
|         return CURLE_WRITE_ERROR; |  | ||||||
|  |         /* store the amount of bytes left in the buffer to write */ | ||||||
|  |         conn->upload_present -= bytes_written; | ||||||
|  |  | ||||||
|  |         /* advance the pointer where to find the buffer when the next send | ||||||
|  |            is to happen */ | ||||||
|  |         conn->upload_fromhere += bytes_written; | ||||||
|       } |       } | ||||||
|       else if(data->set.crlf) |       else if(data->set.crlf) | ||||||
|         k->buf = data->state.buffer; /* put it back on the buffer */ |         k->buf = data->state.buffer; /* put it back on the buffer */ | ||||||
|  |       else { | ||||||
|  |         /* we've uploaded that buffer now */ | ||||||
|  |         conn->upload_fromhere = k->uploadbuf; | ||||||
|  |         conn->upload_present = 0; /* no more bytes left */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       k->writebytecount += bytes_written; | ||||||
|  |       Curl_pgrsSetUploadCounter(data, (double)k->writebytecount); | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -776,15 +844,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |||||||
|   if (result) |   if (result) | ||||||
|     return result; |     return result; | ||||||
|      |      | ||||||
|   if(data->progress.ulspeed > conn->upload_bufsize) { |  | ||||||
|     /* If we're transfering more data per second than fits in our buffer, |  | ||||||
|        we increase the buffer size to adjust to the current |  | ||||||
|        speed. However, we must not set it larger than BUFSIZE. We don't |  | ||||||
|        adjust it downwards again since we don't see any point in that! |  | ||||||
|     */ |  | ||||||
|     conn->upload_bufsize=(long)min(data->progress.ulspeed, BUFSIZE); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (data->set.timeout && |   if (data->set.timeout && | ||||||
|       ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { |       ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { | ||||||
|     failf (data, "Operation timed out with %d out of %d bytes received", |     failf (data, "Operation timed out with %d out of %d bytes received", | ||||||
| @@ -835,6 +894,7 @@ CURLcode Curl_readwrite_init(struct connectdata *conn) | |||||||
|  |  | ||||||
|   data = conn->data; /* there's the root struct */ |   data = conn->data; /* there's the root struct */ | ||||||
|   k->buf = data->state.buffer; |   k->buf = data->state.buffer; | ||||||
|  |   k->uploadbuf = data->state.uploadbuffer; | ||||||
|   k->maxfd = (conn->sockfd>conn->writesockfd? |   k->maxfd = (conn->sockfd>conn->writesockfd? | ||||||
|               conn->sockfd:conn->writesockfd)+1; |               conn->sockfd:conn->writesockfd)+1; | ||||||
|   k->hbufp = data->state.headerbuff; |   k->hbufp = data->state.headerbuff; | ||||||
| @@ -953,6 +1013,7 @@ Transfer(struct connectdata *conn) | |||||||
|     case 0:  /* timeout */ |     case 0:  /* timeout */ | ||||||
|       result = Curl_readwrite(conn, &done); |       result = Curl_readwrite(conn, &done); | ||||||
|       break; |       break; | ||||||
|  |  | ||||||
|     default: /* readable descriptors */ |     default: /* readable descriptors */ | ||||||
|       result = Curl_readwrite(conn, &done); |       result = Curl_readwrite(conn, &done); | ||||||
|       break; |       break; | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								lib/url.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								lib/url.c
									
									
									
									
									
								
							| @@ -191,6 +191,9 @@ CURLcode Curl_close(struct SessionHandle *data) | |||||||
|   /* free the connection cache */ |   /* free the connection cache */ | ||||||
|   free(data->state.connects); |   free(data->state.connects); | ||||||
|  |  | ||||||
|  |   if(data->info.contenttype) | ||||||
|  |     free(data->info.contenttype); | ||||||
|  |  | ||||||
|   free(data); |   free(data); | ||||||
|   return CURLE_OK; |   return CURLE_OK; | ||||||
| } | } | ||||||
| @@ -559,8 +562,11 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) | |||||||
|      * Set a custom string to use as request |      * Set a custom string to use as request | ||||||
|      */ |      */ | ||||||
|     data->set.customrequest = va_arg(param, char *); |     data->set.customrequest = va_arg(param, char *); | ||||||
|     if(data->set.customrequest) |  | ||||||
|       data->set.httpreq = HTTPREQ_CUSTOM; |     /* we don't set | ||||||
|  |        data->set.httpreq = HTTPREQ_CUSTOM; | ||||||
|  |        here, we continue as if we were using the already set type | ||||||
|  |        and this just changes the actual request keyword */ | ||||||
|     break; |     break; | ||||||
|   case CURLOPT_HTTPPOST: |   case CURLOPT_HTTPPOST: | ||||||
|     /* |     /* | ||||||
| @@ -1286,7 +1292,6 @@ static CURLcode CreateConnection(struct SessionHandle *data, | |||||||
|  |  | ||||||
|   /* and we setup a few fields in case we end up actually using this struct */ |   /* and we setup a few fields in case we end up actually using this struct */ | ||||||
|   conn->data = data;           /* remember our daddy */ |   conn->data = data;           /* remember our daddy */ | ||||||
|   conn->upload_bufsize = UPLOAD_BUFSIZE; /* default upload buffer size */ |  | ||||||
|   conn->firstsocket = -1;     /* no file descriptor */ |   conn->firstsocket = -1;     /* no file descriptor */ | ||||||
|   conn->secondarysocket = -1; /* no file descriptor */ |   conn->secondarysocket = -1; /* no file descriptor */ | ||||||
|   conn->connectindex = -1;    /* no index */ |   conn->connectindex = -1;    /* no index */ | ||||||
|   | |||||||
| @@ -83,12 +83,7 @@ | |||||||
| #include "http_chunks.h" /* for the structs and enum stuff */ | #include "http_chunks.h" /* for the structs and enum stuff */ | ||||||
|  |  | ||||||
| /* Download buffer size, keep it fairly big for speed reasons */ | /* Download buffer size, keep it fairly big for speed reasons */ | ||||||
| #define BUFSIZE (1024*50) | #define BUFSIZE (1024*20) | ||||||
|  |  | ||||||
| /* Defaul upload buffer size, keep it smallish to get faster progress meter |  | ||||||
|    updates. This is just default, it is dynamic and adjusts to the upload |  | ||||||
|    speed. */ |  | ||||||
| #define UPLOAD_BUFSIZE (1024*2) |  | ||||||
|  |  | ||||||
| /* Initial size of the buffer to store headers in, it'll be enlarged in case | /* Initial size of the buffer to store headers in, it'll be enlarged in case | ||||||
|    of need. */ |    of need. */ | ||||||
| @@ -251,6 +246,7 @@ struct Curl_transfer_keeper { | |||||||
|   struct SessionHandle *data; |   struct SessionHandle *data; | ||||||
|   struct connectdata *conn; |   struct connectdata *conn; | ||||||
|   char *buf; |   char *buf; | ||||||
|  |   char *uploadbuf; | ||||||
|   int maxfd; |   int maxfd; | ||||||
|  |  | ||||||
|   /* the file descriptors to play with */ |   /* the file descriptors to play with */ | ||||||
| @@ -316,10 +312,6 @@ struct connectdata { | |||||||
|   struct timeval created; /* creation time */ |   struct timeval created; /* creation time */ | ||||||
|   int firstsocket;     /* the main socket to use */ |   int firstsocket;     /* the main socket to use */ | ||||||
|   int secondarysocket; /* for i.e ftp transfers */ |   int secondarysocket; /* for i.e ftp transfers */ | ||||||
|  |  | ||||||
|   long upload_bufsize; /* adjust as you see fit, never bigger than BUFSIZE |  | ||||||
|                           never smaller than UPLOAD_BUFSIZE */ |  | ||||||
|  |  | ||||||
|   long maxdownload; /* in bytes, the maximum amount of data to fetch, 0 |   long maxdownload; /* in bytes, the maximum amount of data to fetch, 0 | ||||||
|                        means unlimited */ |                        means unlimited */ | ||||||
|    |    | ||||||
| @@ -409,6 +401,16 @@ struct connectdata { | |||||||
|  |  | ||||||
|   /* This struct is inited when needed */ |   /* This struct is inited when needed */ | ||||||
|   struct Curl_transfer_keeper keep; |   struct Curl_transfer_keeper keep; | ||||||
|  |  | ||||||
|  |   /* 'upload_present' is used to keep a byte counter of how much data there is | ||||||
|  |      still left in the buffer, aimed for upload. */ | ||||||
|  |   int upload_present; | ||||||
|  |  | ||||||
|  |    /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a | ||||||
|  |       buffer, so the next read should read from where this pointer points to, | ||||||
|  |       and the 'upload_present' contains the number of bytes available at this | ||||||
|  |       position */ | ||||||
|  |   char *upload_fromhere;                                    | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -421,6 +423,8 @@ struct PureInfo { | |||||||
|                     the time was unretrievable */ |                     the time was unretrievable */ | ||||||
|   long header_size;  /* size of read header(s) in bytes */ |   long header_size;  /* size of read header(s) in bytes */ | ||||||
|   long request_size; /* the amount of bytes sent in the request(s) */ |   long request_size; /* the amount of bytes sent in the request(s) */ | ||||||
|  |  | ||||||
|  |   char *contenttype; /* the content type of the object */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -495,8 +499,8 @@ struct UrlState { | |||||||
|   char *headerbuff; /* allocated buffer to store headers in */ |   char *headerbuff; /* allocated buffer to store headers in */ | ||||||
|   int headersize;   /* size of the allocation */ |   int headersize;   /* size of the allocation */ | ||||||
|  |  | ||||||
|   char buffer[BUFSIZE+1]; /* buffer with size BUFSIZE */ |   char buffer[BUFSIZE+1]; /* download buffer */ | ||||||
|  |   char uploadbuffer[BUFSIZE+1]; /* upload buffer */ | ||||||
|   double current_speed;  /* the ProgressShow() funcion sets this */ |   double current_speed;  /* the ProgressShow() funcion sets this */ | ||||||
|  |  | ||||||
|   bool this_is_a_follow; /* this is a followed Location: request */ |   bool this_is_a_follow; /* this is a followed Location: request */ | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|  *                            | (__| |_| |  _ <| |___  |  *                            | (__| |_| |  _ <| |___  | ||||||
|  *                             \___|\___/|_| \_\_____| |  *                             \___|\___/|_| \_\_____| | ||||||
|  * |  * | ||||||
|  * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. |  * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. | ||||||
|  * |  * | ||||||
|  * In order to be useful for every potential user, curl and libcurl are |  * In order to be useful for every potential user, curl and libcurl are | ||||||
|  * dual-licensed under the MPL and the MIT/X-derivate licenses. |  * dual-licensed under the MPL and the MIT/X-derivate licenses. | ||||||
| @@ -38,20 +38,28 @@ char *curl_version(void) | |||||||
|  |  | ||||||
| #ifdef USE_SSLEAY | #ifdef USE_SSLEAY | ||||||
|  |  | ||||||
| #if (SSLEAY_VERSION_NUMBER >= 0x906000) | #if (SSLEAY_VERSION_NUMBER >= 0x905000) | ||||||
|   { |   { | ||||||
|     char sub[2]; |     char sub[2]; | ||||||
|  |     unsigned long ssleay_value; | ||||||
|     sub[1]='\0'; |     sub[1]='\0'; | ||||||
|     if(SSLEAY_VERSION_NUMBER&0xff0) { |     ssleay_value=SSLeay(); | ||||||
|       sub[0]=((SSLEAY_VERSION_NUMBER>>4)&0xff) + 'a' -1; |     if(ssleay_value < 0x906000) { | ||||||
|     } |       ssleay_value=SSLEAY_VERSION_NUMBER; | ||||||
|     else |  | ||||||
|       sub[0]='\0'; |       sub[0]='\0'; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       if(ssleay_value&0xff0) { | ||||||
|  |         sub[0]=((ssleay_value>>4)&0xff) + 'a' -1; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         sub[0]='\0'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     sprintf(ptr, " (OpenSSL %lx.%lx.%lx%s)", |     sprintf(ptr, " (OpenSSL %lx.%lx.%lx%s)", | ||||||
|             (SSLEAY_VERSION_NUMBER>>28)&0xf, |             (ssleay_value>>28)&0xf, | ||||||
|             (SSLEAY_VERSION_NUMBER>>20)&0xff, |             (ssleay_value>>20)&0xff, | ||||||
|             (SSLEAY_VERSION_NUMBER>>12)&0xff, |             (ssleay_value>>12)&0xff, | ||||||
|             sub); |             sub); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
|  |  | ||||||
| INCLUDES = -I$(top_srcdir)/include | INCLUDES = -I$(top_srcdir)/include | ||||||
|  |  | ||||||
| bin_PROGRAMS = app single double | noinst_PROGRAMS = app single double | ||||||
|  |  | ||||||
| app_SOURCES = app.c | app_SOURCES = app.c | ||||||
| app_LDADD = ../lib/libcurl.la | app_LDADD = ../lib/libcurl.la | ||||||
|   | |||||||
| @@ -1450,10 +1450,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ | |||||||
|       GetStr(&config->proxy, nextarg); |       GetStr(&config->proxy, nextarg); | ||||||
|       break; |       break; | ||||||
|     case 'X': |     case 'X': | ||||||
|       /* HTTP request */ |       /* set custom request */ | ||||||
|       GetStr(&config->customrequest, nextarg); |       GetStr(&config->customrequest, nextarg); | ||||||
|       if(SetHTTPrequest(HTTPREQ_CUSTOM, &config->httpreq)) |  | ||||||
|         return PARAM_BAD_USE; |  | ||||||
|       break; |       break; | ||||||
|     case 'y': |     case 'y': | ||||||
|       /* low speed time */ |       /* low speed time */ | ||||||
|   | |||||||
| @@ -1,3 +1,3 @@ | |||||||
| #define CURL_NAME "curl" | #define CURL_NAME "curl" | ||||||
| #define CURL_VERSION "7.9.3-pre1" | #define CURL_VERSION "7.9.4" | ||||||
| #define CURL_ID CURL_NAME " " CURL_VERSION " (" OS ") " | #define CURL_ID CURL_NAME " " CURL_VERSION " (" OS ") " | ||||||
|   | |||||||
| @@ -14,8 +14,10 @@ specified, that will be checked/used if specified. This document includes all | |||||||
| the subsections currently supported. | the subsections currently supported. | ||||||
|  |  | ||||||
| <reply> | <reply> | ||||||
| <data> | <data [nocheck=1]> | ||||||
| data to sent to the client on its request | data to sent to the client on its request and later verified that it arrived | ||||||
|  | safely. Set the nocheck=1 to prevent the test script to verify the arrival | ||||||
|  | of this data. | ||||||
| </data> | </data> | ||||||
| <datacheck> | <datacheck> | ||||||
| if the data is sent but this is what should be checked afterwards | if the data is sent but this is what should be checked afterwards | ||||||
| @@ -30,16 +32,20 @@ reply is sent | |||||||
| <postcmd> | <postcmd> | ||||||
| special purpose server-command to control its behavior *after* the | special purpose server-command to control its behavior *after* the | ||||||
| reply is sent | reply is sent | ||||||
| </oistcmd> | </postcmd> | ||||||
| </reply> | </reply> | ||||||
|  |  | ||||||
| <client> | <client> | ||||||
| <name> | <name> | ||||||
| test case description | test case description | ||||||
| </name> | </name> | ||||||
| <command> | <command [option=no-output]> | ||||||
| command line to run, there's a bunch of %variables that get replaced | command line to run, there's a bunch of %variables that get replaced | ||||||
| accordingly. more about them elsewhere | accordingly. more about them elsewhere | ||||||
|  |  | ||||||
|  | Set 'option=no-output' to prevent the test script to slap on the --output | ||||||
|  | argument that directs the output to a file. The --output is also not added if | ||||||
|  | the client/stdout section is used. | ||||||
| </command> | </command> | ||||||
| <file name="log/filename"> | <file name="log/filename"> | ||||||
| this creates the named file with this content before the test case is run | this creates the named file with this content before the test case is run | ||||||
| @@ -59,6 +65,9 @@ changing protocol data such as port numbers or user-agent strings. | |||||||
| <protocol> | <protocol> | ||||||
| the protocol dump curl should transmit | the protocol dump curl should transmit | ||||||
| </protocol> | </protocol> | ||||||
|  | <stdout> | ||||||
|  | This verfies that this data was passed to stdout. | ||||||
|  | </stdout> | ||||||
| <file name="log/filename"> | <file name="log/filename"> | ||||||
| the file's contents must be identical to this | the file's contents must be identical to this | ||||||
| </file> | </file> | ||||||
|   | |||||||
| @@ -13,5 +13,5 @@ test102  test111  test120  test16   test21   test30   test400  test7 \ | |||||||
| test103  test112  test121  test17   test22   test300  test401  test8 \ | test103  test112  test121  test17   test22   test300  test401  test8 \ | ||||||
| test104  test113  test122  test18   test23   test301  test402  test9 \ | test104  test113  test122  test18   test23   test301  test402  test9 \ | ||||||
| test105  test114  test123  test19   test24   test302  test43   \ | test105  test114  test123  test19   test24   test302  test43   \ | ||||||
| test106  test115  test124  test190  test25   test303  test44   \ | test106  test115  test124  test190  test25   test303  test44   test38 \ | ||||||
| test107  test116  test125  test2    test26   test33   test45   test126 | test107  test116  test125  test2    test26   test33   test45   test126 | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ simple HTTP GET | |||||||
|  <command> |  <command> | ||||||
| http://%HOSTIP:%HOSTPORT/1 | http://%HOSTIP:%HOSTPORT/1 | ||||||
| </command> | </command> | ||||||
| </test> | </client> | ||||||
|  |  | ||||||
| # | # | ||||||
| # Verify data after the test has been "shot" | # Verify data after the test has been "shot" | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								tests/data/test38
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/data/test38
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | # Server-side | ||||||
|  | <reply> | ||||||
|  | <data nocheck=1> | ||||||
|  | HTTP/1.0 200 Mooo | ||||||
|  | Date: Mon, 13 Nov 2000 13:41:09 GMT | ||||||
|  | Server: myown/1.0 | ||||||
|  | Connection: close | ||||||
|  |  | ||||||
|  | todelooooo lalalala yada yada, we know nothing about ranges ;-) | ||||||
|  | </data> | ||||||
|  | </reply> | ||||||
|  |  | ||||||
|  | # Client-side | ||||||
|  | <client> | ||||||
|  |  <name> | ||||||
|  | HTTP resume request without server supporting it | ||||||
|  |  </name> | ||||||
|  |  <command option="no-output"> | ||||||
|  | http://%HOSTIP:%HOSTPORT/want/38 -C - -i -o log/fewl.txt | ||||||
|  | </command> | ||||||
|  | <file name="log/fewl.txt"> | ||||||
|  | This text is here to simulate a partly downloaded file to resume | ||||||
|  | download on. | ||||||
|  | </file> | ||||||
|  | </client> | ||||||
|  |  | ||||||
|  | # Verify data after the test has been "shot" | ||||||
|  | <verify> | ||||||
|  | <errorcode> | ||||||
|  | 33 | ||||||
|  | </errorcode> | ||||||
|  | <strip> | ||||||
|  | ^User-Agent:.* | ||||||
|  | </strip> | ||||||
|  | <protocol> | ||||||
|  | GET /want/38 HTTP/1.1 | ||||||
|  | Range: bytes=78- | ||||||
|  | Host: 127.0.0.1:8999 | ||||||
|  | Pragma: no-cache | ||||||
|  | Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* | ||||||
|  |  | ||||||
|  | </protocol> | ||||||
|  |  | ||||||
|  | # the download target file must remain untouched | ||||||
|  | <file name="log/fewl.txt"> | ||||||
|  | This text is here to simulate a partly downloaded file to resume | ||||||
|  | download on. | ||||||
|  | </file> | ||||||
|  | </verify> | ||||||
| @@ -3,6 +3,9 @@ | |||||||
|  |  | ||||||
| my @xml; | my @xml; | ||||||
|  |  | ||||||
|  | my $warning=0; | ||||||
|  | my $trace=0; | ||||||
|  |  | ||||||
| sub getpartattr { | sub getpartattr { | ||||||
|     my ($section, $part)=@_; |     my ($section, $part)=@_; | ||||||
|  |  | ||||||
| @@ -63,12 +66,21 @@ sub getpart { | |||||||
|             $inside--; |             $inside--; | ||||||
|         } |         } | ||||||
|         elsif((1==$inside) && ($_ =~ /^ *\<\/$section/)) { |         elsif((1==$inside) && ($_ =~ /^ *\<\/$section/)) { | ||||||
|  |             if($trace) { | ||||||
|  |                 print STDERR "*** getpart.pm: $section/$part returned data!\n"; | ||||||
|  |             } | ||||||
|  |             if(!@this && $warning) { | ||||||
|  |                 print STDERR "*** getpart.pm: $section/$part returned empty!\n"; | ||||||
|  |             } | ||||||
|             return @this; |             return @this; | ||||||
|         } |         } | ||||||
|         elsif(2==$inside) { |         elsif(2==$inside) { | ||||||
|             push @this, $_; |             push @this, $_; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if($warning) { | ||||||
|  |         print STDERR "*** getpart.pm: $section/$part returned empty!\n"; | ||||||
|  |     } | ||||||
|     return @this; #empty! |     return @this; #empty! | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -483,9 +483,17 @@ sub singletest { | |||||||
|         writearray($filename, \@inputfile); |         writearray($filename, \@inputfile); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     my %cmdhash = getpartattr("client", "command"); | ||||||
|  |  | ||||||
|     my $out=""; |     my $out=""; | ||||||
|     if (!@validstdout) { |  | ||||||
|         $out="--output $CURLOUT "; |     if($cmdhash{'option'} eq "no-output") { | ||||||
|  |         #print "*** We don't slap on --output\n"; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         if (!@validstdout) { | ||||||
|  |             $out="--output $CURLOUT "; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     # run curl, add -v for debug information output |     # run curl, add -v for debug information output | ||||||
| @@ -827,12 +835,44 @@ if($testthis[0] ne "") { | |||||||
|     $TESTCASES=join(" ", @testthis); |     $TESTCASES=join(" ", @testthis); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | ############################################################################ | ||||||
|  | # | ||||||
|  | # don't let anyone think this works right now | ||||||
|  |  | ||||||
|  | print <<EOM | ||||||
|  |  *************************************************************************** | ||||||
|  |  | ||||||
|  |     THIS DOES NOT WORK | ||||||
|  |  | ||||||
|  |  *************************************************************************** | ||||||
|  |  | ||||||
|  |  Things in curl-land have changed, but the test suite has not been fixed | ||||||
|  |  accordingly and thus, the test suite is currently more or less useless. | ||||||
|  |  | ||||||
|  |  *PLEASE* help us fixing this. We have to make our new test server written | ||||||
|  |  in C work and get used instead of the perl version previously used. | ||||||
|  |  | ||||||
|  |  The working version of the test server is found here: | ||||||
|  |  | ||||||
|  |  http://curl.haxx.se/dev/sws-0.2.tar.gz | ||||||
|  |     | ||||||
|  |  If you unpack this in the tests/ directory and run the server in there, you | ||||||
|  |  can actually get test-responses if you do like this: | ||||||
|  |  | ||||||
|  |  \$ ./sws 8080 & | ||||||
|  |  \$ curl localhost:8080/3 | ||||||
|  |  | ||||||
|  | EOM | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| # Output curl version and host info being tested | # Output curl version and host info being tested | ||||||
| # | # | ||||||
|  |  | ||||||
| displaydata(); | if(!$listonly) { | ||||||
|  |     displaydata(); | ||||||
|  | } | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| # clear and create logging directory: | # clear and create logging directory: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user