LinuxDevCenter.com

oreilly.comSafari Books Online.Conferences.

We've expanded our Linux news coverage and improved our search! Search for all things Linux across O'Reilly!

Search
Search Tips

advertisement

Listen Print Discuss Subscribe to Linux Subscribe to Newsletters

Simplify Network Programming with libCURL
Pages: 1, 2

FTP Upload (Push a File to a Legacy System)

Legacy system uploads and downloads go hand in hand. The stub program step3 uses libCURL to log in to a remote FTP host and upload a file. It also describes a way to use a true C++ object as the callback handler (albeit indirectly).



step2 takes the remote FTP host, log in, and password as arguments. (A real app would read in this data from a config file; for now, pretend it's not a security problem to specify it on the command line.) The example merges the hostname with the target file name to form the URL:

ftp://host/file

It also concatenates the log in and password into a string used as the context option CURLOPT_USERPWD:

login:password

Note that you can also put the log in info in the URL:

ftp://login:password@host/file

This URL format is simply another means to pass the log in information to the API. Unlike a browser, libCURL doesn't use a cache or history bar that prying eyes can later discover. (Hopefully, the remote FTP server doesn't log that kind of information, either.) Nor is the information available via ps browsing.

For firewall-friendly transfers, the context option CURLOPT_FTP_USE_EPSV tells the library to use passive FTP.

libCURL doesn't limit you to file transfers alone. You can also send arbitrary FTP commands, such as mkdir or cwd. Store the commands in a libCURL linked list (curl_slist*):

struct curl_slist* commands = NULL ;
commands = curl_slist_append( commands , "mkdir /some/path" ) ;
commands = curl_slist_append( commands , "mkdir /another/path" ) ;
... 

The CURLOPT_QUOTE context option executes commands after logging in to the FTP server but before transferring data. CURLOPT_POSTQUOTE specifies a list of commands to execute after having transferred data.

curl_easy_setopt( ctx , CURLOPT_QUOTE , commands ) ;
// ... call curl_easy_perform() to run the FTP session ...
curl_slist_free_all( commands ) ;

You can use these context options to curl-ify your old FTP scripts. step3 uses CURLOPT_QUOTE to call cwd / such that the file uploads relative to the root directory of the FTP server. (Without an explicit directory change, uploads operate to a path relative to the user's home directory.)

The context option CURLOPT_UPLOAD tells the library this will be an upload call.

CURLOPT_FTPAPPEND tells libCURL to append to the target file instead of overwriting it. It's not necessary in this example, but it's something you often see in legacy FTP jobs.

Similar to downloading data, when uploading you have a choice between passing the libCURL library a file handle or creating the data yourself in a callback.

To upload data from an existing file handle, set that FILE* as the context option CURLOPT_READDATA.

To use a callback instead (for example, to generate upload data on the fly), assign a function to the context option CURLOPT_READFUNCTION. The function signature is very similar to that of CURLOPT_WRITEFUNCTION:

size_t function(
  char* buffer ,
  size_t size ,
  size_t nitems ,
  void* userData
) ;

The difference is that buffer is where you store data in this case, and the product size*nmemb is the maximum number of bytes you can put there. (Return the number of bytes you put in the buffer.) userData is the value assigned to CURLOPT_READDATA.

step3's callback function is rather brief. I employ a C++ template technique to use an object indirectly as a callback handler. If you're not familiar with templates, note that the declaration

template< typename T > 
class UploadHandler {
  ...

means the class UploadHandler is incomplete as written. The data type T comes from elsewhere in the code, when registering the function with libCURL:

curl_easy_setopt(
  ctx ,
  CURLOPT_READFUNCTION ,
  UploadHandler< UploadData >::execute
);

Here, UploadData is the type of object the handler function will use to do the work.

In turn, the static class function UploadHandler::execute() is a mere pass-through: it casts the userData value to type T and invokes T::execute() to do the actual work.

static size_t execute(
  char* buffer ,
  size_t size ,
  size_t nitems ,
  void* userData
){

  T* realHandler = static_cast< T* >( userData ) ;
  return( realHandler->execute( buffer , size , nitems ) ) ;

}

UploadHandler will work with any class that implements a fitting execute member function. I could have used standard inheritance instead:

// ... inside UploadHandler::execute() ...

Handler* h = static_cast< Handler* >( userData ) ;
return( h->execute( ... ) ) ;

I prefer the flexibility of templates, though. Inheritance would tie all handler objects to the Handler interface.

Similar to download callbacks, the library may call upload callbacks called several times for a single file. Code accordingly.

HTTP POST (Populate a Web Form)

HTTP POST operations send form data to a web server as well as making code-to-code calls such as those in web services. The request body comprises the POST data.

This article's final example, step4, demonstrates how to use libCURL for an HTTP POST. It also explains how to set up custom HTTP request headers, such as browser identification.

The POST body is just a string with & characters between key=value pairs:

const char* postData = "param1=value1&param2=value2&..." ;

Pass this string to the library by assigning it to the CURLOPT_POSTFIELDS option:

curl_easy_setopt( ctx , CURLOPT_POSTFIELDS , postData ) ;

Assign a curl_slist* to CURLOPT_HTTPHEADER to set custom HTTP headers:

curl_slist* responseHeaders = NULL ;

responseHeaders = curl_slist_append(
  responseHeaders ,
  "Expect: 100-continue"
) ;

// ... other curl_slist_append() calls ...

curl_easy_setopt(
  ctx ,
  CURLOPT_HTTPHEADER ,
  responseHeaders
) ;

Note that libCURL clients skip the intermediate step of downloading and processing a form's HTML. In turn, it is unaware of any hidden fields or client-side technologies used therein (such as JavaScript). Put another way, you have to know what fields the web server expects before you can use a libCURL client to POST data.

Conclusion

libCURL provides clean, simple networking for your native-code applications. With this API in your toolbox, you can incorporate one-off FTP operations into your main application, automate HTTP POST requests, and more.

There's much more to libCURL than I've presented here. The examples should, however, give you a head start in putting libCURL to use in your own apps.

Resources

  • The article's sample code includes the source for the stub programs, as well as a JSP and PHP page with which to test step4. (The JSP requires a servlet spec 2.4 container, such as Tomcat 5, and a proper 2.4 web.xml.)

    The pages simply echo the request headers and POST parameters received from the client.

  • The curl web site has documentation and tutorials.

  • The TCPMon utility ships with Apache's Axis web services project. It's a listening proxy that shows client/server conversations in a GUI window. I've found it invaluable for debugging problems with my curl code, especially HTTP POST operations.

    TCPMon is a Java application and is thus portable to any Java-enabled platform that meets the JDK version requirements.

  • libCURL is especially useful for creating REST-based web services clients. Also known as XML over HTTP, REST web service calls encapsulate HTTP GET or POST requests instead of wrapping them in a SOAP envelope. Amazon.com, for example, offers its public web services API via REST as well as SOAP. Yahoo's web services use REST exclusively.

Ethan McCallum grew from curious child to curious adult, turning his passion for technology into a career.

Linux Network Administrator's Guide

Related Reading

Linux Network Administrator's Guide
By Tony Bautts, Terry Dawson, Gregor N. Purdy

Return to the Linux DevCenter.


Have a question about the example code? Ask Ethan here.
You must be logged in to the O'Reilly Network to post a talkback.
Post Comment


Tagged Articles

Post to del.icio.us

This article has been tagged:

programming

Articles that share the tag programming:

Rolling with Ruby on Rails (1374 tags)

Very Dynamic Web Interfaces (279 tags)

Ajax on Rails (231 tags)

Understanding MVC in PHP (202 tags)

A Simpler Ajax Path (186 tags)

View All

curl

Articles that share the tag curl:

Simplify Network Programming with libCURL (4 tags)

View All

network

Articles that share the tag network:

Untwisting Python Network Programming (67 tags)

Wireless Mesh Networking (52 tags)

Building a Desktop Firewall (24 tags)

VPN on Mac OS X (19 tags)

Demystifying LDAP (19 tags)

View All

http

Articles that share the tag http:

How to Create a REST Protocol (45 tags)

Very Dynamic Web Interfaces (40 tags)

XML on the Web Has Failed (28 tags)

How Java Web Servers Work (14 tags)

Constructing or Traversing URIs? (13 tags)

View All

c

Articles that share the tag c:

Using Design by Contract in C (14 tags)

Extending Ruby with C (13 tags)

C is for Cocoa (5 tags)

Smart Pointers in C++ (4 tags)

make for Nonprogrammers (3 tags)

View All

Sponsored Resources

  • Inside Lightroom
Advertisement

Sponsored by:

O'Reilly Media

©2009, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
O'Reilly FYI
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com