Imported Upstream version 7.44.0
[platform/upstream/curl.git] / docs / libcurl / libcurl-tutorial.html
index 43bfc24..70e34aa 100644 (file)
@@ -4,15 +4,20 @@
 <title>libcurl-tutorial man page</title>
 <meta name="generator" content="roffit">
 <STYLE type="text/css">
-P.level0 {
+pre {
+  overflow: auto;
+  margin: 0;
+}
+
+P.level0, pre.level0 {
  padding-left: 2em;
 }
 
-P.level1 {
+P.level1, pre.level1 {
  padding-left: 4em;
 }
 
-P.level2 {
+P.level2, pre.level2 {
  padding-left: 6em;
 }
 
@@ -96,9 +101,9 @@ p.roffit {
 <p class="level0">If you at any point would like to blank all previously set options for a single easy handle, you can call <a Class="emphasis" href="./curl_easy_reset.html">curl_easy_reset</a> and you can also make a clone of an easy handle (with all its set options) using <a Class="emphasis" href="./curl_easy_duphandle.html">curl_easy_duphandle</a>. 
 <p class="level0">Many of the options you set in libcurl are "strings", pointers to data terminated with a zero byte. When you set strings with <a Class="emphasis" href="./curl_easy_setopt.html">curl_easy_setopt</a>, libcurl makes its own copy so that they don't need to be kept around in your application after being set[4]. 
 <p class="level0">One of the most basic properties to set in the handle is the URL. You set your preferred URL to transfer with <span Class="emphasis">CURLOPT_URL(3)</span> in a manner similar to: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;curl_easy_setopt(handle, CURLOPT_URL, "<a href="http://domain.com/">http://domain.com/</a>");
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;curl_easy_setopt(handle, CURLOPT_URL, "<a href="http://domain.com/">http://domain.com/</a>");
+</pre>
 
 <p class="level0">
 <p class="level0">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: 
@@ -119,25 +124,7 @@ p.roffit {
 <p class="level0">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. 
 <p class="level0">For some protocols, downloading a file can involve a complicated process of logging in, setting the transfer mode, changing the current directory and finally transferring the file data. libcurl takes care of all that complication for you. Given simply the URL to a file, libcurl will take care of all the details needed to get the file moved from one machine to another. 
 <p class="level0"><a name="Multi-threading"></a><h2 class="nroffsh">Multi-threading Issues</h2>
-<p class="level0">The first basic rule is that you must <span Class="bold">never</span> simultaneously share a libcurl handle (be it easy or multi or whatever) between multiple threads. Only use one handle in one thread at any time. You can pass the handles around among threads, but you must never use a single handle from more than one thread at any given time. 
-<p class="level0">libcurl is completely thread safe, except for two issues: signals and SSL/TLS handlers. Signals are used for timing out name resolves (during DNS lookup) - when built without using either the c-ares or threaded resolver backends. 
-<p class="level0">If you are accessing HTTPS or FTPS URLs in a multi-threaded manner, you are then of course using the underlying SSL library multi-threaded and those libs might have their own requirements on this issue. Basically, you need to provide one or two functions to allow it to function properly. For all details, see this: 
-<p class="level0">OpenSSL 
-<p class="level0">&nbsp;<a href="http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION">http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION</a> 
-<p class="level0">GnuTLS 
-<p class="level0">&nbsp;<a href="http://gnutls.org/manual/html_node/Thread-safety.html">http://gnutls.org/manual/html_node/Thread-safety.html</a> 
-<p class="level0">NSS 
-<p class="level0">&nbsp;is claimed to be thread-safe already without anything required. 
-<p class="level0">PolarSSL 
-<p class="level0">&nbsp;Required actions unknown. 
-<p class="level0">yassl 
-<p class="level0">&nbsp;Required actions unknown. 
-<p class="level0">axTLS 
-<p class="level0">&nbsp;Required actions unknown. 
-<p class="level0">Secure Transport 
-<p class="level0">&nbsp;The engine is fully thread-safe, and no additional steps are required. 
-<p class="level0">When using multiple threads you should set the <span Class="emphasis">CURLOPT_NOSIGNAL(3)</span> option to 1 for all handles. Everything will or might work fine except that timeouts are not honored during the DNS lookup - which you can work around by building libcurl with c-ares support. c-ares is a library that provides asynchronous name resolves. On some platforms, libcurl simply will not function properly multi-threaded unless this option is set. 
-<p class="level0">Also, note that <span Class="emphasis">CURLOPT_DNS_USE_GLOBAL_CACHE(3)</span> is not thread-safe. 
+<p class="level0">libcurl is thread safe but there are a few exceptions. Refer to <a Class="emphasis" href="./libcurl-thread.html">libcurl-thread</a> for more information. 
 <p class="level0"><a name="When"></a><h2 class="nroffsh">When It Doesn't Work</h2>
 <p class="level0">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. 
 <p class="level0">There's one golden rule when these things occur: set the <span Class="emphasis">CURLOPT_VERBOSE(3)</span> option to 1. 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 <span Class="emphasis">CURLOPT_HEADER(3)</span> set 1. 
@@ -155,10 +142,10 @@ p.roffit {
 <p class="level0">Tell libcurl that we want to upload: 
 <p class="level0">&nbsp;curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1L); 
 <p class="level0">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 <span Class="emphasis">CURLOPT_INFILESIZE_LARGE(3)</span> for all known file sizes like this[1]: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;/* in this example, file_size must be an curl_off_t variable */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;/* in this example, file_size must be an curl_off_t variable */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);
+</pre>
 
 <p class="level0">
 <p class="level0">When you call <a Class="emphasis" href="./curl_easy_perform.html">curl_easy_perform</a> 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. 
@@ -174,11 +161,11 @@ p.roffit {
 <p class="level0">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 <span Class="emphasis">CURLOPT_NETRC(3)</span> option: 
 <p class="level0">&nbsp;curl_easy_setopt(easyhandle, CURLOPT_NETRC, 1L); 
 <p class="level0">And a very basic example of how such a .netrc file may look like: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;machine myhost.mydomain.com
- &nbsp;login userlogin
- &nbsp;password secretword
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;machine myhost.mydomain.com
+&nbsp;login userlogin
+&nbsp;password secretword
+</pre>
 
 <p class="level0">
 <p class="level0">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. 
@@ -197,65 +184,77 @@ p.roffit {
 <p class="level0"><a name="HTTP"></a><h2 class="nroffsh">HTTP POSTing</h2>
 <p class="level0">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. 
 <p class="level0">The first version is the simple POST, the most common version, that most HTML pages using the &lt;form&gt; tag uses. We provide a pointer to the data and tell libcurl to post it all to the remote site: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;   char *data="name=daniel&project=curl";
- &nbsp;   curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data);
- &nbsp;   curl_easy_setopt(easyhandle, CURLOPT_URL, "<a href="http://posthere.com/">http://posthere.com/</a>");
- <p class="level0">&nbsp;   curl_easy_perform(easyhandle); /* post away! */
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;   char *data="name=daniel&project=curl";
+&nbsp;   curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data);
+&nbsp;   curl_easy_setopt(easyhandle, CURLOPT_URL, "<a href="http://posthere.com/">http://posthere.com/</a>");
+&nbsp;
+&nbsp;   curl_easy_perform(easyhandle); /* post away! */
+</pre>
 
 <p class="level0">
 <p class="level0">Simple enough, huh? Since you set the POST options with the <span Class="emphasis">CURLOPT_POSTFIELDS(3)</span>, this automatically switches the handle to use POST in the upcoming request. 
 <p class="level0">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 prevent 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. 
-<p class="level0"><pre>
-<p class="level0">&nbsp;struct curl_slist *headers=NULL;
- &nbsp;headers = curl_slist_append(headers, "Content-Type: text/xml");
- <p class="level0">&nbsp;/* post binary data */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, binaryptr);
- <p class="level0">&nbsp;/* set the size of the postfields data */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23L);
- <p class="level0">&nbsp;/* pass our list of custom made headers */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
- <p class="level0">&nbsp;curl_easy_perform(easyhandle); /* post away! */
- <p class="level0">&nbsp;curl_slist_free_all(headers); /* free the header list */
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;struct curl_slist *headers=NULL;
+&nbsp;headers = curl_slist_append(headers, "Content-Type: text/xml");
+&nbsp;
+&nbsp;/* post binary data */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, binaryptr);
+&nbsp;
+&nbsp;/* set the size of the postfields data */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23L);
+&nbsp;
+&nbsp;/* pass our list of custom made headers */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
+&nbsp;
+&nbsp;curl_easy_perform(easyhandle); /* post away! */
+&nbsp;
+&nbsp;curl_slist_free_all(headers); /* free the header list */
+</pre>
 
 <p class="level0">
 <p class="level0">While the simple examples above cover the majority of all cases where HTTP POST operations are required, they don't do multi-part formposts. Multi-part formposts were introduced as a better way to post (possibly large) binary data and were first documented in the <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> (updated in RFC2388). They're called multi-part because they're built by a chain of parts, each part being a single unit of data. Each part has its own name and contents. You can in fact create and post a multi-part 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 <a Class="emphasis" href="./curl_formadd.html">curl_formadd</a>. Using this function, you add parts to the form. When you're done adding parts, you post the whole form. 
 <p class="level0">The following example sets two simple text parts with plain textual contents, and then a file with binary contents and uploads the whole thing. 
-<p class="level0"><pre>
-<p class="level0">&nbsp;struct curl_httppost *post=NULL;
- &nbsp;struct curl_httppost *last=NULL;
- &nbsp;curl_formadd(&post, &last,
- &nbsp;             CURLFORM_COPYNAME, "name",
- &nbsp;             CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
- &nbsp;curl_formadd(&post, &last,
- &nbsp;             CURLFORM_COPYNAME, "project",
- &nbsp;             CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
- &nbsp;curl_formadd(&post, &last,
- &nbsp;             CURLFORM_COPYNAME, "logotype-image",
- &nbsp;             CURLFORM_FILECONTENT, "curl.png", CURLFORM_END);
- <p class="level0">&nbsp;/* Set the form info */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPPOST, post);
- <p class="level0">&nbsp;curl_easy_perform(easyhandle); /* post away! */
- <p class="level0">&nbsp;/* free the post data again */
- &nbsp;curl_formfree(post);
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;struct curl_httppost *post=NULL;
+&nbsp;struct curl_httppost *last=NULL;
+&nbsp;curl_formadd(&post, &last,
+&nbsp;             CURLFORM_COPYNAME, "name",
+&nbsp;             CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
+&nbsp;curl_formadd(&post, &last,
+&nbsp;             CURLFORM_COPYNAME, "project",
+&nbsp;             CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
+&nbsp;curl_formadd(&post, &last,
+&nbsp;             CURLFORM_COPYNAME, "logotype-image",
+&nbsp;             CURLFORM_FILECONTENT, "curl.png", CURLFORM_END);
+&nbsp;
+&nbsp;/* Set the form info */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPPOST, post);
+&nbsp;
+&nbsp;curl_easy_perform(easyhandle); /* post away! */
+&nbsp;
+&nbsp;/* free the post data again */
+&nbsp;curl_formfree(post);
+</pre>
 
 <p class="level0">
 <p class="level0">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 as you like, but this little example will show how you set headers to one specific part when you add that to the post handle: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;struct curl_slist *headers=NULL;
- &nbsp;headers = curl_slist_append(headers, "Content-Type: text/xml");
- <p class="level0">&nbsp;curl_formadd(&post, &last,
- &nbsp;             CURLFORM_COPYNAME, "logotype-image",
- &nbsp;             CURLFORM_FILECONTENT, "curl.xml",
- &nbsp;             CURLFORM_CONTENTHEADER, headers,
- &nbsp;             CURLFORM_END);
- <p class="level0">&nbsp;curl_easy_perform(easyhandle); /* post away! */
- <p class="level0">&nbsp;curl_formfree(post); /* free post */
- &nbsp;curl_slist_free_all(headers); /* free custom header list */
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;struct curl_slist *headers=NULL;
+&nbsp;headers = curl_slist_append(headers, "Content-Type: text/xml");
+&nbsp;
+&nbsp;curl_formadd(&post, &last,
+&nbsp;             CURLFORM_COPYNAME, "logotype-image",
+&nbsp;             CURLFORM_FILECONTENT, "curl.xml",
+&nbsp;             CURLFORM_CONTENTHEADER, headers,
+&nbsp;             CURLFORM_END);
+&nbsp;
+&nbsp;curl_easy_perform(easyhandle); /* post away! */
+&nbsp;
+&nbsp;curl_formfree(post); /* free post */
+&nbsp;curl_slist_free_all(headers); /* free custom header list */
+</pre>
 
 <p class="level0">
 <p class="level0">Since all options on an easyhandle are "sticky", they remain the same until changed even if you do call <a Class="emphasis" href="./curl_easy_perform.html">curl_easy_perform</a>, you may need to tell curl to go back to a plain GET request if you intend to do one as your next request. You force an easyhandle to go back to GET by using the <span Class="emphasis">CURLOPT_HTTPGET(3)</span> option: 
@@ -267,13 +266,13 @@ p.roffit {
 <p class="level0">Switch on the progress meter by, oddly enough, setting <span Class="emphasis">CURLOPT_NOPROGRESS(3)</span> to zero. This option is set to 1 by default. 
 <p class="level0">For most applications however, the built-in progress meter is useless and what instead is interesting is the ability to specify a progress callback. The function pointer you pass to libcurl will then be called on irregular intervals with information about the current transfer. 
 <p class="level0">Set the progress callback by using <span Class="emphasis">CURLOPT_PROGRESSFUNCTION(3)</span>. And pass a pointer to a function that matches this prototype: 
-<p class="level0"><pre>
-<p class="level0">&nbsp;int progress_callback(void *clientp,
- &nbsp;                      double dltotal,
- &nbsp;                      double dlnow,
- &nbsp;                      double ultotal,
- &nbsp;                      double ulnow);
- </pre>
+<p class="level0"><pre class="level0">
+&nbsp;int progress_callback(void *clientp,
+&nbsp;                      double dltotal,
+&nbsp;                      double dlnow,
+&nbsp;                      double ultotal,
+&nbsp;                      double ulnow);
+</pre>
 
 <p class="level0">
 <p class="level0">If any of the input arguments is unknown, a 0 will be passed. The first argument, the 'clientp' is the pointer you pass to libcurl with <span Class="emphasis">CURLOPT_PROGRESSDATA(3)</span>. libcurl won't touch it. 
@@ -282,15 +281,15 @@ p.roffit {
 <p class="level0">There's basically only one thing to keep in mind when using C++ instead of C when interfacing libcurl: 
 <p class="level0">The callbacks CANNOT be non-static class member functions 
 <p class="level0">Example C++ code: 
-<p class="level0"><pre>
-<p class="level0">class AClass {
- &nbsp;   static size_t write_data(void *ptr, size_t size, size_t nmemb,
- &nbsp;                            void *ourpointer)
- &nbsp;   {
- &nbsp;     /* do what you want with the data */
- &nbsp;   }
- &nbsp;}
- </pre>
+<p class="level0"><pre class="level0">
+class AClass {
+&nbsp;   static size_t write_data(void *ptr, size_t size, size_t nmemb,
+&nbsp;                            void *ourpointer)
+&nbsp;   {
+&nbsp;     /* do what you want with the data */
+&nbsp;   }
+&nbsp;}
+</pre>
 
 <p class="level0">
 <p class="level0"><a name="Proxies"></a><h2 class="nroffsh">Proxies</h2>
@@ -373,22 +372,26 @@ p.roffit {
 <p class="level1">
 <p class="level0"><a name="Modify"></a><span class="nroffip">Modify Headers</span> 
 <p class="level1">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 is this easy: 
-<p class="level1"><pre>
-<p class="level1">&nbsp;struct curl_slist *headers=NULL; /* init to NULL is important */
- <p class="level1">&nbsp;headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
- &nbsp;headers = curl_slist_append(headers, "X-silly-content: yes");
- <p class="level1">&nbsp;/* pass our list of custom made headers */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
- <p class="level1">&nbsp;curl_easy_perform(easyhandle); /* transfer http */
- <p class="level1">&nbsp;curl_slist_free_all(headers); /* free the header list */
- </pre>
+<p class="level1"><pre class="level1">
+&nbsp;struct curl_slist *headers=NULL; /* init to NULL is important */
+&nbsp;
+&nbsp;headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
+&nbsp;headers = curl_slist_append(headers, "X-silly-content: yes");
+&nbsp;
+&nbsp;/* pass our list of custom made headers */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
+&nbsp;
+&nbsp;curl_easy_perform(easyhandle); /* transfer http */
+&nbsp;
+&nbsp;curl_slist_free_all(headers); /* free the header list */
+</pre>
 
 <p class="level1">
 <p class="level1">... and if you think some of the internally generated headers, such as Accept: or Host: don't contain the data you want them to contain, you can replace them by simply setting them too: 
-<p class="level1"><pre>
-<p class="level1">&nbsp;headers = curl_slist_append(headers, "Accept: Agent-007");
- &nbsp;headers = curl_slist_append(headers, "Host: munged.host.line");
- </pre>
+<p class="level1"><pre class="level1">
+&nbsp;headers = curl_slist_append(headers, "Accept: Agent-007");
+&nbsp;headers = curl_slist_append(headers, "Host: munged.host.line");
+</pre>
 
 <p class="level1">
 <p class="level1">
@@ -411,13 +414,16 @@ p.roffit {
 <p class="level1">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. 
 <p class="level1">Sending custom commands to a FTP server means that you need to send the commands exactly as the FTP server expects them (<a href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</a> 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 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. 
 <p class="level1">A little example that deletes a given file before an operation: 
-<p class="level1"><pre>
-<p class="level1">&nbsp;headers = curl_slist_append(headers, "DELE file-to-remove");
- <p class="level1">&nbsp;/* pass the list of custom commands to the handle */
- &nbsp;curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers);
- <p class="level1">&nbsp;curl_easy_perform(easyhandle); /* transfer ftp data! */
- <p class="level1">&nbsp;curl_slist_free_all(headers); /* free the header list */
- </pre>
+<p class="level1"><pre class="level1">
+&nbsp;headers = curl_slist_append(headers, "DELE file-to-remove");
+&nbsp;
+&nbsp;/* pass the list of custom commands to the handle */
+&nbsp;curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers);
+&nbsp;
+&nbsp;curl_easy_perform(easyhandle); /* transfer ftp data! */
+&nbsp;
+&nbsp;curl_slist_free_all(headers); /* free the header list */
+</pre>
 
 <p class="level1">
 <p class="level1">If you would instead want this operation (or chain of operations) to happen _after_ the data transfer took place the option to <a Class="emphasis" href="./curl_easy_setopt.html">curl_easy_setopt</a> would instead be called <span Class="emphasis">CURLOPT_POSTQUOTE(3)</span> and used the exact same way. 
@@ -435,7 +441,7 @@ p.roffit {
 <p class="level0">&nbsp;curl_easy_setopt(easyhandle, CURLOPT_COOKIE, "name1=var1; name2=var2;"); 
 <p class="level0">In many cases, that is not enough. You might want to dynamically save whatever cookies the remote server passes to you, and make sure those cookies are then used accordingly on later requests. 
 <p class="level0">One way to do this, is to save all headers you receive in a plain file and when you make a request, you tell libcurl to read the previous headers to figure out which cookies to use. Set the header file to read cookies from with <span Class="emphasis">CURLOPT_COOKIEFILE(3)</span>. 
-<p class="level0">The <span Class="emphasis">CURLOPT_COOKIEFILE(3)</span> option also automatically enables the cookie parser in libcurl. Until the cookie parser is enabled, libcurl will not parse or understand incoming cookies and they will just be ignored. However, when the parser is enabled the cookies will be understood and the cookies will be kept in memory and used properly in subsequent requests when the same handle is used. Many times this is enough, and you may not have to save the cookies to disk at all. Note that the file you specify to ICURLOPT_COOKIEFILE(3)</span> doesn't have to exist to enable the parser, so a common way to just enable the parser and not read any cookies is to use the name of a file you know doesn't exist. 
+<p class="level0">The <span Class="emphasis">CURLOPT_COOKIEFILE(3)</span> option also automatically enables the cookie parser in libcurl. Until the cookie parser is enabled, libcurl will not parse or understand incoming cookies and they will just be ignored. However, when the parser is enabled the cookies will be understood and the cookies will be kept in memory and used properly in subsequent requests when the same handle is used. Many times this is enough, and you may not have to save the cookies to disk at all. Note that the file you specify to <span Class="emphasis">CURLOPT_COOKIEFILE(3)</span> doesn't have to exist to enable the parser, so a common way to just enable the parser and not read any cookies is to use the name of a file you know doesn't exist. 
 <p class="level0">If you would rather use existing cookies that you've previously received with your Netscape or Mozilla browsers, you can make libcurl use that cookie file as input. The <span Class="emphasis">CURLOPT_COOKIEFILE(3)</span> is used for that too, as libcurl will automatically find out what kind of file it is and act accordingly. 
 <p class="level0">Perhaps the most advanced cookie operation libcurl offers, is saving the entire internal cookie state back into a Netscape/Mozilla formatted cookie file. We call that the cookie-jar. When you set a file name with <span Class="emphasis">CURLOPT_COOKIEJAR(3)</span>, that file name will be created and all received cookies will be stored in it when <a Class="emphasis" href="./curl_easy_cleanup.html">curl_easy_cleanup</a> is called. This enables cookies to get passed on properly between multiple handles without any information getting lost. 
 <p class="level0"><a name="FTP"></a><h2 class="nroffsh">FTP Peculiarities We Need</h2>