Add custom handling of redirects and automatically sign the redirected URI, update TODO

git-svn-id: https://svn.php.net/repository/pecl/oauth/trunk@278387 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
John Jawed
2009-04-07 18:39:38 +00:00
parent c9bc6c1784
commit e57f52b10e
3 changed files with 160 additions and 109 deletions

18
TODO
View File

@@ -1,26 +1,8 @@
- Add support for PUT
* need to add support for HTTP PUT, I think this might be a bit tricky since PUT requires CURLOPT_READDATA which requires a FILE * handle...now I suppose it would be possible to write to a temp file somewhere or require the caller to specify a file handle (like in user land php curl).
pros for creating a temp file:
- won't need to have the user mess around with writing the file somewhere themselves
cons for creating a temp file:
- everything that may go wrong with creating temp files...
pros for forcing the user to pass in a file handle:
- we don't have to do anything other than point libcurl to the file handle
cons for forcing the user to pass in a file handle:
- harder for users, the whole point of this ext is to simplify the php user land interface so this is a sticking point
* of course I maybe completely wrong about my understanding of PUT in libcurl so feel free to /d this entire section
* will probably need to separate out the auth type and http method completely, thinking something in the order of:
$o->setArgs(array("param_name" => "value"),"POST");
$o->setArgs('{"message": "Hello World"}',"PUT");
- Respect param precedence,
* This ext does not currently care to respect parameter precedence, in the sense that if a common param is defined in POST/GET or Authorization header, the precendence is defined by: OAuth Core 1.0 section 9.1.1
- Add tests
* need there be anymore said?
- Add docs
* example code is great but real docs help too
- Allow runtime setting of Expect header for POST requests
* Don't know if always disabling the Expect header is going to have a big enough impact, if it does, we'll need to allow setting it dynamically for API's which can't handle it properly

246
oauth.c
View File

@@ -201,6 +201,24 @@ void soo_handle_error(long errorCode, char *msg, char *response TSRMLS_DC) /* {{
}
/* }}} */
static void oauth_prop_hash_dtor(php_so_object *soo TSRMLS_DC) /* {{{ */
{
HashTable *ht;
zval **p_cur;
ht = soo->properties;
for (zend_hash_internal_pointer_reset(ht);
zend_hash_get_current_data(ht, (void **)&p_cur) == SUCCESS;
zend_hash_move_forward(ht)) {
if(Z_TYPE_PP(p_cur)==IS_STRING) {
zval_ptr_dtor(p_cur);
}
}
FREE_ARGS_HASH(ht);
}
/* }}} */
static char *soo_hmac_sha1(char *message, zval *cs, zval *ts TSRMLS_DC) /* {{{ */
{
zval *args[4],*retval,*func;
@@ -336,7 +354,7 @@ static char *generate_sig_base(php_so_object *soo, char *uri, HashTable *post_ar
{
zval *func, *exret2, *exargs2[2];
uint cur_key_len, post_cur_key_len;
ulong num_key, post_num_key;
ulong num_key, post_num_key, oauth_sig_h;
zend_bool prepend_amp = FALSE;
char *query, *cur_key;
char *arg_key = NULL, *post_cur_key = NULL, *auth_type = NULL;
@@ -424,6 +442,10 @@ static char *generate_sig_base(php_so_object *soo, char *uri, HashTable *post_ar
sapi_module.treat_data(PARSE_STRING, query, exargs2[0] TSRMLS_CC);
smart_str_free(&squery);
/* remove oauth_signature if it's in the hash */
oauth_sig_h = zend_hash_func(OAUTH_PARAM_SIGNATURE, sizeof(OAUTH_PARAM_SIGNATURE));
zend_hash_quick_del(Z_ARRVAL_P(exargs2[0]), OAUTH_PARAM_SIGNATURE, sizeof(OAUTH_PARAM_SIGNATURE), oauth_sig_h);
MAKE_STD_ZVAL(exargs2[1]);
ZVAL_STRING(exargs2[1], "strnatcmp", 0);
ZVAL_STRING(func, "uksort", 0);
@@ -552,19 +574,14 @@ static CURLcode make_req(php_so_object *soo, char *url, HashTable *ht, ulong met
zend_bool prepend_amp = FALSE, prepend_comma = FALSE;
char *s_code, *cur_key, *content_type = NULL, *bufz = NULL;
char *auth_type = NULL, *param_name = NULL, *param_val = NULL;
uint cur_key_len;
uint cur_key_len, is_redirect, follow_redirects;
ulong num_key;
smart_str surl = {0}, sheader = {0};
auth_type = Z_STRVAL_PP(soo_get_property(soo, OAUTH_ATTR_AUTHMETHOD TSRMLS_CC));
follow_redirects = Z_LVAL_PP(soo_get_property(soo, OAUTH_ATTR_FOLLOWREDIRECTS TSRMLS_CC));
curl = curl_easy_init();
if(Z_LVAL_PP(soo_get_property(soo, OAUTH_ATTR_FOLLOWREDIRECTS TSRMLS_CC))) {
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, OAUTH_MAX_REDIRS);
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L);
}
if (!strcmp(auth_type, OAUTH_AUTH_TYPE_FORM)) {
for (zend_hash_internal_pointer_reset(ht);
zend_hash_get_current_data(ht, (void **)&p_cur) == SUCCESS;
@@ -683,7 +700,6 @@ static CURLcode make_req(php_so_object *soo, char *url, HashTable *ht, ulong met
#endif
#if LIBCURL_VERSION_NUM >= 0x071304
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, OAUTH_PROTOCOLS_ALLOWED);
curl_easy_setopt(curl, CURLOPT_PROTOCOLS, OAUTH_PROTOCOLS_ALLOWED);
#endif
@@ -703,90 +719,110 @@ static CURLcode make_req(php_so_object *soo, char *url, HashTable *ht, ulong met
crres = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (CURLE_OK == crres && ctres == CURLE_OK) {
ALLOC_INIT_ZVAL(info);
array_init(info);
is_redirect = (response_code > 300 && response_code < 304 && soo->last_location_header);
if(is_redirect && follow_redirects) {
if(soo->redirects >= OAUTH_MAX_REDIRS) {
cres = FAILURE;
spprintf(&bufz, 0, "max redirections exceeded (max: %ld last redirect url: %s)", OAUTH_MAX_REDIRS, soo->last_location_header);
MAKE_STD_ZVAL(zret);
ZVAL_STRING(zret, soo->lastresponse.c, 1)
so_set_response_args(soo->properties, zret, NULL TSRMLS_CC);
soo_handle_error(response_code, bufz, soo->lastresponse.c TSRMLS_CC);
efree(bufz);
} else {
++soo->redirects;
make_standard_query(ht, soo TSRMLS_CC);
oauth_add_signature(soo, soo->last_location_header, ht, NULL TSRMLS_CC);
cres = make_req(soo, soo->last_location_header, ht, method TSRMLS_CC);
}
} else {
ALLOC_INIT_ZVAL(info);
array_init(info);
CAAL("http_code", response_code);
CAAL("http_code", response_code);
if(response_code > 300 && response_code < 304 && soo->last_location_header) {
CAAS("redirect_url", soo->last_location_header);
}
if(is_redirect) {
CAAS("redirect_url", soo->last_location_header);
}
if (content_type != NULL) {
CAAS("content_type", content_type);
}
if (curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {
CAAS("url", s_code);
}
if (content_type != NULL) {
CAAS("content_type", content_type);
}
if (curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {
CAAS("url", s_code);
}
if (curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {
CAAL("header_size", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {
CAAL("request_size", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_FILETIME, &l_code) == CURLE_OK) {
CAAL("filetime", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {
CAAL("ssl_verify_result", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {
CAAL("redirect_count", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME,&d_code) == CURLE_OK) {
CAAD("total_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {
CAAD("namelookup_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {
CAAD("connect_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {
CAAD("pretransfer_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK){
CAAD("size_upload", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK){
CAAD("size_download", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK){
CAAD("speed_download", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK){
CAAD("speed_upload", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {
CAAD("download_content_length", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {
CAAD("upload_content_length", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK){
CAAD("starttransfer_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK){
CAAD("redirect_time", d_code);
}
so_set_response_info(soo->properties, info);
if (curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {
CAAL("header_size", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {
CAAL("request_size", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_FILETIME, &l_code) == CURLE_OK) {
CAAL("filetime", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {
CAAL("ssl_verify_result", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {
CAAL("redirect_count", l_code);
}
if (curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME,&d_code) == CURLE_OK) {
CAAD("total_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {
CAAD("namelookup_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {
CAAD("connect_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {
CAAD("pretransfer_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK){
CAAD("size_upload", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK){
CAAD("size_download", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK){
CAAD("speed_download", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK){
CAAD("speed_upload", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {
CAAD("download_content_length", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {
CAAD("upload_content_length", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK){
CAAD("starttransfer_time", d_code);
}
if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK){
CAAD("redirect_time", d_code);
}
/* XXX maybe we should instead check for specific codes, like 40X */
if (response_code != 200 && !(response_code > 300 && response_code < 304)) {
cres = FAILURE;
spprintf(&bufz, 0, "Invalid auth/bad request (got a %d, expected 200 or a redirect)", (int)response_code);
MAKE_STD_ZVAL(zret);
ZVAL_STRING(zret, soo->lastresponse.c, 1)
so_set_response_args(soo->properties, zret, NULL TSRMLS_CC);
soo_handle_error(response_code, bufz, soo->lastresponse.c TSRMLS_CC);
efree(bufz);
so_set_response_info(soo->properties, info);
if(response_code == 200) {
soo->redirects = 0;
}
/* XXX maybe we should instead check for specific codes, like 40X */
if (response_code != 200 && !(response_code > 300 && response_code < 304)) {
cres = FAILURE;
spprintf(&bufz, 0, "Invalid auth/bad request (got a %d, expected 200 or a redirect)", (int)response_code);
MAKE_STD_ZVAL(zret);
ZVAL_STRING(zret, soo->lastresponse.c, 1)
so_set_response_args(soo->properties, zret, NULL TSRMLS_CC);
soo_handle_error(response_code, bufz, soo->lastresponse.c TSRMLS_CC);
efree(bufz);
}
}
}
} else if(cres == CURLE_TOO_MANY_REDIRECTS) {
cres = FAILURE;
spprintf(&bufz, 0, "Max redirections exceeded (%d allowed)", (int)OAUTH_MAX_REDIRS);
}
}
CLEANUP_CURL_AND_FORM(formdata, curl);
return cres;
}
@@ -831,6 +867,32 @@ static void make_standard_query(HashTable *ht, php_so_object *soo TSRMLS_DC) /*
}
/* }}} */
static int oauth_add_signature(php_so_object *soo, char *uri, HashTable *args, HashTable *extra_args TSRMLS_DC) /* {{{ */
{
char *sbs = NULL, *sig = NULL;
zval **token_secret = NULL, **consumer_secret = NULL;
sbs = generate_sig_base(soo, uri, args, extra_args TSRMLS_CC);
if(!sbs) {
return FAILURE;
}
consumer_secret = soo_get_property(soo, OAUTH_ATTR_CONSUMER_SECRET TSRMLS_CC);
SEPARATE_ZVAL(consumer_secret);
token_secret = soo_get_property(soo, OAUTH_ATTR_TOKEN_SECRET TSRMLS_CC);
sig = soo_hmac_sha1(sbs, *consumer_secret, *token_secret TSRMLS_CC);
efree(sbs);
if(!sig) {
return FAILURE;
}
SO_ADD_SIG(args, sig);
return SUCCESS;
}
/* }}} */
/* {{{ proto string oauth_urlencode(string uri)
URI encoding according to RFC 3986, note: is not utf8 capable until the underlying phpapi is */
PHP_FUNCTION(oauth_urlencode)
@@ -865,6 +927,7 @@ SO_METHOD(__construct)
soo = fetch_so_object(getThis() TSRMLS_CC);
memset(soo->last_location_header, 0, OAUTH_MAX_HEADER_LEN);
soo->redirects = 0;
TSRMLS_SET_CTX(soo->thread_ctx);
@@ -957,14 +1020,17 @@ SO_METHOD(__construct)
SO_METHOD(__destruct)
{
php_so_object *soo;
HashTable *data_ptr;
zval **data_ptr;
HashTable *last_req_info;
ulong h = zend_hash_func(OAUTH_ATTR_LAST_RES_INFO, sizeof(OAUTH_ATTR_LAST_RES_INFO));
soo = fetch_so_object(getThis() TSRMLS_CC);
/* free the response info hash if one exists */
oauth_prop_hash_dtor(soo TSRMLS_CC);
if (zend_hash_quick_find(soo->properties, OAUTH_ATTR_LAST_RES_INFO, sizeof(OAUTH_ATTR_LAST_RES_INFO), h, (void **)&data_ptr) == SUCCESS) {
FREE_ARGS_HASH(data_ptr);
last_req_info = HASH_OF(*data_ptr);
FREE_ARGS_HASH(last_req_info);
}
}
@@ -1026,7 +1092,7 @@ SO_METHOD(getRequestToken)
/* }}} */
/* {{{ proto bool OAuth::enableRedirects()
Follow redirects automatically without resigning the request; this breaks certain providers who expect OAuth headers to be resigned for each request (enabled by default) */
Follow and sign redirects automatically (enabled by default) */
SO_METHOD(enableRedirects)
{
php_so_object *soo;

View File

@@ -38,7 +38,7 @@
#define OAUTH_SIG_METHOD_HMACSHA1 "HMAC-SHA1"
#if LIBCURL_VERSION_NUM >= 0x071304
#define OAUTH_ALLOWED_PROTOCOLS CURLPROTO_HTTP | CURLPROTO_HTTPS
#define OAUTH_PROTOCOLS_ALLOWED CURLPROTO_HTTP | CURLPROTO_HTTPS
#endif
extern zend_module_entry oauth_module_entry;
@@ -118,11 +118,14 @@ typedef struct {
smart_str lastresponse;
void ***thread_ctx;
char last_location_header[OAUTH_MAX_HEADER_LEN];
uint redirects;
} php_so_object;
static inline zval **soo_get_property(php_so_object *soo, char *prop_name TSRMLS_DC);
static int soo_set_nonce(php_so_object *soo TSRMLS_DC);
static inline int soo_set_property(php_so_object *soo, zval *prop, char *prop_name TSRMLS_DC);
static int oauth_add_signature(php_so_object *soo, char *uri, HashTable *args, HashTable *eargs TSRMLS_DC);
static void make_standard_query(HashTable *ht, php_so_object *soo TSRMLS_DC);
#endif