Switch from libsoup to libcurl, add login and my information support
authorPhilipp Zabel <philipp.zabel@gmail.com>
Sat, 1 May 2010 09:04:23 +0000 (11:04 +0200)
committerPhilipp Zabel <philipp.zabel@gmail.com>
Sat, 1 May 2010 09:04:23 +0000 (11:04 +0200)
libsoup in maemo doesn't support encrypted connections.

Makefile.am
configure.ac
src/adac-mitfahrclub.vala
src/beifahrer-cli.vala
vapi/libcurl.deps [new file with mode: 0644]
vapi/libcurl.vapi [new file with mode: 0644]

index 0b76648..4f60bc9 100644 (file)
@@ -59,7 +59,7 @@ beifahrer_VALAFLAGS = --disable-dbus-transformation --thread --vapidir ./vapi \
         --pkg libosso-abook-1.0 \
         --pkg libhildonmime \
         --pkg liblocation \
-       --pkg libsoup-2.4
+       --pkg libcurl
 beifahrer_CFLAGS = -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\" \
        ${DBUS_CFLAGS} \
        ${GCONF_CFLAGS} \
@@ -68,7 +68,7 @@ beifahrer_CFLAGS = -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\" \
        ${MAEMO_LAUNCHER_CFLAGS} \
        ${OSSO_CFLAGS} \
        ${ABOOK_CFLAGS} \
-       ${SOUP_CFLAGS} \
+       ${CURL_CFLAGS}
        ${XML_CFLAGS}
 beifahrer_LDADD = \
        ${DBUS_LIBS} \
@@ -78,7 +78,7 @@ beifahrer_LDADD = \
        ${MAEMO_LAUNCHER_LIBS} \
        ${OSSO_LIBS} \
        ${ABOOK_LIBS} \
-       ${SOUP_LIBS} \
+       ${CURL_LIBS} \
        ${XML_LIBS}
 
 beifahrer_cli_VALASOURCES = src/beifahrer-cli.vala src/adac-mitfahrclub.vala
@@ -89,15 +89,18 @@ src/beifahrer-cli.c: ${beifahrer_cli_VALASOURCES}
        ${VALAC} -C ${beifahrer_cli_VALASOURCES} ${beifahrer_cli_VALAFLAGS}
 
 beifahrer_cli_VALAFLAGS = --thread --vapidir ./vapi \
-       --pkg libsoup-2.4 \
+       --pkg gio-2.0 \
+       --pkg libcurl \
        --pkg libxml-2.0
 beifahrer_cli_CFLAGS = -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\" \
        ${GIO_CFLAGS} \
-       ${SOUP_CFLAGS} \
+       ${CURL_CFLAGS} \
+       ${GTHREAD_CFLAGS} \
        ${XML_CFLAGS}
 beifahrer_cli_LDADD = \
        ${GIO_LIBS} \
-       ${SOUP_LIBS} \
+       ${CURL_LIBS} \
+       ${GTHREAD_LIBS} \
        ${XML_LIBS}
 
 CLEANFILES = \
index f7c3c92..72a1c2b 100644 (file)
@@ -16,6 +16,10 @@ AC_PROG_LIBTOOL
 
 CFLAGS="$CFLAGS -Wall -ansi -Wmissing-prototypes -Wmissing-declarations"
 
+PKG_CHECK_MODULES(CURL, libcurl)
+AC_SUBST(CURL_LIBS)
+AC_SUBST(CURL_CFLAGS)
+
 PKG_CHECK_MODULES(GCONF, gconf-2.0 >= 2.16.0)
 AC_SUBST(GCONF_LIBS)
 AC_SUBST(GCONF_CFLAGS)
@@ -32,6 +36,10 @@ PKG_CHECK_MODULES(GOBJECT, gobject-2.0)
 AC_SUBST(GOBJECT_LIBS)
 AC_SUBST(GOBJECT_CFLAGS)
 
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
+AC_SUBST(GTHREAD_LIBS)
+AC_SUBST(GTHREAD_CFLAGS)
+
 PKG_CHECK_MODULES(HILDON, hildon-1 >= 2.2.0)
 AC_SUBST(HILDON_LIBS)
 AC_SUBST(HILDON_CFLAGS)
@@ -67,10 +75,6 @@ PKG_CHECK_MODULES(OSSO, libosso >= 2.20)
 AC_SUBST(OSSO_LIBS)
 AC_SUBST(OSSO_CFLAGS)
 
-PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.26.3)
-AC_SUBST(SOUP_LIBS)
-AC_SUBST(SOUP_CFLAGS)
-
 PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.20.3)
 AC_SUBST(GIO_LIBS)
 AC_SUBST(GIO_CFLAGS)
index a9a8ace..b78fe54 100644 (file)
@@ -71,40 +71,201 @@ public class Lift : Object {
        }
 }
 
-public class CallbackMessage : Soup.Message {
-       public SourceFunc callback;
-
-       public CallbackMessage (string url, SourceFunc? _callback) {
-               method = "GET";
-               callback = _callback;
-               set_uri (new Soup.URI (url));
+public class MyInformation {
+       public enum Gender {
+               MALE,
+               FEMALE
        }
+
+       public Gender gender;
+       public string title;
+       public string first_name;
+       public string last_name;
+       public Date birthday;
+       public Date registered_since;
+
+       // Address
+       public string street;
+       public string number;
+       public string zip_code;
+       public string city;
+       public string country;
+
+       // Contact
+       public string phone1;
+       public string phone2;
+       public string phone3;
+       public string cell;
+       public string email1;
+       public string email2;
+
+       public bool smoker;
+       public bool adac_member;
 }
 
 public class AdacMitfahrclub {
-       const string BASE_URI = "http://mitfahrclub.adac.de";
+       const string HTTP_BASE_URI = "http://mitfahrclub.adac.de";
+       const string HTTPS_BASE_URI = "https://mitfahrclub.adac.de";
 
-       Soup.SessionAsync session;
+       Curl.EasyHandle curl;
        List<City> city_list = null;
 
        public AdacMitfahrclub () {
-               session = new Soup.SessionAsync ();
+               curl = new Curl.EasyHandle ();
+               // FIXME: Fremantle SDK doesn't come with certs
+               curl.setopt (Curl.Option.SSL_VERIFYPEER, 0);
+       //      curl.setopt (Curl.Option.VERBOSE, 1);
        }
 
-       private void message_finished (Soup.Session session, Soup.Message message) {
-               Idle.add (((CallbackMessage) message).callback);
+       private string _url = null;
+       private SourceFunc callback = null;
+       void* download_thread () {
+               curl.setopt (Curl.Option.WRITEFUNCTION, write_callback);
+               curl.setopt (Curl.Option.WRITEDATA, this);
+               curl.setopt (Curl.Option.URL, _url);
+               if (aeolus_cookie != null)
+                       curl.setopt (Curl.Option.COOKIE, "MIKINIMEDIA=%s; Quirinus[adacAeolus]=%s;".printf (mikini_cookie, aeolus_cookie));
+               else if (mikini_cookie != null)
+                       curl.setopt (Curl.Option.COOKIE, "MIKINIMEDIA=%s".printf (mikini_cookie));
+               var res = curl.perform ();
+
+               if (callback != null)
+                       Idle.add (callback);
+               callback = null;
+
+               return null;
+       }
+
+       StringBuilder result;
+
+       [CCode (instance_pos = -1)]
+       size_t write_callback (void *buffer, size_t size, size_t nmemb) {
+       //      if (cancellable != null && cancellable.is_cancelled ())
+       //              return 0;
+
+               result.append_len ((string) buffer, (ssize_t) (size * nmemb));
+
+               return size * nmemb;
        }
 
        private async Html.Doc* get_html_document (string url) {
-               var message = new CallbackMessage (url, get_html_document.callback);
-               session.queue_message (message, message_finished);
+               _url = url;
+               callback = get_html_document.callback;
+               result = new StringBuilder ();
+                try {
+                        Thread.create(download_thread, false);
+                } catch (ThreadError e) {
+                        critical ("Failed to create download thread\n");
+                        return null;
+                }
 
                yield;
 
-               return Html.Doc.read_memory ((char[]) message.response_body.data, (int) message.response_body.length,
+               return Html.Doc.read_memory ((char[]) result.str, (int) result.len,
                                             url, null, Html.ParserOption.NOERROR | Html.ParserOption.NOWARNING);
        }
 
+       private string username;
+       private string password;
+       private string mikini_cookie;
+       private string aeolus_cookie;
+
+       public void set_cookie (string value) {
+               aeolus_cookie = value;
+       }
+
+       public void login (string? _username, string? _password) {
+               username = _username;
+               password = _password;
+               if (logged_in)
+                       return;
+               if (login_callback != null)
+                       return;
+               login_thread ();
+       }
+
+       public bool logged_in = false;
+       private SourceFunc login_callback = null;
+       public async bool login_async (string? _username, string? _password) {
+               username = _username;
+               password = _password;
+               if (logged_in)
+                       return true;
+               if (login_callback != null)
+                       return false;
+               login_callback = login_async.callback;
+               result = new StringBuilder ();
+                try {
+                        Thread.create(login_thread, false);
+                } catch (ThreadError e) {
+                        critical ("Failed to create login thread\n");
+                        return false;
+                }
+
+               yield;
+               login_callback = null;
+
+               return logged_in;
+       }
+
+       void *login_thread () {
+               result = new StringBuilder ();
+               curl.setopt (Curl.Option.URL, HTTP_BASE_URI);
+               curl.setopt (Curl.Option.WRITEFUNCTION, write_callback);
+               curl.setopt (Curl.Option.WRITEDATA, this);
+               curl.setopt (Curl.Option.COOKIEFILE, "");
+               var res = curl.perform ();
+
+               Curl.SList cookies;
+               curl.getinfo (Curl.Info.COOKIELIST, out cookies);
+               unowned Curl.SList cookie = cookies;
+               while (cookie != null) {
+                       if (cookie.data != null) {
+                               var c = cookie.data.split ("\t");
+                               if (c.length > 5)
+                                       print ("%s=%s\n", c[5], c[6]);
+                                       if (c[5] == "MIKINIMEDIA")
+                                               mikini_cookie = c[6];
+                       }
+                       cookie = cookie.next;
+               }
+
+               result = new StringBuilder ();
+               string postdata = "data[User][continue]=/&data[User][js_allowed]=0&data[User][cookie_allowed]=1&data[User][username]=%s&data[User][password]=%s&data[User][remember_me]=1".printf (username, password);
+               curl.setopt (Curl.Option.POSTFIELDS, postdata);
+               curl.setopt (Curl.Option.URL, HTTPS_BASE_URI + "/users/login/");
+               curl.setopt (Curl.Option.SSL_VERIFYPEER, 0);
+               res = curl.perform ();
+       //      print ("%s\n", result.str);
+
+               cookies = null;
+               curl.getinfo (Curl.Info.COOKIELIST, out cookies);
+               cookie = cookies;
+               while (cookie != null) {
+                       if (cookie.data != null) {
+                               var c = cookie.data.split ("\t");
+                               if (c.length > 5)
+                                       print ("%s=%s\n", c[5], c[6]);
+                                       // "Quirinus[adacAeolus]"
+                                       if (c[5] == "Quirinus[adacAeolus]") {
+                                               aeolus_cookie = c[6];
+                                               logged_in = true;
+                                       }
+                       }
+                       cookie = cookie.next;
+               }
+
+               if (result.str.contains ("<div id=\"flashMessage\" class=\"message\">Die eingegebenen Zugangsdaten konnten nicht gefunden werden. Bitte versuchen Sie es erneut.</div>")) {
+                       print ("LOGIN FAILED\n");
+                       aeolus_cookie = null;
+                       logged_in = false;
+               }
+
+               if (login_callback != null)
+                       Idle.add (login_callback);
+               return null;
+       }
+
        private void save_city_list () {
                FileStream list_file = FileStream.open ("/home/user/.cache/beifahrer/city_list", "w");
                if (list_file == null)
@@ -166,7 +327,7 @@ public class AdacMitfahrclub {
        }
 
        public async unowned List<City>? download_city_list () {
-               var doc = yield get_html_document (BASE_URI);
+               var doc = yield get_html_document (HTTP_BASE_URI);
                if (doc == null) {
                        stderr.printf ("Error: parsing failed\n");
                        return null;
@@ -253,7 +414,7 @@ public class AdacMitfahrclub {
                        return null;
                }
 
-               string url = BASE_URI + "/mitfahrclub/%s/%s/b.html".printf (
+               string url = HTTP_BASE_URI + "/mitfahrclub/%s/%s/b.html".printf (
                        city_from,
                        city_to
                );
@@ -369,7 +530,7 @@ public class AdacMitfahrclub {
        }
 
        public async bool update_lift_details (Lift lift) {
-                string url = BASE_URI + lift.href;
+                string url = HTTP_BASE_URI + lift.href;
 
                var doc = yield get_html_document (url);
                if (doc == null) {
@@ -512,6 +673,139 @@ public class AdacMitfahrclub {
                return true;
        }
 
+       public async MyInformation get_my_information () {
+                string url = HTTPS_BASE_URI + "/users/view";
+
+               var doc = yield get_html_document (url);
+               if (doc == null) {
+                       stderr.printf ("Error: parsing failed\n");
+                       return null;
+               }
+
+               var table = search_tag_by_class (doc->children, "table", "user");
+               if (table == null) {
+                       stderr.printf ("Error: does not contain user table\n");
+                       return null;
+               }
+
+               var my_info = new MyInformation ();
+
+               Xml.Node* n;
+               for (n = table->children; n != null; n = n->next) {
+                       if (n->name == "tr") {
+                               var n2 = n->children;
+                               if (n2 == null || n2->name != "td" ||
+                                   n2->children == null || n2->children->name != "text")
+                                       continue;
+
+                               string text = n2->children->content;
+
+                               n2 = n2->next;
+                               if (n2 != null && n2->name == "text")
+                                       n2 = n2->next;
+                               if (n2 == null || n2->name != "td" ||
+                                   n2->children == null || n2->children->name != "text")
+                                       continue;
+
+                               string content = n2->children->content;
+
+                               switch (text) {
+                               case "Anrede":
+                                       my_info.gender = (content == "Herr") ? MyInformation.Gender.MALE : MyInformation.Gender.FEMALE;
+                                       continue;
+                               case "Titel":
+                                       my_info.title = content;
+                                       continue;
+                               case "Vorname":
+                                       my_info.first_name = content;
+                                       continue;
+                               case "Name":
+                                       my_info.last_name = content;
+                                       continue;
+                               case "Geburtsdatum":
+                       //              my_info.birthday = ...
+                                       continue;
+                               case "registriert seit":
+                       //              my_info.registered_since = content;
+                                       continue;
+                       //      default:
+                       //              print ("\t%s=%s\n", text, content);
+                       //              break;
+                               }
+
+                               text = content;
+
+                               n2 = n2->next;
+                               if (n2 != null && n2->name == "text")
+                                       n2 = n2->next;
+                               if (n2 == null || n2->name != "td")
+                                       continue;
+
+                               if (n2->children != null && n2->children->name != "text")
+                                       content = n2->children->content;
+                               else
+                                       content = "";
+
+                               switch (text) {
+                               case "Straße, Nr.":
+                                       my_info.street = content;
+                                       my_info.number = "";
+                                       continue;
+                               case "PLZ, Ort":
+                                       my_info.zip_code = "";
+                                       my_info.city = content;
+                                       continue;
+                               case "Land":
+                                       my_info.country = content;
+                                       continue;
+                               case "Telefon 1":
+                                       my_info.phone1 = content;
+                                       continue;
+                               case "Telefon 2":
+                                       my_info.phone2 = content;
+                                       continue;
+                               case "Telefon 3":
+                                       my_info.phone3 = content;
+                                       continue;
+                               case "Handy":
+                                       my_info.cell = content;
+                                       continue;
+                               case "Email 1":
+                                       my_info.email1 = content;
+                                       continue;
+                               case "Email 2":
+                                       my_info.email2 = content;
+                                       continue;
+                               case "Raucher":
+                                       // FIXME
+                                       my_info.smoker = false;
+                                       continue;
+                               case "ADAC-Mitglied":
+                                       // FIXME
+                                       my_info.adac_member = false;
+                                       continue;
+                       //      default:
+                       //              print ("\"%s\"=\"%s\"\n", text, content);
+                       //              break;
+                               }
+                       }
+               }
+
+/*
+                       <tr class="head top">
+                               <td width="150" class="label">Anrede</td>
+                               <td width="400">Herr</td>
+                       </tr>
+                       <tr class="head">
+                               <td class="label">Titel</td>
+
+                               <td>--</td>
+                       </tr>
+                       ...
+*/
+               return my_info;
+       }
+
        Xml.Node* search_tag_by_property (Xml.Node* node, string tag, string prop, string val) requires (node != null) {
                for (var n = node; n != null; n = n->next) {
                        if (n->name == tag && n->get_prop (prop) == val)
index 31bf3d9..aefa372 100644 (file)
@@ -72,17 +72,36 @@ async void get_details (string href) {
        loop.quit ();
 }
 
+void usage () {
+       print ("usage: beifahrer-cli <city_from> <city_to> [date]\n");
+       print ("or:    beifahrer-cli details <lift_url>\n");
+       print ("or:    beifahrer-cli login <username> <password>\n");
+       print ("or:    beifahrer-cli login <cookie>\n");
+}
+
 static int main (string[] args) {
        loop = new MainLoop (null);
 
        if (args.length < 3) {
-               print ("usage: beifahrer-cli <city_from> <city_to> [date]\n");
-               print ("or:    beifahrer-cli details <lift_url>\n");
+               usage ();
                return 1;
        }
 
+       Curl.global_init (Curl.GLOBAL_DEFAULT);
+
        adac = new AdacMitfahrclub ();
 
+       if (args[1] == "login") {
+               if (args.length > 3) {
+                       adac.login (args[2], args[3]);
+               } else {
+                       adac.set_cookie (args[2]);
+                       adac.login (null, null);
+               }
+               loop.run ();
+               return 0;
+       }
+
        if (args[1] == "details") {
                get_details.begin (args[2]);
 
@@ -104,5 +123,8 @@ static int main (string[] args) {
        get_lifts.begin (city_from, city_to, date);
 
        loop.run ();
+
+       Curl.global_cleanup ();
+
        return 0;
 }
diff --git a/vapi/libcurl.deps b/vapi/libcurl.deps
new file mode 100644 (file)
index 0000000..b3188f7
--- /dev/null
@@ -0,0 +1 @@
+posix
diff --git a/vapi/libcurl.vapi b/vapi/libcurl.vapi
new file mode 100644 (file)
index 0000000..5857006
--- /dev/null
@@ -0,0 +1,693 @@
+[CCode (cheader_filename = "curl/curl.h")]
+namespace Curl {
+       [CCode (cname = "CURL_GLOBAL_SSL")]
+       public const long GLOBAL_SSL;
+       [CCode (cname = "CURL_GLOBAL_WIN32")]
+       public const long GLOBAL_WIN32;
+       [CCode (cname = "CURL_GLOBAL_ALL")]
+       public const long GLOBAL_ALL;
+       [CCode (cname = "CURL_GLOBAL_NOTHING")]
+       public const long GLOBAL_NOTHING;
+       [CCode (cname = "CURL_GLOBAL_DEFAULT")]
+       public const int GLOBAL_DEFAULT;
+       public Curl.Code global_init (long flags);
+       public Curl.Code global_init_mem (long flags, Curl.MallocCallback m, Curl.FreeCallback f, Curl.ReallocCallback r, Curl.StrdupCallback s, Curl.CallocCallback c);
+       public static void global_cleanup ();
+       [Compact]
+       [CCode (cname = "CURL", cprefix = "curl_easy_", free_function = "curl_easy_cleanup")]
+       public class EasyHandle {
+               [CCode (cname = "curl_easy_init")]
+               public EasyHandle ();
+               [PrintfFormat]
+               public Curl.Code setopt (Curl.Option option, ...);
+               public Curl.Code perform ();
+               [PrintfFormat]
+               public Curl.Code getinfo (Curl.Info info, ...);
+               public Curl.EasyHandle duphandle ();
+               public void reset ();
+               public Curl.Code recv (void *buffer, size_t buflen, out size_t n);
+               public Curl.Code send (void *buffer, size_t buflen, out size_t n);
+               public string escape (string @string, int length);
+               public string unescape (string @string, int length, out int outlength);
+               [CCode (has_target = false)]
+               public delegate int SocketCallback (Curl.Socket s, int what, void* userp, void *socketp);
+       }
+       [CCode (cname = "CURLcode", cprefix = "CURLE_")]
+       public enum Code {
+               OK,
+               UNSUPPORTED_PROTOCOL,
+               FAILED_INIT,
+               URL_MALFORMAT,
+               COULDNT_RESOLVE_PROXY,
+               COULDNT_RESOLVE_HOST,
+               COULDNT_CONNECT,
+               FTP_WEIRD_SERVER_REPLY,
+               REMOTE_ACCESS_DENIED,
+               FTP_WEIRD_PASS_REPLY,
+               FTP_WEIRD_PASV_REPLY,
+               FTP_WEIRD_227_FORMAT,
+               FTP_CANT_GET_HOST,
+               FTP_COULDNT_SET_TYPE,
+               PARTIAL_FILE,
+               FTP_COULDNT_RETR_FILE,
+               QUOTE_ERROR,
+               HTTP_RETURNED_ERROR,
+               WRITE_ERROR,
+               UPLOAD_FAILED,
+               READ_ERROR,
+               OUT_OF_MEMORY,
+               OPERATION_TIMEDOUT,
+               FTP_PORT_FAILED,
+               FTP_COULDNT_USE_REST,
+               RANGE_ERROR,
+               HTTP_POST_ERROR,
+               SSL_CONNECT_ERROR,
+               BAD_DOWNLOAD_RESUME,
+               FILE_COULDNT_READ_FILE,
+               LDAP_CANNOT_BIND,
+               LDAP_SEARCH_FAILED,
+               FUNCTION_NOT_FOUND,
+               ABORTED_BY_CALLBACK,
+               BAD_FUNCTION_ARGUMENT,
+               INTERFACE_FAILED,
+               TOO_MANY_REDIRECTS,
+               UNKNOWN_TELNET_OPTION,
+               TELNET_OPTION_SYNTAX,
+               PEER_FAILED_VERIFICATION,
+               GOT_NOTHING,
+               SSL_ENGINE_NOTFOUND,
+               SSL_ENGINE_SETFAILED,
+               SEND_ERROR,
+               RECV_ERROR,
+               SSL_CERTPROBLEM,
+               SSL_CIPHER,
+               SSL_CACERT,
+               BAD_CONTENT_ENCODING,
+               LDAP_INVALID_URL,
+               FILESIZE_EXCEEDED,
+               USE_SSL_FAILED,
+               SEND_FAIL_REWIND,
+               SSL_ENGINE_INITFAILED,
+               LOGIN_DENIED,
+               TFTP_NOTFOUND,
+               TFTP_PERM,
+               REMOTE_DISK_FULL,
+               TFTP_ILLEGAL,
+               TFTP_UNKNOWNID,
+               REMOTE_FILE_EXISTS,
+               TFTP_NOSUCHUSER,
+               CONV_FAILED,
+               CONV_REQD,
+               SSL_CACERT_BADFILE,
+               REMOTE_FILE_NOT_FOUND,
+               SSH,
+               SSL_SHUTDOWN_FAILED,
+               AGAIN,
+               SSL_CRL_BADFILE,
+               SSL_ISSUER_ERROR,
+               [CCode (cname = "CURL_LAST")]
+               LAST
+       }
+       [CCode (name = "CURLoption", cprefix = "CURLOPT_")]
+       public enum Option {
+               FILE,
+               URL,
+               PORT,
+               PROXY,
+               USERPWD,
+               PROXYUSERPWD,
+               RANGE,
+               INFILE,
+               WRITEDATA,
+               READDATA,
+               ERRORBUFFER,
+               WRITEFUNCTION,
+               READFUNCTION,
+               TIMEOUT,
+               INFILESIZE,
+               POSTFIELDS,
+               REFERER,
+               FTPPORT,
+               USERAGENT,
+               LOW_SPEED_LIMIT,
+               LOW_SPEED_TIME,
+               RESUME_FROM,
+               COOKIE,
+               HTTPHEADER,
+               HTTPPOST,       // struct HTTPPost
+               SSLCERT,
+               KEYPASSWD,
+               CRLF,
+               QUOTE,
+               WRITEHEADER,
+               HEADERDATA,
+               COOKIEFILE,
+               SSLVERSION,
+               TIMECONDITION,
+               TIMEVALUE,
+               CUSTOMREQUEST,
+               STDERR,
+               POSTQUOTE,
+               WRITEINFO,
+               VERBOSE,
+               HEADER,
+               NOPROGRESS,
+               NOBODY,
+               FAILONERROR,
+               UPLOAD,
+               POST,
+               DIRLISTONLY,
+               APPEND,
+               NETRC,
+               FOLLOWLOCATION,
+               TRANSFERTEXT,
+               PUT,
+               PROGRESSFUNCTION,
+               PROGRESSDATA,
+               AUTOREFERER,
+               PROXYPORT,
+               POSTFIELDSIZE,
+               HTTPPROXYTUNNEL,
+               INTERFACE,
+               KRBLEVEL,
+               SSL_VERIFYPEER,
+               CAINFO,
+               MAXREDIRS,
+               FILETIME,
+               TELNETOPTIONS,
+               MAXCONNECTS,
+               CLOSEPOLICY,
+               FRESH_CONNECT,
+               FORBID_REUSE,
+               RANDOM_FILE,
+               EGDSOCKET,
+               CONNECTTIMEOUT,
+               HEADERFUNCTION,
+               HTTPGET,
+               SSL_VERIFYHOST,
+               COOKIEJAR,
+               SSL_CIPHER_LIST,
+               HTTP_VERSION,
+               FTP_USE_EPSV,
+               SSLCERTTYPE,
+               SSLKEY,
+               SSLKEYTYPE,
+               SSLENGINE,
+               SSLENGINE_DEFAULT,
+               DNS_USE_GLOBAL_CACHE,
+               DNS_CACHE_TIMEOUT,
+               PREQUOTE,
+               DEBUGFUNCTION,
+               DEBUGDATA,
+               COOKIESESSION,
+               CAPATH,
+               BUFFERSIZE,
+               NOSIGNAL,
+               SHARE,
+               PROXYTYPE ,
+               ENCODING,
+               PRIVATE,
+               HTTP200ALIASES,
+               UNRESTRICTED_AUTH,
+               FTP_USE_EPRT,
+               HTTPAUTH,
+               SSL_CTX_FUNCTION,
+               SSL_CTX_DATA,
+               FTP_CREATE_MISSING_DIRS,
+               PROXYAUTH,
+               FTP_RESPONSE_TIMEOUT,
+               IPRESOLVE,
+               MAXFILESIZE,
+               INFILESIZE_LARGE,
+               RESUME_FROM_LARGE,
+               MAXFILESIZE_LARGE,
+               NETRC_FILE,
+               USE_SSL,
+               POSTFIELDSIZE_LARGE,
+               TCP_NODELAY,
+               FTPSSLAUTH,
+               IOCTLFUNCTION,
+               IOCTLDATA,
+               FTP_ACCOUNT,
+               COOKIELIST,
+               IGNORE_CONTENT_LENGTH,
+               FTP_SKIP_PASV_IP,
+               FTP_FILEMETHOD,
+               LOCALPORT,
+               LOCALPORTRANGE,
+               CONNECT_ONLY,
+               CONV_FROM_NETWORK_FUNCTION,
+               CONV_TO_NETWORK_FUNCTION,
+               CONV_FROM_UTF8_FUNCTION,
+               MAX_SEND_SPEED_LARGE,
+               MAX_RECV_SPEED_LARGE,
+               FTP_ALTERNATIVE_TO_USER,
+               SOCKOPTFUNCTION,
+               SOCKOPTDATA,
+               SSL_SESSIONID_CACHE,
+               SSH_AUTH_TYPES,
+               SSH_PUBLIC_KEYFILE,
+               SSH_PRIVATE_KEYFILE,
+               FTP_SSL_CCC,
+               TIMEOUT_MS,
+               CONNECTTIMEOUT_MS,
+               HTTP_TRANSFER_DECODING,
+               HTTP_CONTENT_DECODING,
+               NEW_FILE_PERMS,
+               NEW_DIRECTORY_PERMS,
+               POSTREDIR,
+               SSH_HOST_PUBLIC_KEY_MD5,
+               OPENSOCKETFUNCTION,
+               OPENSOCKETDATA,
+               COPYPOSTFIELDS,
+               PROXY_TRANSFER_MODE,
+               SEEKFUNCTION,
+               SEEKDATA,
+               CRLFILE,
+               ISSUERCERT,
+               ADDRESS_SCOPE,
+               CERTINFO,
+               USERNAME,
+               PASSWORD,
+               PROXYUSERNAME,
+               PROXYPASSWORD,
+               NOPROXY,
+               TFTP_BLKSIZE,
+               SOCKS5_GSSAPI_SERVICE,
+               SOCKS5_GSSAPI_NEC,
+               PROTOCOLS,
+               REDIR_PROTOCOLS,
+               SSH_KNOWNHOSTS,
+               SSH_KEYFUNCTION,
+               SSH_KEYDATA,
+               LASTENTRY
+       }
+       [CCode (name = "CURLINFO", cprefix = "CURLINFO_")]
+       public enum Info {
+               STRING,
+               LONG,
+               DOUBLE,
+               SLIST,
+               EFFECTIVE_URL,
+               RESPONSE_CODE,
+               TOTAL_TIME,
+               NAMELOOKUP_TIME,
+               CONNECT_TIME,
+               PRETRANSFER_TIME,
+               SIZE_UPLOAD,
+               SIZE_DOWNLOAD,
+               SPEED_DOWNLOAD,
+               SPEED_UPLOAD,
+               HEADER_SIZE,
+               REQUEST_SIZE,
+               SSL_VERIFYRESULT,
+               FILETIME,
+               CONTENT_LENGTH_DOWNLOAD,
+               CONTENT_LENGTH_UPLOAD,
+               STARTTRANSFER_TIME,
+               CONTENT_TYPE,
+               REDIRECT_TIME,
+               REDIRECT_COUNT,
+               PRIVATE,
+               HTTP_CONNECTCODE,
+               HTTPAUTH_AVAIL,
+               PROXYAUTH_AVAIL,
+               OS_ERRNO,
+               NUM_CONNECTS,
+               SSL_ENGINES,
+               COOKIELIST,
+               LASTSOCKET,
+               FTP_ENTRY_PATH,
+               REDIRECT_URL,
+               PRIMARY_IP,
+               APPCONNECT_TIME,
+               CERTINFO,
+               CONDITION_UNMET,
+               LASTONE
+       }
+       [CCode (cname = "curl_progress_callback")]
+       public delegate int ProgressCallback (void* clientp, double dltotal, double dlnow, double ultotal, double ulnow);
+       [CCode (cname = "CURL_WRITEFUNC_PAUSE")]
+       public const size_t WRITEFUNC_PAUSE;
+       [CCode (cname = "curl_write_callback")]
+       public delegate size_t WriteCallback (char* buffer, size_t size, size_t nitems, void *outstream);
+       [CCode (cname = "CURL_SEEKFUNC_OK")]
+       public const int SEEKFUNC_OK;
+       [CCode (cname = "CURL_SEEKFUNC_FAIL")]
+       public const int SEEKFUNC_FAIL;
+       [CCode (cname = "CURL_SEEKFUNC_CANTSEEK")]
+       public const int SEEKFUNC_CANTSEEK;
+       [Ccode (cname = "curl_seek_callback")]
+       public delegate int SeekCallback (void* instream, Curl.Offset offset, int origin);
+       [CCode (cname = "CURL_READFUNC_ABORT")]
+       public const size_t READFUNC_ABORT;
+       [CCode (cname = "CURL_READFUNC_PAUSE")]
+       public const size_t READFUNC_PAUSE;
+       [CCode (cname = "curl_read_callback")]
+       public delegate size_t ReadCallback (char* buffer, size_t size, size_t nitems, void *instream);
+       [CCode (cname = "curlsocktype", cprefix = "CURLSOCKTYPE_")]
+       public enum SocketType {
+               IPCXN,
+               LAST
+       }
+       [CCode (cname = "curl_sockopt_callback")]
+       public delegate size_t SockoptCallback (void* clientp, Curl.Socket curlfd, Curl.SocketType purpose);
+       [CCode (cname = "curlioerr", cprefix = "CURLIOE_")]
+       public enum IOError {
+               OK,
+               UNKNOWNCMD,
+               FAILRESTART,
+               LAST
+       }
+       [CCode (cname = "curliocmd", cprefix = "CURLIOCMD_")]
+       public enum IOCmd {
+               NOP,
+               RESTARTREAD,
+               LAST
+       }
+       [CCode (cname = "curl_ioctl_callback")]
+       public delegate Curl.IOError IoctlCallback (Curl.EasyHandle handle, int cmd, void* clientp);
+       [CCode (cname = "curl_malloc_callback")]
+       public delegate void* MallocCallback (size_t size);
+       [CCode (cname = "curl_free_callback")]
+       public delegate void FreeCallback (void* ptr);
+       [CCode (cname = "curl_realloc_callback")]
+       public delegate void* ReallocCallback (void* ptr, size_t size);
+       [CCode (cname = "curl_strdup_callback")]
+       public delegate string StrdupCallback (string str);
+       [CCode (cname = "curl_calloc_callback")]
+       public delegate void* CallocCallback (size_t nmemb, size_t size);
+       [CCode (cname = "curl_infotype", cprefix = "CURLINFO_")]
+       public enum InfoType {
+               TEXT,
+               HEADER_IN,
+               HEADER_OUT,
+               DATA_IN,
+               DATA_OUT,
+               SSL_DATA_IN,
+               SSL_DATA_OUT
+       }
+       [CCode (cname = "curl_debug_callback")]
+       public delegate int DebugCallback (Curl.EasyHandle handle, Curl.InfoType type, [CCode (array_length_type = "size_t")] char[] data, void* userptr);
+       [CCode (cname = "curl_conv_callback")]
+       public delegate Curl.Code ConvCallback ([CCode (array_length_type = "size_t")] char[] buffer);
+       [CCode (cname = "curl_ssl_ctx_callback")]
+       public delegate Curl.Code SSLCtxCallback (Curl.EasyHandle curl, void* ssl_ctx, void* userptr);
+       [CCode (cname = "curl_proxytype", cprefix = "CURLPROXY_")]
+       public enum ProxyType {
+               HTTP,
+               HTTP_1_0,
+               SOCKS4,
+               SOCKS5,
+               SOCKS4A,
+               SOCKS5_HOSTNAME
+       }
+       namespace AuthType {
+               [CCode (cname = "CURLAUTH_NONE")]
+               public const int NONE;
+               [CCode (cname = "CURLAUTH_BASIC")]
+               public const int BASIC;
+               [CCode (cname = "CURLAUTH_DIGEST")]
+               public const int DIGEST;
+               [CCode (cname = "CURLAUTH_GSSNEGOTIATE")]
+               public const int GSSNEGOTIATE;
+               [CCode (cname = "CURLAUTH_NTLM")]
+               public const int NTLM;
+               [CCode (cname = "CURLAUTH_DIGEST_IE")]
+               public const int DIGEST_IE;
+               [CCode (cname = "CURLAUTH_ANY")]
+               public const int ANY;
+               [CCode (cname = "CURLAUTH_ANYSAFE")]
+               public const int ANYSAFE;
+       }
+       namespace SSHAuthType {
+               [CCode (cname = "CURLSSH_AUTH_ANY")]
+               public const int ANY;
+               [CCode (cname = "CURLSSH_AUTH_NONE")]
+               public const int NONE;
+               [CCode (cname = "CURLSSH_AUTH_PUBLICKEY")]
+               public const int PUBLICKEY;
+               [CCode (cname = "CURLSSH_AUTH_PASSWORD")]
+               public const int PASSWORD;
+               [CCode (cname = "CURLSSH_AUTH_HOST")]
+               public const int HOST;
+               [CCode (cname = "CURLSSH_AUTH_KEYBOARD")]
+               public const int KEYBOARD;
+               [CCode (cname = "CURLSSH_AUTH_DEFAULT")]
+               public const int DEFAULT;
+       }
+       public const int ERROR_SIZE;
+       [CCode (cname = "curl_usessl", cprefix = "CURLUSESSL_")]
+       public enum UseSSL {
+               NONE,
+               TRY,
+               CONTROL,
+               ALL
+       }
+       [CCode (cname = "curl_ftpccc", cprefix = "CURLFTPSSL_")]
+       enum FTPSSL {
+               CCC_NONE,
+               CCC_PASSIVE,
+               CCC_ACTIVE
+       }
+       [CCode (cname = "curl_ftpauth", cprefix = "CURLFTPAUTH_")]
+       enum FTPAuthType {
+               DEFAULT,
+               SSL,
+               TLS
+       }
+       [CCode (cname = "curl_ftpcreatedir", cprefix = "CURLFTP_CREATE_DIR_")]
+       enum FTPCreateDir {
+               NONE,
+               [CCode (cname = "CURLFTP_CREATE_DIR")]
+               CREATE,
+               RETRY
+       }
+       [CCode (cname = "curl_ftpmethod", cprefix = "CURLFTPMETHOD_")]
+       enum FTPMethod {
+               DEFAULT,
+               MULTICWD,
+               NOCWD,
+               SINGLECWD
+       }
+       namespace Proto {
+               [CCode (cname = "CURLPROTO_HTTP")]
+               public const int HTTP;
+               [CCode (cname = "CURLPROTO_HTTPS")]
+               public const int HTTPS;
+               [CCode (cname = "CURLPROTO_FTP")]
+               public const int FTP;
+               [CCode (cname = "CURLPROTO_FTPS")]
+               public const int FTPS;
+               [CCode (cname = "CURLPROTO_SCP")]
+               public const int SCP;
+               [CCode (cname = "CURLPROTO_SFTP")]
+               public const int SFTP;
+               [CCode (cname = "CURLPROTO_TELNET")]
+               public const int TELNET;
+               [CCode (cname = "CURLPROTO_LDAP")]
+               public const int LDAP;
+               [CCode (cname = "CURLPROTO_LDAPS")]
+               public const int LDAPS;
+               [CCode (cname = "CURLPROTO_DICT")]
+               public const int DICT;
+               [CCode (cname = "CURLPROTO_FILE")]
+               public const int FILE;
+               [CCode (cname = "CURLPROTO_TFTP")]
+               public const int TFTP;
+               [CCode (cname = "CURLPROTO_ALL")]
+               public const int ALL;
+       }
+       public const int IPRESOLVE_WHATEVER;
+       public const int IPRESOLVE_V4;
+       public const int IPRESOLVE_V6;
+       public const int REDIR_GET_ALL;
+       public const int REDIR_POST_301;
+       public const int REDIR_POST_302;
+       public const int REDIR_POST_ALL;
+       [CCode (cname = "curl_TimeCond", cprefix = "CURL_TIMECOND_")]
+       public enum TimeCond {
+               NONE,
+               IFMODSINCE,
+               IFUNMODSINCE,
+               LASTMOD
+       }
+       [CCode (cname = "CURLformoption", cprefix = "CURLFORM_")]
+       public enum FormOption {
+               COPYNAME,
+               PTRNAME,
+               NAMELENGTH,
+               COPYCONTENTS,
+               PTRCONTENTS,
+               CONTENTSLENGTH,
+               FILECONTENT,
+               ARRAY,
+               OBSOLETE,
+               FILE,
+               BUFFER,
+               BUFFERPTR,
+               BUFFERLENGTH,
+               CONTENTTYPE,
+               CONTENTHEADER,
+               FILENAME,
+               END,
+               OBSOLETE2,
+               STREAM
+       }
+       [CCode (cname = "struct curl_forms")]
+       public struct Forms {
+               public Curl.FormOption option;
+               public string value;
+       }
+       [CCode (cname = "CURLFORMcode", cprefix = "CURL_FORMADD_")]
+       public enum FormCode {
+               OK,
+               MEMORY,
+               OPTION_TWICE,
+               NULL,
+               UNKNOWN_OPTION,
+               INCOMPLETE,
+               ILLEGAL_ARRAY,
+               DISABLED
+       }
+       [CCode (sentinel = "CURLFORM_END")]
+       public Curl.FormCode formadd (ref Curl.HTTPPost httppost, ref unowned Curl.HTTPPost last_post, ...);
+       [CCode (cname = "curl_formget_callback")]
+       public delegate size_t FormgetCallback (void* arg, [CCode (array_size_type = "size_t")] char[] buf);
+       public int formget (Curl.HTTPPost form, void* arg, Curl.FormgetCallback append);
+       public unowned string version ();
+       public void free (void* p);
+       [Compact]
+       [CCode (cname = "struct curl_slist", cprefix = "curl_slist_", free_function = "curl_slist_free_all")]
+       public class SList {
+               public string data;
+               public Curl.SList next;
+               public static SList append (SList? slist, string data);
+       }
+       [CCode (cname = "CURLMcode", cprefix = "CURLM_")]
+       public enum MultiCode {
+               CALL_MULTI_PERFORM,
+               CALL_MULTI_SOCKET,
+               OK,
+               BAD_HANDLE,
+               BAD_EASY_HANDLE,
+               OUT_OF_MEMORY,
+               INTERNAL_ERROR,
+               BAD_SOCKET,
+               UNKNOWN_OPTION
+       }
+       [CCode (cname = "CURLMSG", cprefix = "CURLMSG_")]
+       public enum MessageType {
+               NONE,
+               DONE
+       }
+       [CCode (cname = "CURLMsg")]
+       public struct Message {
+               public Curl.MessageType msg;
+               public Curl.EasyHandle easy_handle;
+               [CCode (cname = "data.whatever")]
+               public void* whatever;
+               [CCode (cname = "data.result")]
+               public Curl.Code result;
+       }
+       [Compact]
+       [CCode (cname = "CURLM", cprefix = "curl_multi_", free_function = "curl_multi_cleanup")]
+       public class MultiHandle {
+               [CCode (cname = "curl_multi_init")]
+               public MultiHandle ();
+               public Curl.MultiCode add_handle (Curl.EasyHandle curl_handle);
+               public Curl.MultiCode remove_handle (Curl.EasyHandle curl_handle);
+               public Curl.MultiCode fdset (Posix.fd_set? read_fd_set, Posix.fd_set? write_fd_set, Posix.fd_set? exc_fd_set, out int max_fd);
+               public Curl.MultiCode perform (out int running_handles);
+               public unowned Curl.Message info_read (out int msgs_in_queue);
+               public unowned string strerror (Curl.MultiCode code);
+               public Curl.MultiCode socket_action (Curl.Socket s, int ev_bitmask, out int running_handles);
+               public Curl.MultiCode socket_all (out int running_handles);
+               public Curl.MultiCode timeout (out long milliseconds);
+               [Printf]
+               public Curl.MultiCode setopt (Curl.MultiOption option, ...);
+               public Curl.MultiCode assign (Curl.Socket sockfd, void* sockp);
+       }
+       [CCode (has_target = false)]
+       public delegate int TimerCallback (Curl.MultiHandle multi, long timeout_ms, void* userp);
+       [SimpleType]
+       [CCode (cname = "curl_socket_t")]
+       public struct Socket {
+       }
+       [SimpleType]
+       [CCode (cname = "curl_off_t")]
+       public struct Offset {
+       }
+       [CCode (cname = "CURL_SOCKET_BAD")]
+       public const Curl.Socket SOCKET_BAD;
+       [CCode (cname = "CURL_POLL_NONE")]
+       public const int POLL_NONE;
+       [CCode (cname = "CURL_POLL_IN")]
+       public const int POLL_IN;
+       [CCode (cname = "CURL_POLL_OUT")]
+       public const int POLL_OUT;
+       [CCode (cname = "CURL_POLL_INOUT")]
+       public const int POLL_INOUT;
+       [CCode (cname = "CURL_POLL_REMOVE")]
+       public const int POLL_REMOVE;
+       [CCode (cname = "CURL_SOCKET_TIMEOUT")]
+       public const int SOCKET_TIMEOUT;
+       [CCode (cname = "CURL_CSELECT_IN")]
+       public const int CSELECT_IN;
+       [CCode (cname = "CURL_CSELECT_OUT")]
+       public const int CSELECT_OUT;
+       [CCode (cname = "CURL_CSELECT_ERR")]
+       public const int CSELECT_ERR;
+       [CCode (cname = "CURLMoption")]
+       public enum MultiOption {
+               SOCKETFUNCTION,
+               SOCKETDATA,
+               PIPELINING,
+               TIMERFUNCTION,
+               TIMERDATA,
+               MAXCONNECTS
+       }
+       [Compact]
+       [CCode (cname = "struct curl_httppost", free_function = "curl_formfree")]
+       public class HTTPPost {
+               public Curl.HTTPPost next;
+               [CCode (array_length_cname = "namelength", array_length_type = "long")]
+               public weak char[] name;
+               [CCode (array_length_cname = "contentslength", array_length_type = "long")]
+               public weak char[] contents;
+               [CCode (array_length_cname = "bufferlength", array_length_type = "long")]
+               public weak char[] buffer;
+               public string contenttype;
+               public Curl.SList contentheader;
+               public Curl.HTTPPost more;
+               public long flags;
+               public string showfilename;
+               public void* userp;
+               [CCode (cname = "HTTPPOST_FILENAME")]
+               public const long FILENAME;
+               [CCode (cname = "HTTPPOST_READFILE")]
+               public const long READFILE;
+               [CCode (cname = "HTTPPOST_PTRCONTENTS")]
+               public const long PTRCONTENTS;
+               [CCode (cname = "HTTPPOST_BUFFER")]
+               public const long BUFFER;
+               [CCode (cname = "HTTPPOST_PTRBUFFER")]
+               public const long PTRBUFFER;
+               [CCode (cname = "HTTPPOST_CALLBACK")]
+               public const long CALLBACK;
+       }
+       [CCode (cname = "LIBCURL_COPYRIGHT")]
+       public const string COPYRIGHT;
+       [CCode (cname = "LIBCURL_VERSION")]
+       public const string VERSION;
+       [CCode (cname = "LIBCURL_VERSION_MAJOR")]
+       public const int VERSION_MAJOR;
+       [CCode (cname = "LIBCURL_VERSION_MINOR")]
+       public const int VERSION_MINOR;
+       [CCode (cname = "LIBCURL_VERSION_PATCH")]
+       public const int VERSION_PATCH;
+       [CCode (cname = "LIBCURL_VERSION_NUM")]
+       public const int VERSION_NUM;
+       [CCode (cname = "LIBCURL_TIMESTAMP")]
+       public const string TIMESTAMP;
+}