libcurl is a free and easy-to-use client-side URL transfer library, supporting FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, FILE and LDAP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, Kerberos4), file transfer resume, http proxy tunneling and more!
This BlitzMax module wraps up the functionality of libcurl, making it easy to accomplish internet-based tasks with very little effort.
There are two libcurl modules available. One with SSH snd SSL support, one without. This is done because the SSL support requires extra client libraries, something which may be too much of a hassle to have users install if you don't need it.
SSL support enables the use of secure protocols like HTTPS and FTPS.
This is the non-SSL module.
The following is a list of some simple examples showing a small portion of the features available with the module.
This guide is based on the libcurl tutorial that comes with the curl distribution, and has been changed where appropriate for use with the BlitzMax libcURL module.
The easy interface is synchronous, meaning that you must wait for an action to complete before control is returned to your program. The asynchronous method uses what is called the multi-interface. More about that interface, what it is targeted for and how to use it is detailed here. You still need to understand the easy interface first, so please continue reading for better understanding.
To use the easy interface, you must first create yourself a TCurlEasy. You need one of these for each easy session you want to perform.
Get an easy handle with
easyhandle = TCurlEasy.Create();It returns a TCurlEasy object. 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 one of the setOpt methods, or one of the other convenience methods (see TCurlEasy for more specific details). 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.
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.setOptString(CURLOPT_URL, "http://domain.com/")Let's assume for a while that you want to receive data as the URL identifies 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:
writeFunction(buffer:Byte Ptr, size:Int, data:Object)You tell libcurl to pass all data to this function by issuing a call to setWriteCallback.
The data parameter allows you to easily pass local data between your application and the function that gets invoked by libcurl. libcurl itself won't touch the data you pass here.
libcurl offers its own default internal callback that'll take care of the data if you don't set the callback. It will then simply output the received data to stdout.
You can also pass in a Stream for libcurl to use to write the data to with the setWriteStream method. You will have to manage the opening and closing of the stream yourself. libcurl assumes it is already opened when it attempts to use it.
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.perform()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 call CurlError passing the error code, to receive the error text as a String.
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 connection.
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 protocol 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 why the server behaves the way it does. Include headers in the normal body output with CURLOPT_HEADER set True.
If CURLOPT_VERBOSE is not enough, you increase the level of debug data your application receive by using the setDebugCallback method.
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.
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 a TCurlEasy 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 object libcurl will pass to our read callback. The read callback should have a prototype similar to:
readFunction(buffer:Byte Ptr, size:Int, data:Object)Where buffer is the pointer to a buffer we fill in with data to upload and size is the size of the buffer and therefore also the maximum amount of data we can return to libcurl in this call. The 'data' object is the custom object we set to point to a type of ours to pass private data between the application and the callback.
curl.setReadCallback(myReadFunction, myData)Tell libcurl that we want to upload:
curl.setOptInt(CURLOPT_UPLOAD, True)
A few protocols won't behave properly when uploads are done without any prior knowledge of the expected file size. So, set the upload file size using the CURLOPT_INFILESIZE_LARGE for all known file sizes like this :
' in this example, size must be an Long variable curl.setOptLong(CURLOPT_INFILESIZE_LARGE, size)
When you call 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.
You can also use setReadStream instead, providing a specific Stream for libcurl to read from. You will have to manage the opening and closing of the stream yourself. libcurl assumes it is already opened when it attempts to use it.
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.setOptString(CURLOPT_USERPWD, "myname:thesecret")
Another case where name and password might be needed at times, is for those users who need to authenticate 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.setOptString(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" section), 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._setOptString(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.
To pass the known private key password to libcurl:
curl.setOptString(CURLOPT_SSLKEYPASSWD, "keypassword")
The previous section showed how to set user name and password for getting URLs that require authentication. When using the HTTP protocol, there are many different ways a client can provide those credentials to the server and you can control what way libcurl will (attempt to) use. The default HTTP authentication method is called 'Basic', which is sending the name and password in clear-text in the HTTP request, base64-encoded. This is insecure.
At the time of this writing libcurl can be built to use: Basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO. You can tell libcurl which one to use with CURLOPT_HTTPAUTH as in:
curl.setOptInt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)And when you send authentication to a proxy, you can also set authentication type the same way but instead with CURLOPT_PROXYAUTH:
curl.setOptInt(CURLOPT_PROXYAUTH, CURLAUTH_NTLM)Both these options allow you to set multiple types (by ORing them together), to make libcurl pick the most secure one out of the types the server/proxy claims to support. This method does however add a round-trip since libcurl must first ask the server what it supports:
curl.setOptInt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_BASIC)For convenience, you can use the CURLAUTH_ANY flag (instead of a list with specific types) which allows libcurl to use whatever method it wants.
When asking for multiple types, libcurl will pick the available one it considers "best" in its own internal order of preference.
There are always many questions regarding how to issue HTTP POSTs with libcurl the proper way, so section will 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