cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: [PATCH] Support for OAuth 2.0 two-legged authorization and the HTTP MAC Internet-Draft

From: Yves Arrouye <yarrouye_at_expedia.com>
Date: Thu, 31 Jan 2013 16:52:07 -0800

Is there no interest in this? If so, is it because of the domain (OAuth 2.0 tokens) or the structure of the patch, or its size, or…? I'm curious. We'd love to be contribute this back to the community.

YA

–––
Learn about GPT services and architectures on Confluence.<http://confluence/display/GPT/GPT+Architecture>

From: Yves Arrouye <yarrouye_at_expedia.com<mailto:yarrouye_at_expedia.com>>
Reply-To: libcurl development <curl-library_at_cool.haxx.se<mailto:curl-library_at_cool.haxx.se>>
Date: Wednesday, January 23, 2013 4:11 PM
To: libcurl development <curl-library_at_cool.haxx.se<mailto:curl-library_at_cool.haxx.se>>
Subject: [PATCH] Support for OAuth 2.0 two-legged authorization and the HTTP MAC Internet-Draft

Please find below a patch to add support for two-legged authorization in OAuth 2.0 (RFC 6749). This means that it generates a proper Authorization header given an OAuth 2.0 token). The patch supports bearer tokens (RFC 6750) as well as HTTP MAC tokens (draft-ietf-oauth-v2-http-mac-02). My company, Expedia, uses OAuth 2.0 internally and I am using this modified curl multiple times daily. We have built it on OS X 10.8, Ubuntu Linux and Windows (both with nmake and MingW32).

Support for OAuth 2.0 and bearer token is enabled by default; support for HTTP MAC is also enabled by default but can be controlled separately since it is an I-D (so the default could be made to not support it for now, though that is the only secure token type for OAuth 2.0 now). This is true both for the configure-based and the Windows build. On Windows, one needs to use OpenSSL (for access to SHA1 and SHA256) if using HTTP MAC.

I picked JSONSL as my JSON library but the patch doesn't require a JSON library in order to enable OAuth 2.0. It will gladly read tokens encoded in application/x-www-form-urlencoded format.

The patch covers code for the library, the curl executable, proper support for the —libcurl option and documentation (README and manual page---Obviously, I couldn't add a "Added in version X.Y.Z" comment there).

I would love feedback on choices I've made, on whether they mesh well with cURL's philosophy, etc. and will do the work to amend the changes as needed.

The code is also on Expedia Inc.'s Github fork of curl: https://github.com/ExpediaInc/curl. Eric, I can send a pull request rather than a patch (or in addition to it).

YA

I sent an email previously with some data about the patch structure. Here it is again:

- oauth2.[hc] defines a structure to hold the known OAuth 2 token types
(bearer and MAC) as well as functions to parse tokens. There is a built in
parser for a token in text/www-url-encoded form, and if configure finds
jsonsl (or if it's under deps on Windows and USE_JSONSL) then tokens in
application/json can also be parsed. The goal is to be able to make a cURL
request to a token endpoint and reuse the response in another cURL call. I
picked jsonsl after looking for a VERY brief time for a simple C json
tokenizer that I could understand in ten minutes or so, but any could

- http_oauth2.[hc] provides for generating the Authorization header. It is
inspired by http_digest.[hc].

- I've added a setopt that takes a pointer to a struct curl_oauth2_token
to indicate that one wants to use OAuth 2.0. I picked the struct rather than
a string to parse so that one could build a token repository wherever one
wants and pass the tokens as needed. It should be easy to expose that
struct to language bindings such as PHP, Python, etc, though I have not
done so. This is a place where I'd need guidance on whether I did right
per cURL's spirit or not.

- When picking auth, libcurl will pick OAuth 2 as an option. It's ranked
above digest and basic but maybe that could be made dependent on the
security of the token too (e.g. Bearer is lowest, but MAC is highest). I
am not sure.

- tool_getparam.c, tool_operate.c, tool_help.c and their friends etc. have
been modified. In the tool, the configuration object takes a string, and
it is then parsed and if successfully parsed it is used in a call to
setopt. I wrote code that either takes a filename (including "-") or, with
a #ifdef, takes the token itself or an @filename like seen in other places
of cURL. I have some reservation about passing the token data (including
the secret) in the clear so that's off for now! A more paranoid
implementation may also balk at the user if the file is group and/or
world-readable, but I don't really like nannying people that way.

- m4/curl-confopts.m4 deals with the --enable-oauth2 option (default is
no) and also looks for JSONSL. In configure.ac I call the new macro and
report on whether OAuth 2 is enabled as well as which token encodings are
supported.

- Similar changes have been made to the winbuild Makefiles, and options
added there are documented in the README.

- docs/curl.1 is updated to document the --oauth2 <file> option as well as
the companion --http-mac-ext <string> (which is how I provide for the
"ext" parameter for HTTP MAC).

That's it in a nutshell.

===== PATCH IS BELOW ==========
diff --git a/configure.ac b/configure.ac
index 569804b..d93e08a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,6 +165,8 @@ curl_verbose_msg="enabled (--disable-verbose)"
    curl_rtsp_msg="no (--enable-rtsp)"
    curl_rtmp_msg="no (--with-librtmp)"
   curl_mtlnk_msg="no (--with-libmetalink)"
+ curl_oauth2_msg="no (--enable-oauth2)"
+curl_httpmac_msg="no (--enable-httpmac)"

     init_ssl_msg=${curl_ssl_msg}

@@ -638,6 +640,8 @@ AC_HELP_STRING([--disable-gopher],[Disable Gopher support]),
        AC_MSG_RESULT(yes)
 )

+CURL_CHECK_OPTION_OAUTH2 dnl check whether to support OAuth 2.0
+CURL_CHECK_OPTION_HTTPMAC dnl check whether to support HTTP MAC

 dnl **********************************************************************
 dnl Check for built-in manual
@@ -3167,6 +3171,18 @@ fi
 dnl set variable for use in automakefile(s)
 AM_CONDITIONAL(USE_MANUAL, test x"$USE_MANUAL" = x1)

+CURL_CHECK_OAUTH2
+if test "x$want_oauth2" = "xyes"; then
+ curl_oauth2_msg="enabled (tokens as application/x-www-form-urlencoded"
+ if test "x$has_json" = "xyes"; then
+ curl_oauth2_msg="$curl_oauth2_msg, application/json"
+ fi
+ curl_oauth2_msg="$curl_oauth2_msg)"
+fi
+if test "x$want_httpmac" = "xyes"; then
+ curl_httpmac_msg="enabled"
+fi
+
 CURL_CHECK_LIB_ARES
 AM_CONDITIONAL(USE_EMBEDDED_ARES, test x$embedded_ares = xyes)

@@ -3547,33 +3563,35 @@ CURL_GENERATE_CONFIGUREHELP_PM

 AC_MSG_NOTICE([Configured to build curl/libcurl:

- curl version: ${CURLVERSION}
- Host setup: ${host}
- Install prefix: ${prefix}
- Compiler: ${CC}
- SSL support: ${curl_ssl_msg}
- SSH support: ${curl_ssh_msg}
- zlib support: ${curl_zlib_msg}
- krb4 support: ${curl_krb4_msg}
- GSSAPI support: ${curl_gss_msg}
- SPNEGO support: ${curl_spnego_msg}
- TLS-SRP support: ${curl_tls_srp_msg}
- resolver: ${curl_res_msg}
- ipv6 support: ${curl_ipv6_msg}
- IDN support: ${curl_idn_msg}
- Build libcurl: Shared=${enable_shared}, Static=${enable_static}
- Built-in manual: ${curl_manual_msg}
- --libcurl option: ${curl_libcurl_msg}
- Verbose errors: ${curl_verbose_msg}
- SSPI support: ${curl_sspi_msg}
- ca cert bundle: ${ca}
- ca cert path: ${capath}
- LDAP support: ${curl_ldap_msg}
- LDAPS support: ${curl_ldaps_msg}
- RTSP support: ${curl_rtsp_msg}
- RTMP support: ${curl_rtmp_msg}
- metalink support: ${curl_mtlnk_msg}
- Protocols: ${SUPPORT_PROTOCOLS}
+ curl version: ${CURLVERSION}
+ Host setup: ${host}
+ Install prefix: ${prefix}
+ Compiler: ${CC}
+ SSL support: ${curl_ssl_msg}
+ SSH support: ${curl_ssh_msg}
+ zlib support: ${curl_zlib_msg}
+ krb4 support: ${curl_krb4_msg}
+ GSSAPI support: ${curl_gss_msg}
+ SPNEGO support: ${curl_spnego_msg}
+ TLS-SRP support: ${curl_tls_srp_msg}
+ resolver: ${curl_res_msg}
+ ipv6 support: ${curl_ipv6_msg}
+ IDN support: ${curl_idn_msg}
+ Build libcurl: Shared=${enable_shared}, Static=${enable_static}
+ Built-in manual: ${curl_manual_msg}
+ --libcurl option: ${curl_libcurl_msg}
+ Verbose errors: ${curl_verbose_msg}
+ SSPI support: ${curl_sspi_msg}
+ ca cert bundle: ${ca}
+ ca cert path: ${capath}
+ LDAP support: ${curl_ldap_msg}
+ LDAPS support: ${curl_ldaps_msg}
+ RTSP support: ${curl_rtsp_msg}
+ RTMP support: ${curl_rtmp_msg}
+ metalink support: ${curl_mtlnk_msg}
+ OAuth 2.0 support: ${curl_oauth2_msg}
+ HTTP MAC support: ${curl_httpmac_msg}
+ Protocols: ${SUPPORT_PROTOCOLS}
 ])

 if test "x$soname_bump" = "xyes"; then
diff --git a/docs/curl.1 b/docs/curl.1
index c2b6887..db9b019 100644
--- a/docs/curl.1
+++ b/docs/curl.1
@@ -149,7 +149,7 @@ most secure one the remote site claims to support. This is done by first
 doing a request and checking the response-headers, thus possibly inducing an
 extra network round-trip. This is used instead of setting a specific
 authentication method, which you can do with \fI--basic\fP, \fI--digest\fP,
-\fI--ntlm\fP, and \fI--negotiate\fP.
+\fI--ntlm\fP, \fI--negotiate\fP, and \fI--oauth2\fP.

 Note that using --anyauth is not recommended if you do uploads from stdin,
 since it may require data to be sent twice and then the client must be able to
@@ -620,6 +620,12 @@ This option can be used multiple times to add/replace/remove multiple headers.
 (SCP/SFTP) Pass a string containing 32 hexadecimal digits. The string should
 be the 128 bit MD5 checksum of the remote host's public key, curl will refuse
 the connection with the host unless the md5sums match. (Added in 7.17.1)
+.IP "--http-mac-ext <string>"
+(HTTP) When using OAuth 2.0 authentication with an HTTP MAC token, provide
+this extension string as part of the normalized request to produce a MAC
+for.
+
+If this option is used several times, the last one will be used.
 .IP "--ignore-content-length"
 (HTTP)
 Ignore the Content-Length header. This is particularly useful for servers
@@ -961,6 +967,12 @@ This option requires a library built with SSL support. Use
 \fI-V, --version\fP to see if your curl supports NTLM.

 If this option is used several times, only the first one is used.
+.IP "--oauth2 <file>"
+(HTTP) Authenticate using the OAuth 2.0 token in the given <file>. The token
+can be provided either in application/json or in text/www-url-encoded formats.
+See also \fI--http-mac-ext\fP for HTTP MAC tokens.
+
+If this option is used several times, the last one will be used.
 .IP "-o, --output <file>"
 Write output to <file> instead of stdout. If you are using {} or [] to fetch
 multiple documents, you can use '#' followed by a number in the <file>
diff --git a/include/curl/Makefile.am b/include/curl/Makefile.am
index 86e8b78..1cab4e4 100644
--- a/include/curl/Makefile.am
+++ b/include/curl/Makefile.am
@@ -21,7 +21,7 @@
 ###########################################################################
 pkginclude_HEADERS = \
  curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \
- typecheck-gcc.h curlbuild.h curlrules.h
+ typecheck-gcc.h curlbuild.h curlrules.h oauth2.h

 pkgincludedir= $(includedir)/curl

diff --git a/include/curl/curl.h b/include/curl/curl.h
index 5b39a24..ea98b62 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -515,6 +515,12 @@ typedef enum {
   CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */
   CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
   CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
+ CURLE_OAUTH2_TOKEN_MALFORMAT, /* 89 - invalid format for OAuth 2 token */
+ CURLE_OAUTH2_TOKEN_TYPE_UNSUPPORTED, /* 90 - unknwon/unsupported OAuth 2
+ token type */
+ CURLE_HTTP_MAC_ALGO_UNSUPPORTED, /* 91 - unknown/unsupported HTTP MAC algo */
+ CURLE_HTTP_MAC_INVALID_HOST, /* 92 - invalid Host: header when using
+ HTTP MAC */
   CURL_LAST /* never use! */
 } CURLcode;

@@ -609,6 +615,7 @@ typedef enum {
  * CURLAUTH_NTLM - HTTP NTLM authentication
  * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour
  * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper
+ * CURLAUTH_MAC - HTTP MAC authentication
  * CURLAUTH_ONLY - Use together with a single other type to force no
  * authentication or just that single type
  * CURLAUTH_ANY - All fine types set
@@ -622,6 +629,7 @@ typedef enum {
 #define CURLAUTH_NTLM (((unsigned long)1)<<3)
 #define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4)
 #define CURLAUTH_NTLM_WB (((unsigned long)1)<<5)
+#define CURLAUTH_OAUTH2 (((unsigned long)1)<<6)
 #define CURLAUTH_ONLY (((unsigned long)1)<<31)
 #define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE)
 #define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))
@@ -1536,6 +1544,10 @@ typedef enum {
   /* set the SMTP auth originator */
   CINIT(MAIL_AUTH, OBJECTPOINT, 217),

+ /* OAuth 2 token to use. */
+ CINIT(OAUTH2_TOKEN, OBJECTPOINT, 218),
+ CINIT(HTTP_MAC_EXT, OBJECTPOINT, 219),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;

@@ -2211,6 +2223,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
   stuff before they can be included! */
 #include "easy.h" /* nothing in curl is fun without the easy stuff */
 #include "multi.h"
+#include "oauth2.h" /* support for OAuth 2 */

 /* the typechecker doesn't work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
diff --git a/include/curl/oauth2.h b/include/curl/oauth2.h
new file mode 100644
index 0000000..40447b3
--- /dev/null
+++ b/include/curl/oauth2.h
@@ -0,0 +1,72 @@
+#ifndef HEADER_CURL_OAUTH2_H
+#define HEADER_CURL_OAUTH2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel_at_haxx.se<mailto:daniel_at_haxx.se>>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum curl_oauth2_token_type {
+ CURL_OAUTH2_TOKEN_TYPE_INVALID = 0,
+ CURL_OAUTH2_TOKEN_TYPE_BEARER,
+ CURL_OAUTH2_TOKEN_TYPE_MAC
+};
+
+enum curl_oauth2_mac_algo {
+ CURL_OAUTH2_MAC_ALGO_INVALID = 0,
+ CURL_OAUTH2_MAC_ALGO_HMAC_SHA1,
+ CURL_OAUTH2_MAC_ALGO_HMAC_SHA256
+};
+
+struct curl_oauth2_token {
+ enum curl_oauth2_token_type token_type;
+ const char *access_token;
+ /* we do not use an union so our parser does not have to worry about
+ the order of attributes in the token hash */
+ struct curl_oauth2_mac_token {
+ const char *mac_key;
+ enum curl_oauth2_mac_algo mac_algo;
+ } mac_token;
+};
+
+/*
+ * NAME curl_parse_oauth2_token()
+ *
+ * DESCRIPTION
+ *
+ * parses a string as an OAuth 2 token
+ */
+CURL_EXTERN CURLcode curl_parse_oauth2_token(const char *tokbuf,
+ size_t tokbufsz,
+ struct curl_oauth2_token *token);
+/* this is to parse a token file */
+CURL_EXTERN CURLcode curl_parse_oauth2_token_file(const char *fname,
+ struct curl_oauth2_token *token);
+
+CURL_EXTERN void curl_free_oauth2_token(struct curl_oauth2_token *token);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_CURL_OAUTH2_H */
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index f8917e8..a91c9d3 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -102,6 +102,9 @@ __extension__ ({ \
     if((_curl_opt) == CURLOPT_SHARE) \
       if(!_curl_is_ptr((value), CURLSH)) \
         _curl_easy_setopt_err_CURLSH(); \
+ if((_curl_opt) == CURLOPT_OAUTH2_TOKEN) \
+ if(!_curl_is_ptr((value), struct CURL_OAuth2_token)) \
+ _curl_easy_setopt_err_curl_oauth2token(); \
   } \
   curl_easy_setopt(handle, _curl_opt, value); \
 })
@@ -191,6 +194,9 @@ _CURL_WARNING(_curl_easy_setopt_err_curl_slist,
   "curl_easy_setopt expects a struct curl_slist* argument for this option")
 _CURL_WARNING(_curl_easy_setopt_err_CURLSH,
   "curl_easy_setopt expects a CURLSH* argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_oauth2token,
+ "curl_easy_setopt expects a "
+ "struct CURL_OAuth2_token* argument for this option")

 _CURL_WARNING(_curl_easy_getinfo_err_string,
   "curl_easy_getinfo expects a pointer to char * for this info")
@@ -264,6 +270,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
    (option) == CURLOPT_RTSP_SESSION_ID || \
    (option) == CURLOPT_RTSP_STREAM_URI || \
    (option) == CURLOPT_RTSP_TRANSPORT || \
+ (option) == CURLOPT_HTTP_MAC_EXT || \
    0)

 /* evaluates to true if option takes a curl_write_callback argument */
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index e970edd..797554a 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -25,7 +25,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c\
   asyn-ares.c asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c\
   curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_schannel.c \
   curl_multibyte.c curl_darwinssl.c hostcheck.c \
- bundles.c conncache.c
+ bundles.c conncache.c oauth2.c http_oauth2.c

 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h\
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h\
@@ -43,4 +43,5 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h\
   gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h\
   curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h curl_ntlm_msgs.h\
   curl_sasl.h curl_schannel.h curl_multibyte.h curl_darwinssl.h\
- hostcheck.h bundles.h conncache.h curl_setup_once.h multihandle.h
+ hostcheck.h bundles.h conncache.h curl_setup_once.h multihandle.h \
+ http_oauth2.h
diff --git a/lib/config-win32.h b/lib/config-win32.h
index cfbca9c..1918c06 100644
--- a/lib/config-win32.h
+++ b/lib/config-win32.h
@@ -667,6 +667,14 @@
 /* If you want to build curl with the built-in manual */
 #define USE_MANUAL 1

+#ifdef DISABLE_OAUTH2
+# define CURL_DISABLE_OAUTH2 1
+#endif
+
+#ifdef DISABLE_HTTPMAC
+# define CURL_DISABLE_HTTPMAC 1
+#endif
+
 #if defined(__POCC__) || (USE_IPV6)
 # define ENABLE_IPV6 1
 #endif
diff --git a/lib/config-win32ce.h b/lib/config-win32ce.h
index a8ab0d3..b879836 100644
--- a/lib/config-win32ce.h
+++ b/lib/config-win32ce.h
@@ -423,6 +423,14 @@
 /* Name of package */
 #define PACKAGE "curl"

+#ifdef DISABLE_OAUTH2
+# define CURL_DISABLE_OAUTH2 1
+#endif
+
+#ifdef DISABLE_HTTPMAC
+# define CURL_DISABLE_HTTPMAC 1
+#endif
+
 /* ---------------------------------------------------------------- */
 /* WinCE */
 /* ---------------------------------------------------------------- */
diff --git a/lib/escape.c b/lib/escape.c
index 6a26cf8..d07fdf5 100644
--- a/lib/escape.c
+++ b/lib/escape.c
@@ -133,7 +133,9 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
 }

 /*
- * Curl_urldecode() URL decodes the given string.
+ * Curl_urlfmtdecode() URL decodes the given string.
+ *
+ * Optionally decodes x-www-form-urlencoded instead of www-urlencoded.
  *
  * Optionally detects control characters (byte codes lower than 32) in the
  * data and rejects such data.
@@ -142,10 +144,10 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
  * *olen. If length == 0, the length is assumed to be strlen(string).
  *
  */
-CURLcode Curl_urldecode(struct SessionHandle *data,
+CURLcode Curl_urlfmtdecode(struct SessionHandle *data,
                         const char *string, size_t length,
                         char **ostring, size_t *olen,
- bool reject_ctrl)
+ bool decode_form, bool reject_ctrl)
 {
   size_t alloc = (length?length:strlen(string))+1;
   char *ns = malloc(alloc);
@@ -181,7 +183,10 @@ CURLcode Curl_urldecode(struct SessionHandle *data,
       string+=2;
       alloc-=2;
     }
- if(reject_ctrl && (in < 0x20)) {
+ else if(in == '+' && decode_form) {
+ in = ' ';
+ }
+ else if(reject_ctrl && (in < 0x20)) {
       free(ns);
       return CURLE_URL_MALFORMAT;
     }
diff --git a/lib/escape.h b/lib/escape.h
index 731b136..1de5e2a 100644
--- a/lib/escape.h
+++ b/lib/escape.h
@@ -24,10 +24,13 @@
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred. */

-CURLcode Curl_urldecode(struct SessionHandle *data,
- const char *string, size_t length,
- char **ostring, size_t *olen,
- bool reject_crlf);
+CURLcode Curl_urlfmtdecode(struct SessionHandle *data,
+ const char *string, size_t length,
+ char **ostring, size_t *olen,
+ bool decode_form, bool reject_crlf);
+
+#define Curl_urldecode(d, s, l, o, ol, r) \
+ Curl_urlfmtdecode((d), (s), (l), (o), (ol), FALSE, (r))

 #endif /* HEADER_CURL_ESCAPE_H */

diff --git a/lib/http.c b/lib/http.c
index daaafe3..fd5ca8e 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -56,6 +56,7 @@
 #include "strequal.h"
 #include "sslgen.h"
 #include "http_digest.h"
+#include "http_oauth2.h"
 #include "curl_ntlm.h"
 #include "curl_ntlm_wb.h"
 #include "http_negotiate.h"
@@ -285,6 +286,10 @@ static bool pickoneauth(struct auth *pick)
      of preference in case of the existence of multiple accepted types. */
   if(avail & CURLAUTH_GSSNEGOTIATE)
     pick->picked = CURLAUTH_GSSNEGOTIATE;
+#ifndef CURL_DISABLE_OAUTH2
+ else if(avail & CURLAUTH_OAUTH2)
+ pick->picked = CURLAUTH_OAUTH2;
+#endif
   else if(avail & CURLAUTH_DIGEST)
     pick->picked = CURLAUTH_DIGEST;
   else if(avail & CURLAUTH_NTLM)
@@ -439,13 +444,16 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
   if(data->state.authproblem)
     return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;

- if(conn->bits.user_passwd &&
+#ifndef CURL_DISABLE_OAUTH2
+ /* should we have separate functions for OAuth 2.0 and username:password? */
+ if((conn->bits.user_passwd || conn->bits.oauth2) &&
      ((data->req.httpcode == 401) ||
       (conn->bits.authneg && data->req.httpcode < 300))) {
     pickhost = pickoneauth(&data->state.authhost);
     if(!pickhost)
       data->state.authproblem = TRUE;
   }
+#endif
   if(conn->bits.proxy_user_passwd &&
      ((data->req.httpcode == 407) ||
       (conn->bits.authneg && data->req.httpcode < 300))) {
@@ -553,6 +561,18 @@ output_auth_headers(struct connectdata *conn,
   else
 #endif
 #ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_OAUTH2
+ if(authstatus->picked == CURLAUTH_OAUTH2) {
+ auth="OAuth 2.0";
+ result = Curl_output_oauth2(conn,
+ proxy,
+ (const unsigned char *)request,
+ (const unsigned char *)path);
+ if(result)
+ return result;
+ }
+ else
+#endif
   if(authstatus->picked == CURLAUTH_DIGEST) {
     auth="Digest";
     result = Curl_output_digest(conn,
@@ -581,10 +601,19 @@ output_auth_headers(struct connectdata *conn,
   }

   if(auth) {
- infof(data, "%s auth using %s with user '%s'\n",
- proxy?"Proxy":"Server", auth,
- proxy?(conn->proxyuser?conn->proxyuser:""):
- (conn->user?conn->user:""));
+#ifndef CURL_DISABLE_OAUTH2
+ if(conn->bits.oauth2) {
+ infof(data, "%s OAuth 2.0 auth using token\n",
+ proxy?"Proxy":"Server");
+ }
+ else
+#endif
+ {
+ infof(data, "%s auth using %s with user '%s'\n",
+ proxy?"Proxy":"Server", auth,
+ proxy?(conn->proxyuser?conn->proxyuser:""):
+ (conn->user?conn->user:""));
+ }
     authstatus->multi = (!authstatus->done) ? TRUE : FALSE;
   }
   else
@@ -625,7 +654,11 @@ Curl_http_output_auth(struct connectdata *conn,
   authproxy = &data->state.authproxy;

   if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
- conn->bits.user_passwd)
+ conn->bits.user_passwd
+#ifndef CURL_DISABLE_OAUTH2
+ || conn->bits.oauth2
+#endif
+ )
     /* continue please */ ;
   else {
     authhost->done = TRUE;
@@ -804,6 +837,26 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,
       else
 #endif
 #ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_OAUTH2
+ if(checkprefix("MAC", start) || checkprefix("Bearer", start)) {
+ if((authp->avail & CURLAUTH_OAUTH2) != 0) {
+ infof(data, "Ignoring duplicate OAuth2 auth header.\n");
+ }
+ else {
+ *availp |= CURLAUTH_OAUTH2;
+ authp->avail |= CURLAUTH_OAUTH2;
+ if(authp->picked == CURLAUTH_OAUTH2) {
+ /* We asked for OAuth 2.0 authentication but got a 40X back
+ anyway, which basically means our token isn't
+ valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
         if(checkprefix("Digest", start)) {
           if((authp->avail & CURLAUTH_DIGEST) != 0) {
             infof(data, "Ignoring duplicate digest auth header.\n");
@@ -1721,7 +1774,76 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     conn->allocptr.uagent=NULL;
   }

- /* setup the authentication headers */
+ Curl_safefree(conn->allocptr.host);
+
+ ptr = Curl_checkheaders(data, "Host:");
+ if(ptr && (!data->state.this_is_a_follow ||
+ Curl_raw_equal(data->state.first_host, conn->host.name))) {
+#if !defined(CURL_DISABLE_COOKIES)
+ /* If we have a given custom Host: header, we extract the host name in
+ order to possibly use it for cookie reasons later on. We only allow the
+ custom Host: header if this is NOT a redirect, as setting Host: in the
+ redirected request is being out on thin ice. Except if the host name
+ is the same as the first one! */
+ char *cookiehost = copy_header_value(ptr);
+ if(!cookiehost)
+ return CURLE_OUT_OF_MEMORY;
+ if(!*cookiehost)
+ /* ignore empty data */
+ free(cookiehost);
+ else {
+ /* If the host begins with '[', we start searching for the port after
+ the bracket has been closed */
+ int startsearch = 0;
+ if(*cookiehost == '[') {
+ char *closingbracket;
+ /* since the 'cookiehost' is an allocated memory area that will be
+ freed later we cannot simply increment the pointer */
+ memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
+ closingbracket = strchr(cookiehost, ']');
+ if(closingbracket)
+ *closingbracket = 0;
+ }
+ else {
+ char *colon = strchr(cookiehost + startsearch, ':');
+ if(colon)
+ *colon = 0; /* The host must not include an embedded port number */
+ }
+ Curl_safefree(conn->allocptr.cookiehost);
+ conn->allocptr.cookiehost = cookiehost;
+ }
+#endif
+
+ conn->allocptr.host = NULL;
+ }
+ else {
+ /* When building Host: headers, we must put the host name within
+ [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
+
+ if(((conn->given->protocol&CURLPROTO_HTTPS) &&
+ (conn->remote_port == PORT_HTTPS)) ||
+ ((conn->given->protocol&CURLPROTO_HTTP) &&
+ (conn->remote_port == PORT_HTTP)) )
+ /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
+ the port number in the host string */
+ conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"");
+ else
+ conn->allocptr.host = aprintf("Host: %s%s%s:%hu\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"",
+ conn->remote_port);
+
+ if(!conn->allocptr.host)
+ /* without Host: we can't make a nice request */
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Setup the authentication headers. We expect that Host: has
+ been set above as some authentication protocols require it. */
   result = Curl_http_output_auth(conn, request, ppath, FALSE);
   if(result)
     return result;
@@ -1808,74 +1930,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       te = "Transfer-Encoding: chunked\r\n";
   }

- Curl_safefree(conn->allocptr.host);
-
- ptr = Curl_checkheaders(data, "Host:");
- if(ptr && (!data->state.this_is_a_follow ||
- Curl_raw_equal(data->state.first_host, conn->host.name))) {
-#if !defined(CURL_DISABLE_COOKIES)
- /* If we have a given custom Host: header, we extract the host name in
- order to possibly use it for cookie reasons later on. We only allow the
- custom Host: header if this is NOT a redirect, as setting Host: in the
- redirected request is being out on thin ice. Except if the host name
- is the same as the first one! */
- char *cookiehost = copy_header_value(ptr);
- if(!cookiehost)
- return CURLE_OUT_OF_MEMORY;
- if(!*cookiehost)
- /* ignore empty data */
- free(cookiehost);
- else {
- /* If the host begins with '[', we start searching for the port after
- the bracket has been closed */
- int startsearch = 0;
- if(*cookiehost == '[') {
- char *closingbracket;
- /* since the 'cookiehost' is an allocated memory area that will be
- freed later we cannot simply increment the pointer */
- memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
- closingbracket = strchr(cookiehost, ']');
- if(closingbracket)
- *closingbracket = 0;
- }
- else {
- char *colon = strchr(cookiehost + startsearch, ':');
- if(colon)
- *colon = 0; /* The host must not include an embedded port number */
- }
- Curl_safefree(conn->allocptr.cookiehost);
- conn->allocptr.cookiehost = cookiehost;
- }
-#endif
-
- conn->allocptr.host = NULL;
- }
- else {
- /* When building Host: headers, we must put the host name within
- [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
-
- if(((conn->given->protocol&CURLPROTO_HTTPS) &&
- (conn->remote_port == PORT_HTTPS)) ||
- ((conn->given->protocol&CURLPROTO_HTTP) &&
- (conn->remote_port == PORT_HTTP)) )
- /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
- the port number in the host string */
- conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
- conn->bits.ipv6_ip?"[":"",
- host,
- conn->bits.ipv6_ip?"]":"");
- else
- conn->allocptr.host = aprintf("Host: %s%s%s:%hu\r\n",
- conn->bits.ipv6_ip?"[":"",
- host,
- conn->bits.ipv6_ip?"]":"",
- conn->remote_port);
-
- if(!conn->allocptr.host)
- /* without Host: we can't make a nice request */
- return CURLE_OUT_OF_MEMORY;
- }
-
 #ifndef CURL_DISABLE_PROXY
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
     /* Using a proxy but does not tunnel through it */
diff --git a/lib/http_oauth2.c b/lib/http_oauth2.c
new file mode 100644
index 0000000..de90414
--- /dev/null
+++ b/lib/http_oauth2.c
@@ -0,0 +1,397 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel_at_haxx.se<mailto:daniel_at_haxx.se>>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_OAUTH2) && !defined(CURL_DISABLE_HTTP)
+
+#include "urldata.h"
+#include "sendf.h"
+#include "rawstr.h"
+#include "curl_base64.h"
+#include "curl_hmac.h"
+#include "http_oauth2.h"
+#include "http.h"
+#include "strtok.h"
+#include "url.h" /* for Curl_safefree() */
+#include "curl_memory.h"
+#include "non-ascii.h" /* included for Curl_convert_... prototypes */
+#include "warnless.h"
+
+#include <curl/oauth2.h>
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define MAX_VALUE_LENGTH 256
+#define MAX_CONTENT_LENGTH 1024
+
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+const HMAC_params Curl_HMAC_SHA1[] = {
+ {
+ (HMAC_hinit_func) SHA1_Init, /* Hash initialization function. */
+ (HMAC_hupdate_func) SHA1_Update, /* Hash update function. */
+ (HMAC_hfinal_func) SHA1_Final, /* Hash computation end function. */
+ sizeof(SHA_CTX), /* Size of hash context structure. */
+ 64, /* Maximum key length. */
+ 20 /* Result size. */
+ }
+};
+
+const HMAC_params Curl_HMAC_SHA256[] = {
+ {
+ (HMAC_hinit_func) SHA256_Init, /* Hash initialization function. */
+ (HMAC_hupdate_func) SHA256_Update, /* Hash update function. */
+ (HMAC_hfinal_func) SHA256_Final, /* Hash computation end function. */
+ sizeof(SHA256_CTX), /* Size of hash context structure. */
+ 64, /* Maximum key length. */
+ 32 /* Result size. */
+ }
+};
+
+#endif
+
+CURLcode Curl_output_oauth2(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath)
+{
+ /* Please refer to draft-ietf-oauth-v2-http-mac for all the juicy
+ details about HTTP MAC construction. */
+
+ struct SessionHandle *data = conn->data;
+ struct curl_oauth2_token *token = data->set.oauth2token;
+ CURLcode rc;
+
+ /* Check that we have an OAuth 2.0 token. */
+
+ if(!token) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+
+ switch(token->token_type) {
+ case CURL_OAUTH2_TOKEN_TYPE_INVALID:
+ rc = CURLE_OAUTH2_TOKEN_MALFORMAT;
+ break;
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ case CURL_OAUTH2_TOKEN_TYPE_MAC:
+ rc = Curl_output_mac(conn, proxy, request, uripath, token);
+ break;
+#endif
+ case CURL_OAUTH2_TOKEN_TYPE_BEARER:
+ rc = Curl_output_bearer(conn, proxy, request, uripath, token);
+ break;
+ default:
+ rc = CURLE_OAUTH2_TOKEN_TYPE_UNSUPPORTED;
+ break;
+ }
+
+ return rc;
+}
+
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+/* Defining this to a relatively large value is a good way to expose
+ bugs in server-side HTTP MAC timestamp validation. Defining it
+ to something larger than the difference between the date of calling
+ and the Epoch is a mistake... */
+#ifndef CURL_DISABLE_HTTPMAC_RECENT_EPOCH
+#define DELTA_EPOCH_IN_SECS 1023667200L
+#endif
+
+CURLcode Curl_output_mac(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct curl_oauth2_token *token)
+{
+ /* Please refer to draft-ietf-oauth-v2-http-mac for all the juicy
+ details about HTTP MAC construction. */
+
+ struct timeval now;
+ char ts[12];
+ char nonce[33];
+ long nonceval = 0;
+ size_t noncesz = 0;
+ char *nreq = NULL;
+ char **allocuserpwd;
+ struct auth *authp;
+ struct SessionHandle *data = conn->data;
+ const char *ext = data->set.str[STRING_HTTP_MAC_EXT];
+ const char *hosthdr = NULL, *hosthdrp1 = NULL, *hosthdrp2 = NULL;
+ char *hostname = NULL;
+ unsigned long port = 0;
+ const char *extinfo = "";
+ const HMAC_params *params;
+ HMAC_context *ctxt;
+ char digest[32]; /* The max of result_len is enough. */
+ char *mac = NULL;
+ size_t macsz = 0;
+ CURLcode rc;
+/* The CURL_OUTPUT_MAC_CONV macro below is for non-ASCII machines.
+ It converts digest text to ASCII so the MAC will be correct for
+ what ultimately goes over the network.
+*/
+#define CURL_OUTPUT_MAC_CONV(a, b) \
+ rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
+ if(rc != CURLE_OK) { \
+ free(b); \
+ goto cleanup; \
+ }
+
+ if(token->token_type != CURL_OAUTH2_TOKEN_TYPE_MAC) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+
+ if(proxy) {
+ allocuserpwd = &conn->allocptr.proxyuserpwd;
+ authp = &data->state.authproxy;
+ }
+ else {
+ allocuserpwd = &conn->allocptr.userpwd;
+ authp = &data->state.authhost;
+ }
+
+ if(*allocuserpwd) {
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = NULL;
+ }
+
+ authp->done = TRUE;
+
+ /* Generate a timestamp from a monotically increasing source whose
+ origin does not change. */
+ now = curlx_tvgettimeofday();
+#ifdef DELTA_EPOCH_IN_SECS
+ now.tv_sec -= DELTA_EPOCH_IN_SECS;
+#endif
+ snprintf(ts, sizeof(ts) - 1, "%ld", (long)now.tv_sec);
+ ts[sizeof(ts) - 1] = '\0';
+
+ /* Generate a nonce that is unique for that timestamp */
+
+ nonceval = (long)now.tv_sec + now.tv_usec;
+ for(noncesz = 0; nonceval && noncesz < sizeof(nonce) - 1; ++noncesz) {
+ int base = "\x08\x10\x0a\x1a"[noncesz % 4];
+ nonce[noncesz] = "0123456789abcdefghijklmnopqrstuvwxyz"[nonceval % base];
+ nonceval /= base;
+ }
+ nonce[noncesz] = '\0';
+
+ /* Find hostname and port in headers, do not use the connection data. */
+
+ hosthdr = conn->allocptr.host;
+ if(!hosthdr) {
+ hosthdr = Curl_checkheaders(data, "Host:");
+ }
+ if(!hosthdr) {
+ rc = CURLE_HTTP_MAC_INVALID_HOST;
+ goto cleanup;
+ }
+
+ for(hosthdrp1 = hosthdr + 5; *hosthdrp1 && ISSPACE(*hosthdrp1); ++hosthdrp1);
+ for(hosthdrp2 = hosthdrp1; *hosthdrp2 && *hosthdrp2 != ':'
+ && !ISSPACE(*hosthdrp2); ++hosthdrp2);
+ if(hosthdrp2 - hosthdrp1 == 0) {
+ rc = CURLE_HTTP_MAC_INVALID_HOST;
+ goto cleanup;
+ }
+ hostname = calloc(1, hosthdrp2 - hosthdrp1 + 1);
+ if(!hostname) {
+ rc = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+ strncpy(hostname, hosthdrp1, hosthdrp2 - hosthdrp1);
+
+ for(hosthdrp1 = hosthdrp2 = (hosthdrp2 + (*hosthdrp2 ? 1 : 0));
+ *hosthdrp2 && ISDIGIT(*hosthdrp2); ++hosthdrp2);
+ if(hosthdrp2 - hosthdrp1) {
+ char *rest;
+ port = strtoul(hosthdrp1, &rest, 10); /* Must be decimal */
+ if(rest != (hosthdrp1 + 1) && !*rest) {
+ if(port > 0xffff) { /* Single unix standard says port numbers are
+ * 16 bits long */
+ rc = CURLE_HTTP_MAC_INVALID_HOST;
+ goto cleanup;
+ }
+ }
+ }
+ else if(conn->handler == &Curl_handler_http) {
+ port = PORT_HTTP;
+ }
+ else if(conn->handler == &Curl_handler_https) {
+ port = PORT_HTTPS;
+ }
+
+ for(; *hosthdrp2 && ISSPACE(*hosthdrp2); ++hosthdrp2);
+ if(*hosthdrp2) {
+ rc = CURLE_HTTP_MAC_INVALID_HOST;
+ goto cleanup;
+ }
+
+ /* Now generate the normalized request */
+ if(!ext) {
+ ext = "";
+ }
+ nreq = aprintf("%s\x0a%s\x0a%s\x0a%s\x0a%s\x0a%lu\x0a%s\x0a",
+ ts,
+ nonce,
+ request,
+ uripath,
+ hostname,
+ port,
+ ext);
+ if(!nreq) {
+ rc = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+ CURL_OUTPUT_MAC_CONV(data, nreq);
+
+ /* Pick appropriate parameters. */
+ switch (token->mac_token.mac_algo) {
+ case CURL_OAUTH2_MAC_ALGO_HMAC_SHA1:
+ params = Curl_HMAC_SHA1;
+ break;
+ case CURL_OAUTH2_MAC_ALGO_HMAC_SHA256:
+ params = Curl_HMAC_SHA256;
+ break;
+ default:
+ rc = CURLE_OAUTH2_TOKEN_MALFORMAT;
+ goto cleanup;
+ }
+
+ /* Compute the MAC using the MAC token key */
+ ctxt = Curl_HMAC_init(params,
+ (const unsigned char *)token->mac_token.mac_key,
+ curlx_uztoui(strlen(token->mac_token.mac_key)));
+ if(!ctxt) {
+ rc = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ /* Update the MAC with the normalized request */
+
+ Curl_HMAC_update(ctxt, (const unsigned char *)nreq,
+ curlx_uztoui(strlen(nreq)));
+
+ /* Finalise the MAC */
+ Curl_HMAC_final(ctxt, (unsigned char *)digest);
+
+ /* Base64-encode the mac to produce the request MAC */
+
+ rc = Curl_base64_encode(data, digest, (*params).hmac_resultlen,
+ &mac, &macsz);
+ if(rc)
+ goto cleanup;
+
+ /* Produce the Authorization header. */
+ if(ext && strlen(ext)) {
+ extinfo = aprintf("ext=\"%s\", ", ext);
+ }
+
+ *allocuserpwd =
+ aprintf( "Authorization: MAC "
+ "id=\"%s\", "
+ "ts=\"%s\", "
+ "nonce=\"%s\", "
+ "%s"
+ "mac=\"%s\"\n", token->access_token, ts, nonce, extinfo, mac);
+
+ if(!*allocuserpwd) {
+ rc = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+ CURL_OUTPUT_MAC_CONV(data, allocuserpwd);
+
+ rc = CURLE_OK;
+
+ cleanup:
+ if(*extinfo) free((char *) extinfo);
+ Curl_safefree(mac);
+ Curl_safefree(hostname);
+ Curl_safefree(nreq);
+
+ return rc;
+}
+
+#endif
+
+CURLcode Curl_output_bearer(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct curl_oauth2_token *token)
+{
+ char **allocuserpwd;
+ struct auth *authp;
+ struct SessionHandle *data = conn->data;
+ CURLcode rc;
+/* The CURL_OUTPUT_BEARER_CONV macro below is for non-ASCII machines.
+ It converts digest text to ASCII so the MAC will be correct for
+ what ultimately goes over the network.
+*/
+#define CURL_OUTPUT_BEARER_CONV(a, b) \
+ rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
+ if(rc != CURLE_OK) { \
+ free(b); \
+ return rc; \
+ }
+
+ (void)request;
+ (void)uripath;
+
+ if(token->token_type != CURL_OAUTH2_TOKEN_TYPE_BEARER) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+
+ if(proxy) {
+ allocuserpwd = &conn->allocptr.proxyuserpwd;
+ authp = &data->state.authproxy;
+ }
+ else {
+ allocuserpwd = &conn->allocptr.userpwd;
+ authp = &data->state.authhost;
+ }
+
+ if(*allocuserpwd) {
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = NULL;
+ }
+
+ authp->done = TRUE;
+
+ /* Produce the Authorization header. */
+ *allocuserpwd =
+ aprintf( "Authorization: Bearer %s\n", token->access_token);
+ if(!*allocuserpwd) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ CURL_OUTPUT_BEARER_CONV(data, allocuserpwd);
+
+ return CURLE_OK;
+}
+
+#endif
diff --git a/lib/http_oauth2.h b/lib/http_oauth2.h
new file mode 100644
index 0000000..029cb41
--- /dev/null
+++ b/lib/http_oauth2.h
@@ -0,0 +1,53 @@
+#ifndef HEADER_CURL_HTTP_OAUTH2_H
+#define HEADER_CURL_HTTP_OAUTH2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel_at_haxx.se<mailto:daniel_at_haxx.se>>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+struct curl_oauth2_token;
+
+enum {
+ CURLMACALGO_SHA1,
+ CURLMACALGO_SHA256
+};
+
+/* this is for creating an OAuth 2 header output */
+CURLcode Curl_output_oauth2(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath);
+
+/* this is for creating a MAC header output */
+CURLcode Curl_output_mac(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct curl_oauth2_token *token);
+
+/* this is for creating a Bearer header output */
+CURLcode Curl_output_bearer(struct connectdata *conn,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct curl_oauth2_token *token);
+
+#endif /* HEADER_CURL_HTTP_OAUTH2_H */
diff --git a/lib/oauth2.c b/lib/oauth2.c
new file mode 100644
index 0000000..f2bac1a
--- /dev/null
+++ b/lib/oauth2.c
@@ -0,0 +1,440 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel_at_haxx.se<mailto:daniel_at_haxx.se>>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_OAUTH2) && !defined(CURL_DISABLE_HTTP)
+
+#include "urldata.h"
+#include "escape.h"
+#include "memdebug.h"
+
+#include <stdio.h>
+
+#if HAVE_JSONSL
+#define JSONSL_STATE_GENERIC
+#include <jsonsl.h>
+#endif
+
+#include <curl/oauth2.h>
+
+#define CURL_OAUTH2_TOKEN_FILE_BUFSZ 2048
+
+struct NameValue {
+ const char *name;
+ int value;
+};
+
+static const struct NameValue token_nv_TOKENTYPES[] = {
+ { "bearer", CURL_OAUTH2_TOKEN_TYPE_BEARER },
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ { "mac", CURL_OAUTH2_TOKEN_TYPE_MAC },
+#endif
+ { 0, 0 }
+};
+
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+static const struct NameValue token_nv_MACALGOS[] = {
+ { "hmac-sha-1", CURL_OAUTH2_MAC_ALGO_HMAC_SHA1 },
+ { "hmac-sha-256", CURL_OAUTH2_MAC_ALGO_HMAC_SHA256 },
+ { 0, 0 }
+};
+
+#endif
+
+static CURLcode set_token_property(struct curl_oauth2_token *token,
+ const char *key, size_t key_len,
+ const char *val, size_t val_len) {
+ CURLcode rc = CURLE_OK;
+ if(key_len == 12 &&
+ !strncmp("access_token", key, 12)) {
+ char *dupstr = malloc(val_len + 1);
+ if(dupstr) {
+ strncpy(dupstr, val, val_len);
+ dupstr[val_len] = '\0';
+ token->access_token = dupstr;
+ }
+ else {
+ rc = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else if(key_len == 10 &&
+ !strncmp(key, "token_type", 10)) {
+ const struct NameValue *nvlist;
+ for(nvlist = token_nv_TOKENTYPES; nvlist->name; ++nvlist) {
+ if((val_len = strlen(nvlist->name)) != 0
+ && !strncmp(val, nvlist->name, val_len)) {
+ break;
+ }
+ }
+ if(nvlist->name) {
+ token->token_type = nvlist->value;
+ }
+ else {
+ token->token_type = CURL_OAUTH2_TOKEN_TYPE_INVALID;
+ rc = CURLE_OAUTH2_TOKEN_TYPE_UNSUPPORTED;
+ }
+ }
+#if !defined(CURL_DISABLE_HTTPMAC) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ else if(key_len == 7 &&
+ !strncmp(key, "mac_key", 7)) {
+ char *dupstr = malloc(val_len + 1);
+ if(dupstr) {
+ strncpy(dupstr, val, val_len);
+ dupstr[val_len] = '\0';
+ token->mac_token.mac_key = dupstr;
+ }
+ else {
+ rc = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else if(key_len == 13 &&
+ !strncmp(key, "mac_algorithm", 13)) {
+ const struct NameValue *nvlist;
+ for(nvlist = token_nv_MACALGOS; nvlist->name; ++nvlist) {
+ if((val_len = strlen(nvlist->name)) != 0
+ && !strncmp(val, nvlist->name, val_len)) {
+ break;
+ }
+ }
+ if(nvlist->name) {
+ token->mac_token.mac_algo = nvlist->value;
+ }
+ else {
+ token->mac_token.mac_algo = CURL_OAUTH2_MAC_ALGO_INVALID;
+ rc = CURLE_HTTP_MAC_ALGO_UNSUPPORTED;
+ }
+ }
+#endif
+
+ return rc;
+}
+
+static CURLcode parse_oauth2_urlencoded(const char *tokstr, size_t tokstrlen,
+ struct curl_oauth2_token *token) {
+ const char *end = tokstr + tokstrlen;
+ const char *tokstrp1, *tokstrp2;
+
+ if(tokstrlen && tokstr[tokstrlen - 1] == '&') {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+
+ for(tokstrp1 = tokstr; tokstrp1 < end; ++tokstrp1) {
+ CURLcode rc;
+ const char *keyenc, *valenc;
+ char *key = NULL, *val = NULL;
+ size_t keyenc_len, key_len, valenc_len, val_len;
+
+ for(tokstrp2 = tokstrp1; tokstrp2 < end && *tokstrp2 != '=' &&
+ *tokstrp2 != '&'; ++tokstrp2);
+ keyenc_len = tokstrp2 - tokstrp1;
+ if(!keyenc_len) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+ keyenc = tokstrp1;
+ rc = Curl_urlfmtdecode(NULL, keyenc, keyenc_len, &key, &key_len,
+ true, true);
+ if(rc != CURLE_OK) {
+ if(rc == CURLE_URL_MALFORMAT) rc = CURLE_OAUTH2_TOKEN_MALFORMAT;
+ return rc;
+ }
+
+ if(tokstrp2 < end && *tokstrp2 != '&') {
+ for(tokstrp1 = tokstrp2 + 1; tokstrp2 < end && *tokstrp2 != '&';
+ ++tokstrp2);
+ }
+ else {
+ tokstrp1 = tokstrp2;
+ }
+ valenc_len = tokstrp2 - tokstrp1;
+ if(!valenc_len) {
+ val = calloc(1, sizeof(char));
+ if(!val) {
+ free(key);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ valenc = tokstrp1;
+ rc = Curl_urlfmtdecode(NULL, valenc, valenc_len, &val, &val_len,
+ true, true);
+ if(rc != CURLE_OK) {
+ if(rc == CURLE_URL_MALFORMAT) rc = CURLE_OAUTH2_TOKEN_MALFORMAT;
+ free(key);
+ return rc;
+ }
+ }
+
+ rc = set_token_property(token, key, key_len, val, val_len);
+ free(key);
+ free(val);
+
+ if(rc != CURLE_OK) {
+ return rc;
+ }
+
+ tokstrp1 = tokstrp2;
+ }
+
+ return CURLE_OK;
+}
+
+#if HAVE_JSONSL
+
+struct jsonsl_token_data {
+ CURLcode ccode;
+ char *key;
+ size_t key_len;
+ struct curl_oauth2_token *token;
+};
+
+static int parse_error_callback(jsonsl_t jsn,
+ jsonsl_error_t err,
+ struct jsonsl_state_st *state,
+ char *errat) {
+ struct jsonsl_token_data *datap = jsn->data;
+ datap->ccode = CURLE_OAUTH2_TOKEN_MALFORMAT;
+
+ (void)jsn;
+ (void)err;
+ (void)state;
+ (void)errat;
+
+ return 0;
+}
+
+static void parse_state_callback(jsonsl_t jsn,
+ jsonsl_action_t action,
+ struct jsonsl_state_st *state,
+ const char *buf) {
+
+ struct jsonsl_token_data *datap = jsn->data;
+
+ (void)action;
+
+ switch(state->level) {
+ case 1:
+ if(state->type != JSONSL_T_OBJECT) {
+ datap->ccode = CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+ break;
+
+ case 2:
+ if(state->type == JSONSL_T_HKEY) {
+ /* we allocate a string for the key so we can work across buffer
+ boundaries later if we decide to implement a streaming model */
+ char *dupstr;
+ Curl_safefree(datap->key);
+ dupstr = malloc(state->pos_cur - state->pos_begin + 1);
+ if(!dupstr) {
+ datap->ccode = CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ strncpy(dupstr, buf - state->pos_cur + state->pos_begin + 1,
+ state->pos_cur - state->pos_begin - 1);
+ dupstr[state->pos_cur - state->pos_begin - 1] = '\0';
+ datap->key = dupstr;
+ datap->key_len = state->pos_cur - state->pos_begin - 1;
+ }
+ }
+ else if(state->type == JSONSL_T_STRING) {
+ /* check if the key for that string is one that is meaningful for
+ an OAuth 2.0 token we support, and if so, store its value */
+ const char *key = datap->key;
+ size_t key_len = datap->key_len;
+ size_t val_start = state->pos_begin + 1;
+ size_t val_len = state->pos_cur - val_start;
+ const char *val = buf - state->pos_cur + val_start;
+
+ datap->ccode = set_token_property(datap->token, key, key_len,
+ val, val_len);
+ }
+ break;
+ default:
+ state->ignore_callback = 1;
+ break;
+ }
+
+ if(datap->ccode != CURLE_OK) {
+ state->ignore_callback = 1;
+ }
+}
+
+static CURLcode parse_oauth2_json(const char *tokstr, size_t tokstrlen,
+ struct curl_oauth2_token *token) {
+ jsonsl_t jsn;
+ struct jsonsl_token_data data = { CURLE_OK, NULL, 0, NULL };
+ data.token = token;
+
+ jsn = jsonsl_new(5); /* we only want one nesting level */
+ if(!jsn) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ jsn->data = &data;
+ jsn->error_callback = parse_error_callback;
+ jsn->action_callback = NULL;
+ jsn->action_callback_POP = parse_state_callback;
+
+ jsonsl_enable_all_callbacks(jsn);
+
+ jsonsl_feed(jsn, tokstr, tokstrlen);
+
+ jsonsl_destroy(jsn);
+
+ Curl_safefree(data.key);
+
+ /* bail if parsing was not successful */
+
+ if(data.ccode != CURLE_OK) {
+ return data.ccode;
+ }
+
+ /* validate the token data we got during parsing */
+
+ switch(token->token_type) {
+ case CURL_OAUTH2_TOKEN_TYPE_INVALID:
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ case CURL_OAUTH2_TOKEN_TYPE_BEARER:
+ if(!token->access_token) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+ break;
+ case CURL_OAUTH2_TOKEN_TYPE_MAC:
+ if(!token->mac_token.mac_key) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+ if(token->mac_token.mac_algo == CURL_OAUTH2_MAC_ALGO_INVALID) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+ }
+ break;
+ }
+
+ return CURLE_OK;
+}
+
+#else
+
+static CURLcode parse_oauth2_json(const char *tokstr, size_t tokstrlen,
+ struct curl_oauth2_token *token) {
+ return CURLE_OAUTH2_TOKEN_MALFORMAT;
+}
+
+#endif /* HAVE_JSONSL */
+
+CURLcode curl_parse_oauth2_token(const char *tokbuf, size_t tokbufsz,
+ struct curl_oauth2_token *token) {
+
+ const char *cp;
+ CURLcode rc;
+
+ /* determine the token file format and parse it */
+
+ for(cp = tokbuf; (size_t) (cp - tokbuf) < tokbufsz && ISSPACE(*cp); ++cp);
+ if((size_t) (cp - tokbuf) < tokbufsz && *cp == '{') {
+ rc = parse_oauth2_json(cp, tokbufsz - (cp - tokbuf), token);
+ }
+ else {
+ rc = parse_oauth2_urlencoded(cp, tokbufsz - (cp - tokbuf), token);
+ }
+
+ return rc;
+}
+
+CURLcode curl_parse_oauth2_token_file(const char *fname,
+ struct curl_oauth2_token *token) {
+ char buf[CURL_OAUTH2_TOKEN_FILE_BUFSZ];
+ char *bufp = NULL;
+ size_t bufsz = sizeof(buf), filesz = 0;
+ size_t nread;
+ FILE *fp = NULL;
+ CURLcode rc;
+
+ /* blank the token out */
+
+ memset(token, 0, sizeof(*token));
+
+ /* read the file into a buffer if possible, expanding in memory if needed */
+
+ fp = fopen(fname, "r");
+ if(fp == NULL) {
+ return CURLE_READ_ERROR;
+ }
+ if((nread = fread(buf, 1, bufsz, fp)) == bufsz) {
+ do {
+ char *newbufp;
+ newbufp = realloc(bufp, filesz + nread);
+ if(!newbufp) {
+ free(bufp);
+ rc = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+ else {
+ bufp = newbufp;
+ }
+ memcpy(bufp + filesz, buf, nread);
+ filesz += nread;
+ } while((nread = fread(buf, 1, bufsz, fp)));
+ }
+ else {
+ bufp = buf;
+ filesz = nread;
+ }
+
+ /* remove trailing white space */
+
+ for(; filesz && ISSPACE(bufp[filesz - 1]); --filesz);
+
+ /* parse the token */
+
+ rc = curl_parse_oauth2_token(bufp, filesz, token);
+
+cleanup:
+ fclose(fp);
+
+ /* free allocated memory */
+
+ if(bufp && bufp != buf) {
+ free(bufp);
+ }
+
+ return rc;
+}
+
+void curl_free_oauth2_token(struct curl_oauth2_token *token) {
+ char *cp = (char *)token->access_token;
+ Curl_safefree(cp);
+#ifndef CURL_DISABLE_HTTPMAC
+ switch(token->token_type) {
+ case CURL_OAUTH2_TOKEN_TYPE_MAC:
+ cp = (char *)token->mac_token.mac_key;
+ Curl_safefree(cp);
+ break;
+ default:
+ break;
+ }
+#endif
+}
+
+#endif
+
diff --git a/lib/strerror.c b/lib/strerror.c
index 58c6dc3..1688406 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -292,6 +292,18 @@ curl_easy_strerror(CURLcode error)
   case CURLE_CHUNK_FAILED:
     return "Chunk callback failed";

+ case CURLE_OAUTH2_TOKEN_MALFORMAT:
+ return "Failed to parse valid OAuth 2.0 token";
+
+ case CURLE_OAUTH2_TOKEN_TYPE_UNSUPPORTED:
+ return "Unsupported OAuth 2.0 token type";
+
+ case CURLE_HTTP_MAC_ALGO_UNSUPPORTED:
+ return "Unsupported HTTP MAC algorithm";
+
+ case CURLE_HTTP_MAC_INVALID_HOST:
+ return "Missing or invalid Host header";
+
     /* error codes not used by current libcurl */
   case CURLE_OBSOLETE16:
   case CURLE_OBSOLETE20:
diff --git a/lib/timeval.c b/lib/timeval.c
index 2fd7201..cafa1cf 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -101,6 +101,59 @@ struct timeval curlx_tvnow(void)

 #endif

+#if defined(WIN32) && !defined(MSDOS)
+
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
+# define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
+#else
+# define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
+#endif
+
+struct timeval curlx_tvgettimeofday(void)
+{
+ struct timeval now;
+
+ /* Define a structure to receive the current Windows filetime */
+ FILETIME ft;
+
+ /* Initialize the present time to 0 and the timezone to UTC */
+ unsigned __int64 tmpres = 0;
+ static int tzflag = 0;
+
+ GetSystemTimeAsFileTime(&ft);
+
+ /* The GetSystemTimeAsFileTime returns the number of 100 nanosecond
+ intervals since Jan 1, 1601 in a structure. Copy the high bits to
+ the 64 bit tmpres, shift it left by 32 then or in the low 32 bits. */
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+
+ /* Convert to microseconds by dividing by 10 */
+ tmpres /= 10;
+
+ /* The Unix epoch starts on Jan 1 1970. Need to subtract the difference
+ * in seconds from Jan 1 1601. */
+ tmpres -= DELTA_EPOCH_IN_MICROSECS;
+
+ /* Finally change microseconds to seconds and place in the seconds value.
+ The modulus picks up the microseconds. */
+
+ now.tv_sec = (long)(tmpres / 1000000UL);
+ now.tv_usec = (long)(tmpres % 1000000UL);
+
+ return now;
+}
+
+#else
+
+struct timeval curlx_tvgettimeofday(void)
+{
+ return curlx_tvnow();
+}
+
+#endif
+
 /*
  * Make sure that the first argument is the more recent time, as otherwise
  * we'll get a weird negative time-diff back...
diff --git a/lib/timeval.h b/lib/timeval.h
index 3f1b9ea..0c2cdb6 100644
--- a/lib/timeval.h
+++ b/lib/timeval.h
@@ -30,6 +30,7 @@
 #include "curl_setup.h"

 struct timeval curlx_tvnow(void);
+struct timeval curlx_tvgettimeofday(void);

 /*
  * Make sure that the first argument (t1) is the more recent time and t2 is
diff --git a/lib/url.c b/lib/url.c
index 80c8a99..f5e249e 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2413,6 +2413,21 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     data->set.tcp_keepintvl = va_arg(param, long);
     break;

+#ifndef CURL_DISABLE_OAUTH2
+
+ case CURLOPT_OAUTH2_TOKEN:
+ /*
+ * OAuth 2.0 token to use in the operation
+ */
+ data->set.oauth2token = va_arg(param, struct curl_oauth2_token *);
+ break;
+ case CURLOPT_HTTP_MAC_EXT:
+ result = setstropt(&data->set.str[STRING_HTTP_MAC_EXT],
+ va_arg(param, char *));
+ break;
+
+#endif
+
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
@@ -3378,6 +3393,9 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
 #endif /* CURL_DISABLE_PROXY */

   conn->bits.user_passwd = (NULL != data->set.str[STRING_USERNAME])?TRUE:FALSE;
+#ifndef CURL_DISABLE_OAUTH2
+ conn->bits.oauth2 = (NULL != data->set.oauth2token)?TRUE:FALSE;
+#endif
   conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;

diff --git a/lib/urldata.h b/lib/urldata.h
index 7a275da..9460eea 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -193,6 +193,10 @@
 #include <libssh2_sftp.h>
 #endif /* HAVE_LIBSSH2_H */

+#ifndef CURL_DISABLE_OAUTH2
+#include <curl/oauth2.h>
+#endif
+
 /* Download buffer size, keep it fairly big for speed reasons */
 #undef BUFSIZE
 #define BUFSIZE CURL_MAX_WRITE_SIZE
@@ -448,6 +452,9 @@ struct ConnectBits {
   bool httpproxy; /* if set, this transfer is done through a http proxy */
   bool user_passwd; /* do we use user+password for this connection? */
   bool proxy_user_passwd; /* user+password for the proxy? */
+#ifndef CURL_DISABLE_OAUTH2
+ bool oauth2; /* do we use OAuth2 for this connection? */
+#endif
   bool ipv6_ip; /* we communicate with a remote site specified with pure IPv6
                    IP address */
   bool ipv6; /* we communicate with a site using an IPv6 address */
@@ -887,7 +894,7 @@ struct connectdata {
                                 well be the same we read from.
                                 CURL_SOCKET_BAD disables */

- /** Dynamicly allocated strings, MUST be freed before this **/
+ /** Dynamically allocated strings, MUST be freed before this **/
   /** struct is killed. **/
   struct dynamically_allocated_data {
     char *proxyuserpwd;
@@ -1376,6 +1383,8 @@ enum dupstring {
   STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */
 #endif

+ STRING_HTTP_MAC_EXT,
+
   /* -- end of strings -- */
   STRING_LAST /* not used, just an end-of-list marker */
 };
@@ -1582,6 +1591,10 @@ struct UserDefined {
   long tcp_keepintvl; /* seconds between TCP keepalive probes */

   size_t maxconnects; /* Max idle connections in the connection cache */
+
+#ifndef CURL_DISABLE_OAUTH2
+ struct curl_oauth2_token *oauth2token;
+#endif
 };

 struct Names {
diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4
index 44b018e..47946dd 100644
--- a/m4/curl-confopts.m4
+++ b/m4/curl-confopts.m4
@@ -85,6 +85,65 @@ AC_HELP_STRING([--disable-ares],[Disable c-ares for DNS lookups]),
 ])

+dnl CURL_CHECK_OPTION_OAUTH2
+dnl -------------------------------------------------
+AC_DEFUN([CURL_CHECK_OPTION_OAUTH2], [
+ AC_BEFORE([$0],[CURL_CHECK_OPTION_HTTPMAC])dnl
+ AC_BEFORE([$0],[CURL_CHECK_OAUTH2])dnl
+ AC_MSG_CHECKING([whether to enable support for OAuth 2.0 authorization])
+ OPT_OAUTH2="yes"
+ AC_ARG_ENABLE(oauth2,
+AC_HELP_STRING([--enable-oauth2],[Enable OAuth 2.0 authorization])
+AC_HELP_STRING([--disable-oauth2],[Disable OAuth 2.0 authorization]),
+ OPT_OAUTH2=$enableval)
+ case "$OPT_OAUTH2" in
+ no)
+ dnl --disable-oauth2 option used
+ want_oauth2="no"
+ AC_DEFINE(CURL_DISABLE_OAUTH2, 1, [to disable OAuth 2.0 support])
+ AC_SUBST(CURL_DISABLE_OAUTH2, [1])
+ ;;
+ *)
+ dnl --enable-oauth2 option used
+ want_oauth2="yes"
+ ;;
+ esac
+ AC_MSG_RESULT([$want_oauth2])
+])
+AC_DEFUN([CURL_CHECK_OPTION_HTTPMAC], [
+ AC_MSG_CHECKING([whether to enable support for HTTP MAC])
+ OPT_HTTPMAC="$want_httpmac"
+ AC_ARG_ENABLE(httpmac,
+AC_HELP_STRING([--enable-httpmac],[Enable HTTP MAC support])
+AC_HELP_STRING([--disable-httpmac],[Disable HTTP MAC support]),
+ OPT_HTTPMAC=$enableval)
+ case "$OPT_HTTPMAC" in
+ no)
+ dnl --disable-httpmac option used
+ want_httpmac="no"
+ AC_DEFINE(CURL_DISABLE_HTTPMAC, 1, [to disable HTTP MAC support])
+ AC_SUBST(CURL_DISABLE_HTTPMAC, [1])
+ ;;
+ *)
+ dnl --enable-httpmac option used
+ want_httpmac="yes"
+ ;;
+ esac
+ AC_MSG_RESULT([$want_httpmac])
+])
+
+AC_DEFUN([CURL_CHECK_OAUTH2], [
+ #
+ if test "$want_oauth2" = "yes"; then
+ dnl OAuth 2.0 support has been requested, see if we can read JSON
+ AC_CHECK_LIB(jsonsl, jsonsl_new,
+ [has_json=yes
+ AC_DEFINE(HAVE_JSONSL, 1, [to use jsonsl to parse JSON])
+ AC_SUBST(HAVE_JSONSL, [1])
+ LIBS="$LIBS -ljsonsl"
+ ])
+ fi])
+
 dnl CURL_CHECK_OPTION_CURLDEBUG
 dnl -------------------------------------------------
 dnl Verify if configure has been invoked with option
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 1f6f948..f9e4416 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -204,6 +204,9 @@ struct Configurable {
   bool use_metalink; /* process given URLs as metalink XML file */
   metalinkfile *metalinkfile_list; /* point to the first node */
   metalinkfile *metalinkfile_last; /* point to the last/current node */
+
+ char *oauth2token; /* file containing the OAuth 2 token */
+ char *httpmacext; /* "ext" property of an HTTP MAC */
 }; /* struct Configurable */

 void free_config_fields(struct Configurable *config);
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 297b986..b6e7cfb 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -95,6 +95,10 @@ static const struct LongShort aliases[]= {
   {"*M", "ntlm-wb", FALSE},
   {"*n", "basic", FALSE},
   {"*o", "anyauth", FALSE},
+#ifndef CURL_DISABLE_OAUTH2
+ {"*O", "oauth2", TRUE},
+ {"*X", "http-mac-ext", TRUE},
+#endif
 #ifdef USE_WATT32
   {"*p", "wdebug", FALSE},
 #endif
@@ -398,9 +402,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
         GetStr(&config->egd_file, nextarg);
         break;
       case 'c': /* connect-timeout */
- err = str2unum(&config->connecttimeout, nextarg);
- if(err)
- return err;
+ rc=str2unum(&config->connecttimeout, nextarg);
+ if(rc)
+ return rc;
         break;
       case 'd': /* ciphers */
         GetStr(&config->cipher_list, nextarg);
@@ -529,6 +533,50 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
         /* --no-anyauth simply doesn't touch it */
         break;

+#ifndef CURL_DISABLE_OAUTH2
+ case 'O': /* --oauth2 */
+#ifdef CURL_ALLOW_OAUTH2_TOKEN_ON_COMMAND_LINE
+ /* get the token string */
+ if('@' == *nextarg) {
+ /* the data begins with a '@' letter, it means that a file name
+ or - (stdin) follows */
+#else
+ {
+#endif
+ FILE *file;
+ const char *fname;
+ if(curlx_strequal("-", nextarg)) {
+ fname = "<stdin>";
+ file = stdin;
+ }
+ else {
+ fname = nextarg;
+ file = fopen(nextarg, "r");
+ }
+ err = file2string(&config->oauth2token, file);
+ if(file && (file != stdin))
+ fclose(file);
+ if(err)
+ return err;
+ if(!config->oauth2token)
+ warnf(config, "Failed to read %s!\n", fname);
+ }
+#ifdef CURL_ALLOW_OAUTH2_TOKEN_ON_COMMAND_LINE
+ else
+ GetStr(&config->oauth2token, nextarg);
+#endif
+
+ if(toggle)
+ config->authtype |= CURLAUTH_OAUTH2;
+ else
+ config->authtype &= ~CURLAUTH_OAUTH2;
+ break;
+
+ case 'X': /* --http-mac-ext */
+ GetStr(&config->httpmacext, nextarg);
+ break;
+#endif
+
 #ifdef USE_WATT32
       case 'p': /* --wdebug */
         dbug_init();
@@ -545,9 +593,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
       case 's': /* --max-redirs */
         /* specified max no of redirects (http(s)), this accepts -1 as a
            special condition */
- err = str2num(&config->maxredirs, nextarg);
- if(err)
- return err;
+ rc = str2num(&config->maxredirs, nextarg);
+ if(rc)
+ return rc;
         if(config->maxredirs < -1)
           return PARAM_BAD_NUMERIC;
         break;
@@ -592,9 +640,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
           return PARAM_LIBCURL_DOESNT_SUPPORT;
         break;
       case 'y': /* --max-filesize */
- err = str2offset(&config->max_filesize, nextarg);
- if(err)
- return err;
+ rc = str2offset(&config->max_filesize, nextarg);
+ if(rc)
+ return rc;
         break;
       case 'z': /* --disable-eprt */
         config->disable_eprt = toggle;
@@ -670,19 +718,19 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
         config->proxybasic = toggle;
         break;
       case 'g': /* --retry */
- err = str2unum(&config->req_retry, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->req_retry, nextarg);
+ if(rc)
+ return rc;
         break;
       case 'h': /* --retry-delay */
- err = str2unum(&config->retry_delay, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->retry_delay, nextarg);
+ if(rc)
+ return rc;
         break;
       case 'i': /* --retry-max-time */
- err = str2unum(&config->retry_maxtime, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->retry_maxtime, nextarg);
+ if(rc)
+ return rc;
         break;

       case 'k': /* --proxy-negotiate */
@@ -769,9 +817,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
         config->nokeepalive = (!toggle)?TRUE:FALSE;
         break;
       case '3': /* --keepalive-time */
- err = str2unum(&config->alivetime, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->alivetime, nextarg);
+ if(rc)
+ return rc;
         break;
       case '4': /* --post302 */
         config->post302 = toggle;
@@ -797,9 +845,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
         config->proxyver = CURLPROXY_HTTP_1_0;
         break;
       case '9': /* --tftp-blksize */
- err = str2unum(&config->tftp_blksize, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->tftp_blksize, nextarg);
+ if(rc)
+ return rc;
         break;
       case 'A': /* --mail-from */
         GetStr(&config->mail_from, nextarg);
@@ -924,9 +972,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
     case 'C':
       /* This makes us continue an ftp transfer at given position */
       if(!curlx_strequal(nextarg, "-")) {
- err = str2offset(&config->resume_from, nextarg);
- if(err)
- return err;
+ rc = str2offset(&config->resume_from, nextarg);
+ if(rc)
+ return rc;
         config->resume_from_current = FALSE;
       }
       else {
@@ -1317,9 +1365,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
       break;
     case 'm':
       /* specified max time */
- err = str2unum(&config->timeout, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->timeout, nextarg);
+ if(rc)
+ return rc;
       break;
     case 'M': /* M for manual, huge help */
       if(toggle) { /* --no-manual shows no manual... */
@@ -1633,17 +1681,17 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
       break;
     case 'y':
       /* low speed time */
- err = str2unum(&config->low_speed_time, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->low_speed_time, nextarg);
+ if(rc)
+ return rc;
       if(!config->low_speed_limit)
         config->low_speed_limit = 1;
       break;
     case 'Y':
       /* low speed limit */
- err = str2unum(&config->low_speed_limit, nextarg);
- if(err)
- return err;
+ rc = str2unum(&config->low_speed_limit, nextarg);
+ if(rc)
+ return rc;
       if(!config->low_speed_time)
         config->low_speed_time = 30;
       break;
diff --git a/src/tool_help.c b/src/tool_help.c
index 124f640..f0eca5b 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -71,7 +71,7 @@ static const char *const helptext[] = {
   " --disable-epsv Inhibit using EPSV (F)",
   " -D, --dump-header FILE Write the headers to this file",
   " --egd-file FILE EGD socket path for random data (SSL)",
- " --engine ENGINGE Crypto engine (SSL). \"--engine list\" for list",
+ " --engine ENGINE Crypto engine (SSL). \"--engine list\" for list",
 #ifdef USE_ENVIRONMENT
   " --environment Write results to environment variables (RISC OS)",
 #endif
@@ -98,6 +98,9 @@ static const char *const helptext[] = {
   " -h, --help This help text",
   " --hostpubmd5 MD5 "
   "Hex encoded MD5 string of the host public key. (SSH)",
+#ifndef CURL_DISABLE_OAUTH2
+ " --http-mac-ext STRING Set the ext field for HTTP MAC (H)",
+#endif
   " -0, --http1.0 Use HTTP 1.0 (H)",
   " --ignore-content-length Ignore the HTTP Content-Length header",
   " -i, --include Include protocol headers in the output (H/F)",
@@ -135,6 +138,10 @@ static const char *const helptext[] = {
   " --no-sessionid Disable SSL session-ID reusing (SSL)",
   " --noproxy List of hosts which do not use proxy",
   " --ntlm Use HTTP NTLM authentication (H)",
+#ifndef CURL_DISABLE_OAUTH2
+ " --oauth2 FILE "
+ "Authenticate using the OAuth 2.0 token in the file (H)",
+#endif
   " -o, --output FILE Write output to <file> instead of stdout",
   " --pass PASS Pass phrase for the private key (SSL/SSH)",
   " --post301 "
diff --git a/src/tool_operate.c b/src/tool_operate.c
index bcbce20..af98028 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -186,6 +186,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 #endif

   /* inits */
+
   config->postfieldsize = -1;
   config->showerror = -1; /* will show errors */
   config->use_httpget = FALSE;
@@ -405,6 +406,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
   for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) {

     int up; /* upload file counter within a single upload glob */
+
     char *infiles; /* might be a glob pattern */
     char *outfiles;
     int infilenum;
@@ -414,6 +416,11 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
     metalinkfile *mlfile;
     metalink_resource *mlres;

+#ifndef CURL_DISABLE_OAUTH2
+ struct curl_oauth2_token oauth2token;
+ memset(&oauth2token, 0, sizeof(oauth2token));
+#endif
+
     outfiles = NULL;
     infilenum = 1;
     inglob = NULL;
@@ -1327,6 +1334,23 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
         retry_sleep = retry_sleep_default; /* ms */
         retrystart = tvnow();

+#ifndef CURL_DISABLE_OAUTH2
+ /* Parse a given OAuth 2 token and set the token as an option.
+ It seems silly to parse each time but if we parse outside
+ of the loop we get an issue with freeing unallocated pointers. */
+ if(config->oauth2token) {
+ res = curl_parse_oauth2_token(config->oauth2token,
+ strlen(config->oauth2token),
+ &oauth2token);
+ if(res) {
+ goto show_error;
+ }
+ }
+
+ my_setopt_oauth2token(curl, CURLOPT_OAUTH2_TOKEN, &oauth2token);
+ my_setopt_str(curl, CURLOPT_HTTP_MAC_EXT, config->httpmacext);
+#endif
+
 #ifndef CURL_DISABLE_LIBCURL_OPTION
         res = easysrc_perform();
         if(res) {
@@ -1636,6 +1660,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
 #endif /* USE_METALINK */

         /* No more business with this output struct */
+
         if(outs.alloc_filename)
           Curl_safefree(outs.filename);
 #ifdef USE_METALINK
@@ -1689,6 +1714,11 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
         urls = NULL;
       }

+#ifndef CURL_DISABLE_OAUTH2
+ /* Cleanup OAUth 2 token */
+ curl_free_oauth2_token(&oauth2token);
+#endif
+
       if(infilenum > 1) {
         /* when file globbing, exit loop upon critical error */
         if(is_fatal_error(res))
diff --git a/src/tool_setopt.c b/src/tool_setopt.c
index 4014177..c61659f 100644
--- a/src/tool_setopt.c
+++ b/src/tool_setopt.c
@@ -61,6 +61,7 @@ const NameValueUnsigned setopt_nv_CURLAUTH[] = {
   NV(CURLAUTH_NTLM),
   NV(CURLAUTH_DIGEST_IE),
   NV(CURLAUTH_NTLM_WB),
+ NV(CURLAUTH_OAUTH2),
   NV(CURLAUTH_ONLY),
   NV(CURLAUTH_NONE),
   NVEND,
@@ -130,6 +131,22 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = {
   NVEND
 };

+/* OAuth 2.0 token types. */
+static const NameValue setopt_nv_OAUTH2_TOKENTYPES[] = {
+ NV(CURL_OAUTH2_TOKEN_TYPE_INVALID),
+ NV(CURL_OAUTH2_TOKEN_TYPE_BEARER),
+ NV(CURL_OAUTH2_TOKEN_TYPE_MAC),
+ NVEND
+};
+
+/* OAuth 2.0 HTTP MAC token algorithm types. */
+static const NameValue setopt_nv_OAUTH2_HTTPMACALGOS[] = {
+ NV(CURL_OAUTH2_MAC_ALGO_INVALID),
+ NV(CURL_OAUTH2_MAC_ALGO_HMAC_SHA1),
+ NV(CURL_OAUTH2_MAC_ALGO_HMAC_SHA256),
+ NVEND
+};
+
 /* Format and add code; jump to nomem on malloc error */
 #define ADD(args) do { \
   ret = easysrc_add args; \
@@ -442,6 +459,60 @@ CURLcode tool_setopt_slist(CURL *curl, struct Configurable *config,
   return ret;
 }

+/* setopt wrapper for CURLOPT_OAUTH2_TOKEN */
+CURLcode tool_setopt_oauth2token(CURL *curl, struct Configurable *config,
+ const char *name, CURLoption tag,
+ struct curl_oauth2_token *token)
+{
+ CURLcode ret = CURLE_OK;
+ bool skip = FALSE;
+
+ ret = curl_easy_setopt(curl, tag, token);
+ if(!token)
+ skip = TRUE;
+
+ if(config->libcurl && !skip && !ret) {
+ const NameValue *nv = NULL;
+ DECL0("struct curl_oauth2_token token;");
+
+ for(nv=setopt_nv_OAUTH2_TOKENTYPES; nv->name; nv++) {
+ if(nv->value == token->token_type) break; /* found it */
+ }
+ if(! nv->name) {
+ /* If no definition was found, output an explicit value.
+ * This could happen if new values are defined and used
+ * but the NameValue list is not updated. */
+ DATA1("token.token_type = %d;", token->token_type);
+ }
+ else {
+ DATA1("token.token_type = %s;", nv->name);
+ }
+
+ DATA1("token.access_token = \"%s\";", token->access_token);
+
+ if(token->token_type == CURL_OAUTH2_TOKEN_TYPE_MAC) {
+ DATA1("token.mac_token.mac_key = \"%s\";", token->mac_token.mac_key);
+ }
+ for(nv=setopt_nv_OAUTH2_HTTPMACALGOS; nv->name; nv++) {
+ if(nv->value == token->mac_token.mac_algo) break; /* found it */
+ }
+ if(! nv->name) {
+ /* If no definition was found, output an explicit value.
+ * This could happen if new values are defined and used
+ * but the NameValue list is not updated. */
+ DATA1("token.mac_token.mac_algo = %d;", token->token_type);
+ }
+ else {
+ DATA1("token.mac_token.mac_algo= %s;", nv->name);
+ }
+ }
+ CODE1("curl_easy_setopt(hnd, %s, &token);", name);
+
+ nomem:
+ return ret;
+}
+
+
 /* generic setopt wrapper for all other options.
  * Some type information is encoded in the tag value. */
 CURLcode tool_setopt(CURL *curl, bool str, struct Configurable *config,
diff --git a/src/tool_setopt.h b/src/tool_setopt.h
index d107756..f62cf00 100644
--- a/src/tool_setopt.h
+++ b/src/tool_setopt.h
@@ -82,6 +82,9 @@ CURLcode tool_setopt_httppost(CURL *curl, struct Configurable *config,
 CURLcode tool_setopt_slist(CURL *curl, struct Configurable *config,
                            const char *name, CURLoption tag,
                            struct curl_slist *list);
+CURLcode tool_setopt_oauth2token(CURL *curl, struct Configurable *config,
+ const char *name, CURLoption tag,
+ struct curl_oauth2_token *token);
 CURLcode tool_setopt(CURL *curl, bool str, struct Configurable *config,
                      const char *name, CURLoption tag, ...);

@@ -106,6 +109,9 @@ CURLcode tool_setopt(CURL *curl, bool str, struct Configurable *config,
 #define my_setopt_slist(x,y,z) \
   SETOPT_CHECK(tool_setopt_slist(x, config, #y, y, z))

+#define my_setopt_oauth2token(x,y,z) \
+ SETOPT_CHECK(tool_setopt_oauth2token(x, config, #y, y, z))
+
 #define res_setopt(x,y,z) tool_setopt(x, FALSE, config, #y, y, z)

 #define res_setopt_str(x,y,z) tool_setopt(x, TRUE, config, #y, y, z)
@@ -135,6 +141,9 @@ CURLcode tool_setopt(CURL *curl, bool str, struct Configurable *config,
 #define my_setopt_slist(x,y,z) \
   SETOPT_CHECK(curl_easy_setopt(x, y, z))

+#define my_setopt_oauth2token(x,y,z) \
+ SETOPT_CHECK(curl_easy_setopt(x, y, z))
+
 #define res_setopt(x,y,z) curl_easy_setopt(x,y,z)

 #define res_setopt_str(x,y,z) curl_easy_setopt(x,y,z)
diff --git a/winbuild/BUILD.WINDOWS.txt b/winbuild/BUILD.WINDOWS.txt
index 5eac382..76f96d5 100644
--- a/winbuild/BUILD.WINDOWS.txt
+++ b/winbuild/BUILD.WINDOWS.txt
@@ -62,10 +62,17 @@ where <options> is one or many of:
   WITH_SSH2=<dll or static> - Enable libSSH2 support, DLL or static
   ENABLE_SSPI=<yes or no> - Enable SSPI support, defaults to yes
   ENABLE_IPV6=<yes or no> - Enable IPv6, defaults to yes
+ ENABLE_OAUTH2=<yes or no> - Enable OAuth 2.0 authorization, defaults to yes
+ ENABLE_HTTPMAC=<yes or no> - Enable HTTP MAC support for OAuth 2.0, defaults to the opposite of ENABLE_WINSSL
+ WITH_JSONSL=<dll or static> - Enable JSONSL support, DLL or static
   ENABLE_IDN=<yes or no> - Enable use of Windows IDN APIs, defaults to yes
                                  Requires Windows Vista or later, or installation from:
                                  http://www.microsoft.com/downloads/details.aspx?FamilyID=AD6158D7-DDBA-416A-9109-07607425A815
   ENABLE_WINSSL=<yes or no> - Enable native Windows SSL support, defaults to yes
   GEN_PDB=<yes or no> - Generate Program Database (debug symbols for release build)
   DEBUG=<yes or no> - Debug builds
- MACHINE=<x86 or x64> - Target architecture (default is x86)
\ No newline at end of file
+ MACHINE=<x86 or x64> - Target architecture (default is x86)
+
+if ENABLE_HTTPMAC is yes then OpenSSL is required (choose OpenSSL by using
+the options USE_WINSSL=no WITH_SSL=<dll or static>).
+
diff --git a/winbuild/Makefile.vc b/winbuild/Makefile.vc
index bc42832..4e4530f 100644
--- a/winbuild/Makefile.vc
+++ b/winbuild/Makefile.vc
@@ -23,6 +23,10 @@ CFGSET=true
 !MESSAGE WITH_SSL=<dll or static> - Enable OpenSSL support, DLL or static
 !MESSAGE WITH_ZLIB=<dll or static> - Enable zlib support, DLL or static
 !MESSAGE WITH_SSH2=<dll or static> - Enable libSSH2 support, DLL or static
+!MESSAGE ENABLE_OAUTH2=<yes or no> - Enable OAUth 2.0 authorization, defaults to yes
+!MESSAGE ENABLE_HTTPMAC=<yes or no> - Enable HTTP MAC for OAuth 2.0, defaults to the opposite of ENABLE_WINSSL
+!MESSAGE WITH_JSONSL=<dll or static> - Enable JSONSL support, DLL or static
+
 !MESSAGE ENABLE_IDN=<yes or no> - Enable use of Windows IDN APIs, defaults to yes
 !MESSAGE Requires Windows Vista or later, or installation from:
 !MESSAGE http://www.microsoft.com/downloads/details.aspx?FamilyID=AD6158D7-DDBA-416A-9109-07607425A815
@@ -102,6 +106,26 @@ USE_WINSSL = true
 USE_WINSSL = false
 !ENDIF

+!IFNDEF ENABLE_OAUTH2
+USE_OAUTH2 = true
+!ELSEIF "$(ENABLE_OAUTH2)"=="yes"
+USE_OAUTH2 = true
+!ELSEIF "$(ENABLE_OAUTH2)"=="no"
+USE_OAUTH2 = false
+!ENDIF
+
+!IFNDEF ENABLE_HTTPMAC
+!IF "$(USE_WINSSL)"=="true"
+USE_HTTPMAC = false
+!ELSE
+USE_HTTPMAC = true
+!ENDIF
+!ELSEIF "$(ENABLE_HTTPMAC)"=="yes"
+USE_HTTPMAC = true
+!ELSEIF "$(ENABLE_HTTPMAC)"=="no"
+USE_HTTPMAC = false
+!ENDIF
+
 CONFIG_NAME_LIB = libcurl

 !IF "$(WITH_SSL)"=="dll"
@@ -128,6 +152,15 @@ USE_SSH2 = true
 SSH2 = static
 !ENDIF

+!IF "$(WITH_JSONSL)"=="dll"
+USE_JSONSL = true
+JSONSL = dll
+!ELSEIF "$(WITH_JSONSL)"=="static"
+USE_JSONSL = true
+JSONSL = static
+!ENDIF
+
+
 CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-vc$(VC)-$(MACHINE)

 !IF "$(DEBUG)"=="yes"
@@ -146,6 +179,10 @@ CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-static
 CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssl-$(SSL)
 !ENDIF

+!IF "$(USE_JSONSL)"=="true"
+CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-jsonsl-$(JSONSL)
+!ENDIF
+
 !IF "$(USE_ZLIB)"=="true"
 CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-zlib-$(ZLIB)
 !ENDIF
@@ -195,6 +232,9 @@ $(MODE):
  @SET USE_SSPI=$(USE_SSPI)
  @SET USE_SPNEGO=$(USE_SPNEGO)
  @SET USE_WINSSL=$(USE_WINSSL)
+ @SET USE_OAUTH2=$(USE_OAUTH2)
+ @SET USE_HTTPMAC=$(USE_HTTPMAC)
+ @SET USE_JSONSL=$(USE_JSONSL)
  @$(MAKE) /NOLOGO /F MakefileBuild.vc

 copy_from_lib:
diff --git a/winbuild/MakefileBuild.vc b/winbuild/MakefileBuild.vc
index 5e8b392..1a10df6 100644
--- a/winbuild/MakefileBuild.vc
+++ b/winbuild/MakefileBuild.vc
@@ -87,7 +87,7 @@ PDB_NAME_DLL_DEBUG = $(BASE_NAME_DEBUG).pdb

 # CURL Command section
 PROGRAM_NAME = curl.exe
-CURL_CFLAGS = /I../lib /I../include /nologo /W3 /EHsc /DWIN32 /FD /c
+CURL_CFLAGS = /I../lib /I../include /nologo /W3 /EHsc /DWIN32 /FD /c $(OAUTH2_CFLAGS)
 CURL_LFLAGS = /nologo /out:$(DIRDIST)\bin\$(PROGRAM_NAME) /subsystem:console /machine:$(MACHINE)
 CURL_RESFLAGS = /i../include

@@ -121,6 +121,19 @@ SSL = static
 SSL_CFLAGS = /DUSE_SSLEAY /I"$(DEVEL_INCLUDE)/openssl"
 !ENDIF

+!IF "$(WITH_JSONSL)"=="dll"
+JSONSL_LIBS = jsonsl.lib
+USE_JSONSL = true
+JSONSL = dll
+!ELSEIF "$(WITH_JSONSL)"=="static"
+JSONSL_LIBS = jsonsl.lib
+USE_JSONSL = true
+JSONSL = static
+!ENDIF
+
+!IFDEF USE_JSONSL
+JSONSL_CFLAGS = /DHAVE_JSONSL
+!ENDIF

 !IF "$(WITH_ZLIB)"=="dll"
 ZLIB_LIBS = zlib.lib
@@ -164,6 +177,13 @@ IDN_CFLAGS = $(IDN_CFLAGS) /DUSE_WIN32_IDN /DWANT_IDN_PROTOTYPES
 WIN_LIBS = $(WIN_LIBS) Normaliz.lib
 !ENDIF

+!IF "$(USE_OAUTH2)"=="false"
+OAUTH2_CFLAGS = $(OAUTH2_CFLAGS) /DDISABLE_OAUTH2
+!ENDIF
+
+!IF "$(USE_HTTPMAC)"=="false"
+OAUTH2_CFLAGS = $(OAUTH2_CFLAGS) /DDISABLE_HTTPMAC
+!ENDIF

 !IFNDEF USE_IPV6
 USE_IPV6 = true
@@ -217,6 +237,18 @@ SSPI_CFLAGS = $(SSPI_CFLAGS) /DUSE_SCHANNEL
 !ENDIF

+!IFNDEF USE_OAUTH2
+USE_OAUTH2 = false
+!ENDIF
+
+!IFNDEF USE_HTTPMAC
+!IF "$(USE_WINSSL)"=="false"
+USE_HTTPMAC = true
+!ELSE
+USE_HTTPMAC = false
+!ENDIF
+!ENDIF
+
 !IF "$(GEN_PDB)"=="yes"
 GEN_PDB = true
 !ENDIF
@@ -322,6 +354,14 @@ LFLAGS = $(LFLAGS) $(SSH2_LFLAGS) $(SSH2_LIBS)
 CFLAGS = $(CFLAGS) $(IDN_CFLAGS)
 !ENDIF

+CFLAGS = $(CFLAGS) $(OAUTH2_CFLAGS)
+
+!IF "$(USE_JSONSL)"=="true"
+CFLAGS = $(CFLAGS) $(JSONSL_CFLAGS)
+LFLAGS = $(LFLAGS) $(JSONSL_LFLAGS) $(JSONSL_LIBS)
+!ENDIF
+
+
 !IF "$(USE_IPV6)"=="true"
 CFLAGS = $(CFLAGS) $(IPV6_CFLAGS)
 !ENDIF
@@ -385,14 +425,16 @@ package: $(TARGET)
  @cd $(MAKEDIR)

 $(TARGET): $(LIB_OBJS) $(LIB_DIROBJ) $(DISTDIR)
- @echo Using SSL: $(USE_SSL)
- @echo Using SSH2: $(USE_SSH2)
- @echo Using ZLIB: $(USE_ZLIB)
- @echo Using IDN: $(USE_IDN)
- @echo Using IPv6: $(USE_IPV6)
- @echo Using SSPI: $(USE_SSPI)
- @echo Using SPNEGO: $(USE_SPNEGO)
- @echo Using WinSSL: $(USE_WINSSL)
+ @echo Using SSL: $(USE_SSL)
+ @echo Using SSH2: $(USE_SSH2)
+ @echo Using ZLIB: $(USE_ZLIB)
+ @echo Using IDN: $(USE_IDN)
+ @echo Using IPv6: $(USE_IPV6)
+ @echo Using SSPI: $(USE_SSPI)
+ @echo Using SPNEGO: $(USE_SPNEGO)
+ @echo Using WinSSL: $(USE_WINSSL)
+ @echo Supporting OAuth 2.0: $(USE_OAUTH2)
+ @echo Supporting HTTP MAC: $(USE_HTTPMAC)
  @echo CFLAGS: $(CFLAGS)
  @echo LFLAGS: $(LFLAGS)
  @echo GenPDB: $(GEN_PDB)

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-02-01