Sortix cross-volatile manual
This manual documents Sortix cross-volatile. You can instead view this document in the latest official manual.
| libcurl-tutorial(3) | Library Functions Manual | libcurl-tutorial(3) |
NAME
libcurl-tutorial - libcurl programming tutorialObjective
This document attempts to describe the general principles and some basic approaches to consider when programming with libcurl. The text focuses on the C interface but should apply fairly well on other language bindings as well as they usually follow the C API pretty closely.Building
There are many different ways to build C programs. This chapter assumes 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 the OpenSSL libraries, 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 a supported SSL library was detected properly at build-time, libcurl is 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
If SSL is supported, the keyword SSL is written to stdout, possibly together with a other features that could be either on or off on for different libcurls.See also the "Features libcurl Provides" further down.
- autoconf macro
- When you write your configure script to detect libcurl and setup variables accordingly, we offer a macro that probably does everything you need in this area. See docs/libcurl/libcurl.m4 file - it includes docs on how to use it.
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.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 usingcurl_global_init()
and it takes one parameter which is a bit pattern that tells libcurl what to initialize. Using CURL_GLOBAL_ALL makes 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 makes libcurl initialize 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 makes libcurl initialize the SSL library properly for this application. This only needs to be done 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(3) has not been called by the time curl_easy_perform(3) 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 good.When the program no longer uses libcurl, it should call curl_global_cleanup(3), which is the opposite of the init call. It performs the reversed operations to cleanup the resources the curl_global_init(3) call initialized.Repeated calls to curl_global_init(3) and curl_global_cleanup(3) should be avoided. They should only be called once each.
Features libcurl Provides
It is considered best-practice to determine libcurl features at runtime rather than at build-time (if possible of course). By calling curl_version_info(3) and checking out the details of the returned struct, your program can figure out exactly what the currently running libcurl supports.Two Interfaces
libcurl first introduced the so called easy interface. All operations in the easy interface are prefixed with 'curl_easy'. The easy interface lets you do single transfers with a synchronous and blocking function call.Handle the Easy libcurl
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. Basically, you should use one handle for every thread you plan to use for transferring. You must never share the same handle in multiple threads.handle = 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.
curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");
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(handle, CURLOPT_WRITEFUNCTION, write_data);
You can control what data your callback function gets in the fourth argument by setting another property:
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &internal_struct);
Using that property, you can easily pass local data between your application and the function that gets invoked by libcurl. libcurl itself does not touch the data you pass with CURLOPT_WRITEDATA(3).
success = curl_easy_perform(handle);
Multi-threading Issues
libcurl is thread safe but there are a few exceptions. Refer to libcurl-thread(3) for more information.When It does not Work
There are 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.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 similar to uploading data to an HTTP server with a PUT request.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 sizenitems* 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(handle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(handle, CURLOPT_READDATA, &filedata);
Tell libcurl that we want to upload:
curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
A few protocols do not 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(3) for all known file sizes like this[1]:
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, file_size);
Passwords
Many protocols use or even require that username and password are provided to be able to download or upload the data of your choice. libcurl offers several ways to specify them.protocol://user:password@example.com/path/
If you need any odd letters in your username or password, you should enter them URL encoded, as %XX where XX is a two-digit hexadecimal number.
curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret");
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "myname:thesecret");
curl_easy_setopt(handle, CURLOPT_NETRC, 1L);
machine myhost.mydomain.com
login userlogin
password secretword
curl_easy_setopt(handle, CURLOPT_KEYPASSWD, "keypassword");
HTTP Authentication
The previous chapter showed how to set username 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 which way libcurl uses them. 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.curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
HTTP POSTing
We get many questions regarding how to issue HTTP POSTs with libcurl the proper way. This chapter thus includes examples using both different versions of HTTP POST that libcurl supports.char *data="name=daniel&project=curl";
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(handle, CURLOPT_URL, "http://posthere.com/");
curl_easy_perform(handle); /* post away! */
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");
/* post binary data */
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, binaryptr);
/* set the size of the postfields data */
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, 23L);
/* pass our list of custom made headers */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(handle); /* post away! */
curl_slist_free_all(headers); /* free the header list */
curl_mime *multipart = curl_mime_init(handle);
curl_mimepart *part = curl_mime_addpart(multipart);
curl_mime_name(part, "name");
curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "project");
curl_mime_data(part, "curl", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "logotype-image");
curl_mime_filedata(part, "curl.png");
/* Set the form info */
curl_easy_setopt(handle, CURLOPT_MIMEPOST, multipart);
curl_easy_perform(handle); /* post away! */
/* free the post data again */
curl_mime_free(multipart);
curl_mime_data_cb(part, filesize, (curl_read_callback) fread,
(curl_seek_callback) fseek, NULL, filepointer);
struct curl_httppost *post=NULL;
struct curl_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(handle, CURLOPT_HTTPPOST, post);
curl_easy_perform(handle); /* post away! */
/* free the post data again */
curl_formfree(post);
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(handle); /* post away! */
curl_formfree(post); /* free post */
curl_slist_free_all(headers); /* free custom header list */
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
Just setting CURLOPT_POSTFIELDS(3) to "" or NULL does not stop libcurl from doing a POST. It just makes it POST without any data to send!
Converting from deprecated form API to MIME API
Four rules have to be respected in building the multi-part:curl_formadd(&post, &last,
CURLFORM_COPYNAME, "id",
CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
CURLFORM_CONTENTHEADER, headers,
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "id");
curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
curl_mime_headers(part, headers, FALSE);
curl_formadd(&post, &last,
CURLFORM_PTRNAME, "logotype-image",
CURLFORM_FILECONTENT, "-",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "logotype-image");
curl_mime_data_cb(part, (curl_off_t) -1, fread, fseek, NULL, stdin);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "datafile[]",
CURLFORM_FILE, "file1",
CURLFORM_FILE, "file2",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "datafile[]");
curl_mime_filedata(part, "file1");
part = curl_mime_addpart(multipart);
curl_mime_name(part, "datafile[]");
curl_mime_filedata(part, "file2");
curl_easy_setopt(handle, CURLOPT_READFUNCTION, myreadfunc);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "stream",
CURLFORM_STREAM, arg,
CURLFORM_CONTENTLEN, (curl_off_t) datasize,
CURLFORM_FILENAME, "archive.zip",
CURLFORM_CONTENTTYPE, "application/zip",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "stream");
curl_mime_data_cb(part, (curl_off_t) datasize,
myreadfunc, NULL, NULL, arg);
curl_mime_filename(part, "archive.zip");
curl_mime_type(part, "application/zip");
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "memfile",
CURLFORM_BUFFER, "memfile.bin",
CURLFORM_BUFFERPTR, databuffer,
CURLFORM_BUFFERLENGTH, (long) sizeof databuffer,
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "memfile");
curl_mime_data(part, databuffer, (curl_off_t) sizeof databuffer);
curl_mime_filename(part, "memfile.bin");
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "message",
CURLFORM_FILECONTENT, "msg.txt",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "message");
curl_mime_filedata(part, "msg.txt");
curl_mime_filename(part, NULL);
Showing Progress
For historical and traditional reasons, libcurl has a built-in progress meter that can be switched on and then makes it present a progress meter in your terminal.int progress_callback(void *clientp,
double dltotal,
double dlnow,
double ultotal,
double ulnow);
libcurl with C++
There is basically only one thing to keep in mind when using C++ instead of C when interfacing libcurl:class AClass {
static size_t write_data(void *ptr, size_t size, size_t nmemb,
void *ourpointer)
{
/* do what you want with the data */
}
}
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".- Proxy Options
-
To tell libcurl to use a proxy at a given port number:
curl_easy_setopt(handle, 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(handle, CURLOPT_PROXYUSERPWD, "user:password");
If you want to, you can specify the hostname only in the CURLOPT_PROXY(3) option, and set the port number separately with CURLOPT_PROXYPORT(3).Tell libcurl what kind of proxy it is with CURLOPT_PROXYTYPE(3) (if not, it defaults to assuming an HTTP proxy):
curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
- Environment Variables
-
libcurl automatically 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 old tradition 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://][user:password@]machine[:port]". Where the protocol:// part specifies which type of proxy it is, and the optional port number specifies on which port the proxy operates. If not specified, the internal default port number is 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 was not 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.To explicitly disable libcurl's checking for and using the proxy environment variables, set the proxy name to "" - an empty string - with CURLOPT_PROXY(3).
- SSL and Proxies
-
SSL is for secure point-to-point connections. This involves strong encryption and similar things, which effectively 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 an HTTP proxy is to ask the proxy to tunnel everything through without being able to check or fiddle with the traffic.Opening an SSL connection over an HTTP proxy is therefore 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 dear 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 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 an 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 administrators of proxies and is rarely allowed.Tell libcurl to use proxy tunneling like this:
curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1L);
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 does not stand in the way for such innovative actions either!
- Proxy Auto-Config
-
Netscape first came up with this. It is basically a webpage (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 browser to a SOCKS proxy).libcurl has no means to interpret or evaluate JavaScript and thus it does not 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 interpreter; people have successfully used the Mozilla JavaScript engine in the past.- Ask your admins to stop this, for a static proxy setup or similar.
Persistence Is The Way to Happiness
Re-cycling the same easy handle several times when doing multiple requests is the way to go.HTTP Headers Used by libcurl
When you use libcurl to do HTTP requests, it passes along a series of headers automatically. It might be good for you to know and understand these. You can replace or remove them by using the CURLOPT_HTTPHEADER(3) option.- Host
- This header is required by HTTP 1.1 and even many 1.0 servers and should be the name of the server we want to talk to. This includes the port number if anything but default.
- Accept
- "/"
- Expect
- When doing POST requests, libcurl sets this header to "100-continue" to ask the server for an "OK" message before it proceeds with sending the data part of the post. If the posted data amount is deemed "small", libcurl does not use this header.
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 has excellent proxy-support.- CURLOPT_CUSTOMREQUEST
-
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(3) is there for you. It is simple to use:
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
When using the custom request, you change the request keyword of the actual request you are performing. Thus, by default you make a GET request but you can also make a POST operation (as described before) and then replace the POST keyword if you want to. You are the boss.
- Modify Headers
-
HTTP-like protocols pass a series of headers to the server when doing the request, and you are free to pass any amount of extra headers that you think fit. Adding headers is this easy:
struct curl_slist *headers=NULL; /* init to NULL is important */
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(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(handle); /* transfer http */
curl_slist_free_all(headers); /* free the header list */
... and if you think some of the internally generated headers, such as Accept: or Host: do not contain the data you want them to contain, you can replace them by simply setting them too:
headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Host: munged.host.line");
- Delete Headers
-
If you replace an existing header with one with no contents, you prevent the header from being sent. For instance, if you want to completely prevent the "Accept:" header from being sent, you can disable it with code similar to this:
headers = curl_slist_append(headers, "Accept:");Both replacing and canceling internal headers should be done with careful consideration and you should be aware that you may violate the HTTP protocol when doing so.
- Enforcing chunked transfer-encoding
- By making sure a request uses the custom header "Transfer-Encoding: chunked" when doing a non-GET HTTP operation, libcurl switches over to "chunked" upload, even though the size of the data to upload might be known. By default, libcurl usually switches over to chunked upload automatically if the upload data size is unknown.
- HTTP Version
-
All HTTP requests includes the version number to tell the server which version we support. libcurl speaks HTTP 1.1 by default. Some old servers do not like getting 1.1-requests and when dealing with stubborn old things like that, you can tell libcurl to use 1.0 instead by doing something like this:
curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- FTP Custom Commands
-
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 an FTP server means that you need to send the commands exactly as the FTP server expects them (RFC 959 is a good guide here), and you can only use commands that work on the control-connection alone. All kinds of commands that require data interchange and thus need a data-connection must be left to libcurl's own judgment. Also be aware that libcurl does its 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(handle, CURLOPT_QUOTE, headers);
curl_easy_perform(handle); /* 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(3) would instead be called CURLOPT_POSTQUOTE(3) and used the exact same way.The custom FTP commands are issued to the server in the same order they are added to the list, and if a command gets an error code returned back from the server, no more commands are issued and libcurl bails out with an error code (CURLE_QUOTE_ERROR). Note that if you use CURLOPT_QUOTE(3) to send commands before a transfer, no transfer actually takes place when a quote command has failed.If you set the CURLOPT_HEADER(3) to 1, you tell libcurl to get information about the target file and output "headers" about it. The headers are in "HTTP-style", looking like they do in HTTP.The option to enable headers or to run custom FTP commands may be useful to combine with CURLOPT_NOBODY(3). If this option is set, no actual file content transfer is performed.
- FTP Custom CURLOPT_CUSTOMREQUEST
- If you do want to list the contents of an FTP directory using your own defined FTP command, CURLOPT_CUSTOMREQUEST(3) does just that. "NLST" is the default one for listing directories but you are free to pass in your idea of a good alternative.
Cookies Without Chocolate Chips
In the HTTP sense, a cookie is a name with an associated value. A server sends the name and value to the client, and expects it to get sent back on every subsequent request to the server that matches the particular conditions set. The conditions include that the domain name and path match and that the cookie has not become too old.curl_easy_setopt(handle, CURLOPT_COOKIE, "name1=var1; name2=var2;");
FTP Peculiarities We Need
FTP transfers use a second TCP/IP connection for the data transfer. This is usually a fact you can forget and ignore but at times this detail comes back to haunt you. libcurl offers several different ways to customize how the second connection is being made.MIME API revisited for SMTP and IMAP
In addition to support HTTP multi-part form fields, the MIME API can be used to build structured email messages and send them via SMTP or append such messages to IMAP directories.curl_mime *message = curl_mime_init(handle);
/* The inline part is an alternative proposing the html and the text
versions of the email. */
curl_mime *alt = curl_mime_init(handle);
/* HTML message. */
curl_mimepart *part = curl_mime_addpart(alt);
curl_mime_data(part, "<html><body><p>This is HTML</p></body></html>",
CURL_ZERO_TERMINATED);
curl_mime_type(part, "text/html");
/* Text message. */
part = curl_mime_addpart(alt);
curl_mime_data(part, "This is plain text message",
CURL_ZERO_TERMINATED);
/* Create the inline part. */
part = curl_mime_addpart(message);
curl_mime_subparts(part, alt);
curl_mime_type(part, "multipart/alternative");
struct curl_slist *headers = curl_slist_append(NULL,
"Content-Disposition: inline");
curl_mime_headers(part, headers, TRUE);
/* Add the attachment. */
part = curl_mime_addpart(message);
curl_mime_filedata(part, "manual.pdf");
curl_mime_encoder(part, "base64");
/* Build the mail headers. */
headers = curl_slist_append(NULL, "From: me@example.com");
headers = curl_slist_append(headers, "To: you@example.com");
/* Set these into the easy handle. */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(handle, CURLOPT_MIMEPOST, mime);
Headers Equal Fun
Some protocols provide "headers", meta-data separated from the normal data. These headers are by default not included in the normal data stream, but you can make them appear in the data stream by setting CURLOPT_HEADER(3) to 1.Post Transfer Information
See curl_easy_getinfo(3).The multi Interface
The easy interface as described in detail in this document is a synchronous interface that transfers one file at a time and does not return until it is done.SSL, Certificates and Other Tricks
[ seeding, passwords, keys, certificates, ENGINE, ca certs ]
Sharing Data Between Easy Handles
You can share some data between easy handles when the easy interface is used, and some data is share automatically when you use the multi interface.Footnotes
- [1]
- libcurl 7.10.3 and later have the ability to switch over to chunked Transfer-Encoding in cases where HTTP uploads are done with data of an unknown size.
- [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.
- [4]
- This behavior was different in versions before 7.17.0, where strings had to remain valid past the end of the curl_easy_setopt(3) call.
SEE ALSO
libcurl-easy(3), libcurl-errors(3), libcurl-multi(3), libcurl-url(3)| 2025-10-27 | libcurl |