From 0413523d4c788bbf6b692062c6dcb2c663a00b00 Mon Sep 17 00:00:00 2001 From: guinux Date: Mon, 22 Feb 2016 13:47:40 +0100 Subject: [PATCH] add a custom download function --- src/Makefile | 3 +- src/daemon.vala | 251 +++++++++++++++++++++++++++-- vapi/libcurl.vapi | 401 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 638 insertions(+), 17 deletions(-) create mode 100644 vapi/libcurl.vapi diff --git a/src/Makefile b/src/Makefile index 82e9fda..79aecfa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,10 +61,11 @@ pamac-tray: ../vapi/libalpm.vapi $(COMMON_SOURCES) tray.vala $(COMMON_SOURCES) \ tray.vala -pamac-daemon: ../vapi/libalpm.vapi ../vapi/polkit-gobject-1.vapi $(COMMON_SOURCES) mirrors_config.vala daemon.vala +pamac-daemon: ../vapi/libalpm.vapi ../vapi/polkit-gobject-1.vapi ../vapi/libcurl.vapi $(COMMON_SOURCES) mirrors_config.vala daemon.vala valac -o pamac-daemon \ $(COMMON_VALA_FLAGS) \ --pkg=polkit-gobject-1 \ + --pkg=libcurl \ --thread \ $(COMMON_SOURCES) \ mirrors_config.vala \ diff --git a/src/daemon.vala b/src/daemon.vala index 66ed321..314a3ca 100644 --- a/src/daemon.vala +++ b/src/daemon.vala @@ -1,7 +1,7 @@ /* * pamac-vala * - * Copyright (C) 2014-2015 Guillaume Benoit + * Copyright (C) 2014-2016 Guillaume Benoit * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,6 +52,8 @@ namespace Pamac { private GLib.File lockfile; private ErrorInfos current_error; public Timer timer; + public Cancellable cancellable; + public Curl.Easy curl; public signal void emit_event (uint primary_event, uint secondary_event, string[] details); public signal void emit_providers (string depend, string[] providers); @@ -83,6 +85,12 @@ namespace Pamac { refresh_handle (); Timeout.add (500, check_pacman_running); create_thread_pool (); + cancellable = new Cancellable (); + Curl.global_init (Curl.GLOBAL_SSL); + } + + ~Daemon () { + Curl.global_cleanup (); } public void set_environment_variables (HashTable variables) { @@ -134,7 +142,7 @@ namespace Pamac { alpm_config.handle.eventcb = (Alpm.EventCallBack) cb_event; alpm_config.handle.progresscb = (Alpm.ProgressCallBack) cb_progress; alpm_config.handle.questioncb = (Alpm.QuestionCallBack) cb_question; - alpm_config.handle.dlcb = (Alpm.DownloadCallBack) cb_download; + alpm_config.handle.fetchcb = (Alpm.FetchCallBack) cb_fetch; alpm_config.handle.totaldlcb = (Alpm.TotalDownloadCallBack) cb_totaldownload; alpm_config.handle.logcb = (Alpm.LogCallBack) cb_log; lockfile = GLib.File.new_for_path (alpm_config.handle.lockfile); @@ -323,7 +331,14 @@ namespace Pamac { current_error = ErrorInfos (); int force = (force_refresh) ? 1 : 0; uint success = 0; + cancellable.reset (); foreach (var db in alpm_config.handle.syncdbs) { + if (cancellable.is_cancelled ()) { + refresh_handle (); + refresh_finished (false); + intern_lock = false; + return; + } if (db.update (force) >= 0) { success++; } @@ -448,6 +463,7 @@ namespace Pamac { public bool trans_init (Alpm.TransFlag transflags) { current_error = ErrorInfos (); + cancellable.reset (); if (alpm_config.handle.trans_init (transflags) == -1) { current_error.message = _("Failed to init transaction"); current_error.details = { Alpm.strerror (alpm_config.handle.errno ()) }; @@ -700,6 +716,13 @@ namespace Pamac { Alpm.List err_data; if (alpm_config.handle.trans_commit (out err_data) == -1) { Alpm.Errno errno = alpm_config.handle.errno (); + // cancel the download return an EXTERNAL_DOWNLOAD error + if (errno == Alpm.Errno.EXTERNAL_DOWNLOAD && cancellable.is_cancelled ()) { + trans_release (); + refresh_handle (); + trans_commit_finished (false); + return; + } current_error.message = _("Failed to commit transaction"); string detail = Alpm.strerror (errno); string[] details = {}; @@ -781,12 +804,7 @@ namespace Pamac { // it will end the normal way return; } - Posix.raise (Posix.SIGINT); - current_error = ErrorInfos (); - trans_commit_finished (false); - alpm_config.handle.unlock (); - intern_lock = false; - refresh_handle (); + cancellable.cancel (); } [DBus (no_reply = true)] @@ -994,19 +1012,216 @@ private void cb_progress (Alpm.Progress progress, string pkgname, int percent, u } } -private void cb_download (string filename, uint64 xfered, uint64 total) { - if (xfered == 0) { - pamac_daemon.emit_download (filename, xfered, total); +private uint64 prevprogress; + +private int cb_download (void* data, uint64 dltotal, uint64 dlnow, uint64 ultotal, uint64 ulnow) { + + if (unlikely (pamac_daemon.cancellable.is_cancelled ())) { + return 1; + } + + string filename = (string) data; + + if (unlikely (dltotal == 0 || prevprogress == dltotal)) { + return 0; + } else if (unlikely (dlnow == 0)) { + pamac_daemon.emit_download (filename, dlnow, dltotal); pamac_daemon.timer.start (); - } else if (xfered == total) { - pamac_daemon.emit_download (filename, xfered, total); + } else if (unlikely (dlnow == dltotal)) { + pamac_daemon.emit_download (filename, dlnow, dltotal); pamac_daemon.timer.stop (); - } else if (pamac_daemon.timer.elapsed () < 0.5) { - return; + } else if (likely (pamac_daemon.timer.elapsed () < 0.5)) { + return 0; } else { - pamac_daemon.emit_download (filename, xfered, total); + pamac_daemon.emit_download (filename, dlnow, dltotal); pamac_daemon.timer.start (); } + +//~ // avoid displaying progress for redirects with a body +//~ if (respcode >= 300) { +//~ return 0; +//~ } + + prevprogress = dlnow; + + return 0; +} + +private int cb_fetch (string fileurl, string localpath, int force) { + if (pamac_daemon.cancellable.is_cancelled ()) { + return -1; + } + + if (pamac_daemon.curl == null) { + pamac_daemon.curl = new Curl.Easy (); + } + + char error_buffer[Curl.ERROR_SIZE]; + var url = GLib.File.new_for_uri (fileurl); + var destfile = GLib.File.new_for_path (localpath + url.get_basename ()); + var tempfile = GLib.File.new_for_path (destfile.get_path () + ".part"); + + pamac_daemon.curl.reset (); + pamac_daemon.curl.setopt (Curl.Option.URL, fileurl); + pamac_daemon.curl.setopt (Curl.Option.FAILONERROR, 1L); + pamac_daemon.curl.setopt (Curl.Option.ERRORBUFFER, error_buffer); + pamac_daemon.curl.setopt (Curl.Option.CONNECTTIMEOUT, 30L); + pamac_daemon.curl.setopt (Curl.Option.FILETIME, 1L); + pamac_daemon.curl.setopt (Curl.Option.NOPROGRESS, 0L); + pamac_daemon.curl.setopt (Curl.Option.FOLLOWLOCATION, 1L); + pamac_daemon.curl.setopt (Curl.Option.XFERINFOFUNCTION, cb_download); + pamac_daemon.curl.setopt (Curl.Option.XFERINFODATA, (void*) url.get_basename ()); + pamac_daemon.curl.setopt (Curl.Option.LOW_SPEED_LIMIT, 1L); + pamac_daemon.curl.setopt (Curl.Option.LOW_SPEED_TIME, 30L); + pamac_daemon.curl.setopt (Curl.Option.NETRC, Curl.NetRCOption.OPTIONAL); + pamac_daemon.curl.setopt (Curl.Option.HTTPAUTH, Curl.CURLAUTH_ANY); + + bool remove_partial_download = true; + if (fileurl.contains (".pkg.tar.") && !fileurl.has_suffix (".sig")) { + remove_partial_download = false; + } + + string open_mode = "wb"; + prevprogress = 0; + + try { + if (force == 0) { + if (destfile.query_exists ()) { + // start from scratch only download if our local is out of date. + pamac_daemon.curl.setopt (Curl.Option.TIMECONDITION, Curl.TimeCond.IFMODSINCE); + FileInfo info = destfile.query_info ("time::modified", 0); + TimeVal time = info.get_modification_time (); + pamac_daemon.curl.setopt (Curl.Option.TIMEVALUE, time.tv_sec); + } else if (tempfile.query_exists ()) { + // a previous partial download exists, resume from end of file. + FileInfo info = tempfile.query_info ("standard::size", 0); + int64 size = info.get_size (); + pamac_daemon.curl.setopt (Curl.Option.RESUME_FROM_LARGE, size); + open_mode = "ab"; + } + } else { + if (tempfile.query_exists ()) { + tempfile.delete (); + } + } + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + + Posix.FILE localf = Posix.FILE.open (tempfile.get_path (), open_mode); + if (localf == null) { + stdout.printf ("could not open file %s\n", tempfile.get_path ()); + return -1; + } + + pamac_daemon.curl.setopt (Curl.Option.WRITEDATA, localf); + + // perform transfer + Curl.Code err = pamac_daemon.curl.perform (); + + + // disconnect relationships from the curl handle for things that might go out + // of scope, but could still be touched on connection teardown. This really + // only applies to FTP transfers. + pamac_daemon.curl.setopt (Curl.Option.NOPROGRESS, 1L); + pamac_daemon.curl.setopt (Curl.Option.ERRORBUFFER, null); + + int ret; + + // was it a success? + switch (err) { + case Curl.Code.OK: + long timecond, remote_time = -1; + double remote_size, bytes_dl; + unowned string effective_url; + + // retrieve info about the state of the transfer + pamac_daemon.curl.getinfo (Curl.Info.FILETIME, out remote_time); + pamac_daemon.curl.getinfo (Curl.Info.CONTENT_LENGTH_DOWNLOAD, out remote_size); + pamac_daemon.curl.getinfo (Curl.Info.SIZE_DOWNLOAD, out bytes_dl); + pamac_daemon.curl.getinfo (Curl.Info.CONDITION_UNMET, out timecond); + pamac_daemon.curl.getinfo (Curl.Info.EFFECTIVE_URL, out effective_url); + + if (timecond == 1 && bytes_dl == 0) { + // time condition was met and we didn't download anything. we need to + // clean up the 0 byte .part file that's left behind. + try { + if (tempfile.query_exists ()) { + tempfile.delete (); + } + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + ret = 1; + } + // remote_size isn't necessarily the full size of the file, just what the + // server reported as remaining to download. compare it to what curl reported + // as actually being transferred during curl_easy_perform () + else if (remote_size != -1 && bytes_dl != -1 && bytes_dl != remote_size) { + pamac_daemon.emit_log ((uint) Alpm.LogLevel.ERROR, + dgettext ("libalpm", "%s appears to be truncated: %jd/%jd bytes\n").printf ( + fileurl, bytes_dl, remote_size)); + if (remove_partial_download) { + try { + if (tempfile.query_exists ()) { + tempfile.delete (); + } + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + } + ret = -1; + } else { + try { + tempfile.move (destfile, FileCopyFlags.OVERWRITE); + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + ret = 0; + } + break; + case Curl.Code.ABORTED_BY_CALLBACK: + if (remove_partial_download) { + try { + if (tempfile.query_exists ()) { + tempfile.delete (); + } + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + } + ret = -1; + break; + default: + // other cases are errors + try { + if (tempfile.query_exists ()) { + if (remove_partial_download) { + tempfile.delete (); + } else { + // delete zero length downloads + FileInfo info = tempfile.query_info ("standard::size", 0); + int64 size = info.get_size (); + if (size == 0) { + tempfile.delete (); + } + } + } + } catch (GLib.Error e) { + stderr.printf ("Error: %s\n", e.message); + } + // do not report error for missing sig with db + if (!fileurl.has_suffix ("db.sig")) { + string hostname = url.get_uri ().split("/")[2]; + pamac_daemon.emit_log ((uint) Alpm.LogLevel.ERROR, + dgettext ("libalpm", "failed retrieving file '%s' from %s : %s\n").printf ( + url.get_basename (), hostname, error_buffer)); + } + ret = -1; + break; + } + + return ret; } private void cb_totaldownload (uint64 total) { @@ -1014,6 +1229,10 @@ private void cb_totaldownload (uint64 total) { } private void cb_log (Alpm.LogLevel level, string fmt, va_list args) { + // do not log errors when download is cancelled + if (pamac_daemon.cancellable.is_cancelled ()) { + return; + } Alpm.LogLevel logmask = Alpm.LogLevel.ERROR | Alpm.LogLevel.WARNING; if ((level & logmask) == 0) { return; diff --git a/vapi/libcurl.vapi b/vapi/libcurl.vapi new file mode 100644 index 0000000..c27879a --- /dev/null +++ b/vapi/libcurl.vapi @@ -0,0 +1,401 @@ +/* + * licurl-vala + * Vala bindings for libcurl + * + * Copyright (C) 2016 Guillaume Benoit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a get of the GNU General Public License + * along with this program. If not, see . + */ + + +[CCode (cheader_filename = "curl/curl.h")] +namespace Curl { + [CCode (cprefix = "curl_")] + public static Curl.Code global_init (long flags); + public static void global_cleanup (); + public static unowned string version (); + + [CCode (cname = "CURL", cprefix = "curl_easy_", free_function = "curl_easy_cleanup")] + [Compact] + public class Easy { + [CCode (cname = "curl_easy_init")] + public Easy(); + public void cleanup (); + [PrintfFormat] + public Curl.Code getinfo (Curl.Info info, ...); + public Curl.Code easy_pause (int bitmask); + public Curl.Code perform (); + public void reset (); + [PrintfFormat] + public Curl.Code setopt (Curl.Option option, ...); + public static unowned string strerror (Curl.Code errornum); + } + + [CCode (cname = "CURLcode", cprefix = "CURLE_", has_type_id = false)] + public enum Code { + OK, + UNSUPPORTED_PROTOCOL, + FAILED_INIT, + URL_MALFORMAT, + NOT_BUILT_IN, + 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_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, + FTP_PRET_FAILED, + RTSP_CSEQ_ERROR, + RTSP_SESSION_ERROR, + FTP_BAD_FILE_LIST, + CHUNK_FAILED + } + + [CCode (cname = "CURLINFO", cprefix = "CURLINFO_", has_type_id = false)] + public enum Info { + NONE, + 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, + RTSP_SESSION_ID, + RTSP_CLIENT_CSEQ, + RTSP_SERVER_CSEQ, + RTSP_CSEQ_RECV, + PRIMARY_PORT, + LOCAL_IP, + LOCAL_PORT, + LASTONE + } + + [CCode (cname = "CURL_NETRC_OPTION", cprefix = "CURL_NETRC_", has_type_id = false)] + public enum NetRCOption { + IGNORED, + OPTIONAL, + REQUIRED, + LAST + } + + [CCode (cname = "CURLoption", cprefix = "CURLOPT_", has_type_id = false)] + public enum Option { + FILE, + WRITEDATA, + URL, + PORT, + PROXY, + USERPWD, + PROXYUSERPWD, + RANGE, + INFILE, + READDATA, + ERRORBUFFER, + WRITEFUNCTION, + READFUNCTION, + TIMEOUT, + INFILESIZE, + POSTFIELDS, + REFERER, + FTPPORT, + USERAGENT, + LOW_SPEED_LIMIT, + LOW_SPEED_TIME, + RESUME_FROM, + COOKIE, + HTTPHEADER, + RTSPHEADER, + 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, + XFERINFOFUNCTION, + XFERINFODATA, + AUTOREFERER, + PROXYPORT, + POSTFIELDSIZE, + HTTPPROXYTUNNEL, + INTERFACE, + KRBLEVEL, + SSL_VERIFYPEER, + CAINFO, + MAXREDIRS, + FILETIME, + TELNETOPTIONS, + MAXCONNECTS, + 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_CACHE_TIMEOUT, + PREQUOTE, + DEBUGFUNCTION, + DEBUGDATA, + COOKIESESSION, + CAPATH, + BUFFERSIZE, + NOSIGNAL, + SHARE, + PROXYTYPE, + ACCEPT_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, + MAIL_FROM, + MAIL_RCPT, + FTP_USE_PRET, + RTSP_REQUEST, + RTSP_SESSION_ID, + RTSP_STREAM_URI, + RTSP_TRANSPORT, + RTSP_CLIENT_CSEQ, + RTSP_SERVER_CSEQ, + INTERLEAVEDATA, + INTERLEAVEFUNCTION, + WILDCARDMATCH, + CHUNK_BGN_FUNCTION, + CHUNK_END_FUNCTION, + FNMATCH_FUNCTION, + CHUNK_DATA, + FNMATCH_DATA, + RESOLVE, + TLSAUTH_USERNAME, + TLSAUTH_PASSWORD, + TLSAUTH_TYPE, + TRANSFER_ENCODING, + CLOSESOCKETFUNCTION, + CLOSESOCKETDATA, + LASTENTRY + } + + [CCode (cname = "curl_TimeCond", cprefix = "CURL_TIMECOND_", has_type_id = false)] + public enum TimeCond { + NONE, + IFMODSINCE, + IFUNMODSINCE, + LASTMOD, + LAST + } + + [CCode (cname = "curl_progress_callback", has_target = false)] + public delegate int ProgressCallback (void* user_data, uint64 dltotal, uint64 dlnow, uint64 ultotal, uint64 ulnow); + + [CCode (cname = "CURLAUTH_ANY", cheader_filename = "curl/curl.h")] + public const int CURLAUTH_ANY; + + [CCode (cname = "CURL_ERROR_SIZE")] + public const int ERROR_SIZE; + + [CCode (cname = "CURL_GLOBAL_SSL")] + public const int GLOBAL_SSL; + +}