1 /*
2 +----------------------------------------------------------------------+
3 | See LICENSE file for further copyright information                   |
4 +----------------------------------------------------------------------+
5 | Authors: John Jawed <jawed@php.net>                                  |
6 |          Felipe Pena <felipe@php.net>                                |
7 |          Rasmus Lerdorf <rasmus@php.net>                             |
8 |          Tjerk Meesters <datibbaw@php.net>                           |
9 +----------------------------------------------------------------------+
10 */
11 
12 #include "php_oauth.h"
13 #include "provider.h"
14 
15 static zend_class_entry *soo_class_entry;
16 static zend_class_entry *soo_exception_ce;
17 static zend_object_handlers so_object_handlers;
18 
php_so_object_new(zend_class_entry * ce)19 static zend_object* php_so_object_new(zend_class_entry *ce) /* {{{ */
20 {
21 	php_so_object *nos;
22 
23 	nos = ecalloc(1, sizeof(php_so_object) + zend_object_properties_size(ce));
24 	nos->signature = NULL;
25 	nos->timeout = 0;
26 
27 	zend_object_std_init(&nos->zo, ce);
28 	object_properties_init(&nos->zo, ce);
29 
30 	nos->zo.handlers = &so_object_handlers;
31 
32 	return &nos->zo;
33 }
34 /* }}} */
35 
36 #if PHP_VERSION_ID < 80000
oauth_clone_obj(zval * this_ptr)37 static zend_object *oauth_clone_obj(zval *this_ptr) /* {{{ */
38 {
39 	php_so_object *old_obj = Z_SOO_P(this_ptr);
40 #else
41 static zend_object *oauth_clone_obj(zend_object *this_ptr) /* {{{ */
42 {
43 	php_so_object *old_obj = so_object_from_obj(this_ptr);
44 #endif
45 	php_so_object *new_obj = so_object_from_obj(php_so_object_new(old_obj->zo.ce));
46 
47 	zend_objects_clone_members(&new_obj->zo, &old_obj->zo);
48 
49 	return &new_obj->zo;
50 }
51 /* }}} */
52 
53 static int oauth_parse_str(char *params, zval *dest_array) /* {{{ */
54 {
55 	char *res = NULL, *var, *val, *separator = NULL;
56 	char *strtok_buf = NULL;
57 
58 	if (!params) {
59 		return FAILURE;
60 	}
61 
62 	res = params;
63 	separator = (char *) estrdup(PG(arg_separator).input);
64 	var = php_strtok_r(res, separator, &strtok_buf);
65 	while (var) {
66 		val = strchr(var, '=');
67 
68 		if (val) { /* have a value */
69 			int val_len;
70 
71 			*val++ = '\0';
72 			php_url_decode(var, strlen(var));
73 			val_len = php_url_decode(val, strlen(val));
74 			val = estrndup(val, val_len);
75 		} else {
76 			int val_len;
77 
78 			php_url_decode(var, strlen(var));
79 			val_len = 0;
80 			val = estrndup("", val_len);
81 		}
82 		add_assoc_string(dest_array, var, val);
83 		efree(val);
84 		var = php_strtok_r(NULL, separator, &strtok_buf);
85 	}
86 
87 	efree(separator);
88 	return SUCCESS;
89 }
90 /* }}} */
91 
92 static int so_set_response_args(HashTable *hasht, zval *data, zval *retarray) /* {{{ */
93 {
94 	if (data && Z_TYPE_P(data) == IS_STRING) {
95 
96 		if (retarray) {
97 			char *res = NULL;
98 
99 			res = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data));
100 			/* do not use oauth_parse_str here, we want the result to pass through input filters */
101 			sapi_module.treat_data(PARSE_STRING, res, retarray);
102 		}
103 
104 		return (zend_hash_str_update(hasht, OAUTH_RAW_LAST_RES, sizeof(OAUTH_RAW_LAST_RES) -1, data) == NULL) ? FAILURE : SUCCESS;
105 	}
106 	return FAILURE;
107 }
108 /* }}} */
109 
110 static zval *so_set_response_info(HashTable *hasht, zval *info) /* {{{ */
111 {
112 	return zend_hash_str_update(hasht, OAUTH_ATTR_LAST_RES_INFO, sizeof(OAUTH_ATTR_LAST_RES_INFO) - 1, info);
113 }
114 /* }}} */
115 
116 static void oauth_prop_hash_dtor(php_so_object *soo) /* {{{ */
117 {
118 	HashTable *ht;
119 
120 	ht = soo->properties;
121 
122 	FREE_ARGS_HASH(ht);
123 }
124 /* }}} */
125 
126 static void so_object_free_storage(zend_object *obj) /* {{{ */
127 {
128 	php_so_object *soo;
129 
130 	soo = so_object_from_obj(obj);
131 	zend_object_std_dtor(&soo->zo);
132 
133 	if (soo->lastresponse.c) {
134 		smart_string_free(&soo->lastresponse);
135 	}
136 	if (soo->headers_in.c) {
137 		smart_string_free(&soo->headers_in);
138 	}
139 	if (soo->headers_out.c) {
140 		smart_string_free(&soo->headers_out);
141 	}
142 	if (soo->signature) {
143 		zend_string_release(soo->signature);
144 	}
145 
146 	oauth_prop_hash_dtor(soo);
147 
148 	if (soo->debug_info) {
149 		FREE_DEBUG_INFO(soo->debug_info);
150 		if (soo->debug_info->sbs) {
151 			efree(soo->debug_info->sbs);
152 		}
153 		efree(soo->debug_info);
154 		soo->debug_info = NULL;
155 	}
156 
157 	smart_string_free(&soo->headers_in);
158 	if (soo->headers_out.c) {
159 		smart_string_free(&soo->headers_out);
160 	}
161 	if(Z_TYPE(soo->debugArr) != IS_UNDEF) {
162 		zval_ptr_dtor(&soo->debugArr);
163 	}
164 	OAUTH_SIGCTX_FREE(soo->sig_ctx);
165 	if (soo->nonce) {
166 		efree(soo->nonce);
167 	}
168 	if (soo->timestamp) {
169 		efree(soo->timestamp);
170 	}
171 
172 }
173 /* }}} */
174 
175 void soo_handle_error(php_so_object *soo, long errorCode, char *msg, char *response, char *additional_info) /* {{{ */
176 {
177 	zval ex;
178 	zend_class_entry *dex = zend_exception_get_default(), *soox = soo_exception_ce;
179 
180 	object_init_ex(&ex, soox);
181 
182 	if (!errorCode) {
183 		php_error(E_WARNING, "caller did not pass an errorcode!");
184 	} else {
185 		zend_update_property_long(dex, OBJ_FOR_PROP(&ex), "code", sizeof("code")-1, errorCode);
186 	}
187 	if (response) {
188 		zend_update_property_string(dex, OBJ_FOR_PROP(&ex), "lastResponse", sizeof("lastResponse")-1, response);
189 	}
190 	if(soo && soo->debug && Z_TYPE(soo->debugArr) != IS_UNDEF) {
191 		zend_update_property(dex, OBJ_FOR_PROP(&ex), "debugInfo", sizeof("debugInfo") - 1, &soo->debugArr);
192 	}
193 
194 	if(additional_info) {
195 		zend_update_property_string(dex, OBJ_FOR_PROP(&ex), "additionalInfo", sizeof("additionalInfo")-1, additional_info);
196 	}
197 
198 	zend_update_property_string(dex, OBJ_FOR_PROP(&ex), "message", sizeof("message")-1, msg);
199 	zend_throw_exception_object(&ex);
200 }
201 /* }}} */
202 
203 zend_string *soo_sign_hmac(php_so_object *soo, char *message, const char *cs, const char *ts, const oauth_sig_context *ctx) /* {{{ */
204 {
205 	zval args[4], retval, func;
206 	char *tret;
207 	zend_string *result;
208 
209 	ZVAL_STRING(&func, "hash_hmac");
210 
211 	if (!zend_is_callable(&func, 0, NULL)) {
212 		zval_ptr_dtor(&func);
213 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "HMAC signature generation failed, is ext/hash installed?", NULL, NULL);
214 		return NULL;
215 	}
216 
217 	/* cs and ts would at best be empty, so this should be safe ;-) */
218 	spprintf(&tret, 0, "%s&%s", cs, ts);
219 
220 	ZVAL_STRING(&args[0], ctx->hash_algo);
221 	ZVAL_STRING(&args[1], message);
222 	ZVAL_STRING(&args[2], tret);
223 	ZVAL_BOOL(&args[3], 1);
224 
225 	call_user_function(EG(function_table), NULL, &func, &retval, 4, args);
226 	result = php_base64_encode((unsigned char *)Z_STRVAL(retval), Z_STRLEN(retval));
227 
228 	efree(tret);
229 	zval_ptr_dtor(&retval);
230 	zval_ptr_dtor(&func);
231 	zval_ptr_dtor(&args[0]);
232 	zval_ptr_dtor(&args[1]);
233 	zval_ptr_dtor(&args[2]);
234 	zval_ptr_dtor(&args[3]);
235 
236 	return result;
237 }
238 /* }}} */
239 
240 zend_string *soo_sign_rsa(php_so_object *soo, char *message, const oauth_sig_context *ctx) /* {{{ */
241 {
242 	zval args[3], func, retval;
243 	zend_string *result;
244 
245 	/* check for empty private key */
246 	if (Z_TYPE(ctx->privatekey) == IS_UNDEF) {
247 		return NULL;
248 	}
249 
250 	ZVAL_STRING(&func, "openssl_sign");
251 
252 	/* TODO: add support for other algorithms instead of OPENSSL_ALGO_SHA1 */
253 
254 	ZVAL_STRING(&args[0], message);
255 	ZVAL_NULL(&args[1]);
256 	ZVAL_MAKE_REF(&args[1]);
257 	ZVAL_DUP(&args[2], &ctx->privatekey);
258 
259 	call_user_function(EG(function_table), NULL, &func, &retval, 3, args);
260 
261 	if (Z_TYPE(retval) == IS_TRUE || Z_TYPE(retval) == IS_FALSE) {
262 		zend_string *sig_str = zval_get_string(&args[1]);
263 		result = php_base64_encode((unsigned char *) sig_str->val, sig_str->len);
264 		zend_string_release(sig_str);
265 		zval_ptr_dtor(&args[1]);
266 	} else {
267 		result = NULL;
268 	}
269 
270 	zval_ptr_dtor(&retval);
271 	zval_ptr_dtor(&func);
272 	zval_ptr_dtor(&args[0]);
273 
274 	return result;
275 }
276 /* }}} */
277 
278 zend_string *soo_sign_plain(php_so_object *soo, const char *cs, const char *ts) /* {{{ */
279 {
280 	return strpprintf(0, "%s&%s", cs, ts);
281 }
282 /* }}} */
283 
284 oauth_sig_context *oauth_create_sig_context(const char *sigmethod) /* {{{ */
285 {
286 	oauth_sig_context *ctx;
287 
288 	OAUTH_SIGCTX_INIT(ctx);
289 	if (0==strcmp(sigmethod, OAUTH_SIG_METHOD_HMACSHA1)) {
290 		OAUTH_SIGCTX_HMAC(ctx, "sha1");
291 	} else if (0==strcmp(sigmethod, OAUTH_SIG_METHOD_HMACSHA256)) {
292 		OAUTH_SIGCTX_HMAC(ctx, "sha256");
293 	} else if (0==strcmp(sigmethod, OAUTH_SIG_METHOD_RSASHA1)) {
294 		OAUTH_SIGCTX_RSA(ctx, "sha1");
295 	} else if (0==strcmp(sigmethod, OAUTH_SIG_METHOD_PLAINTEXT)) {
296 		OAUTH_SIGCTX_PLAIN(ctx);
297 	}
298 
299 	return ctx;
300 }
301 /* }}} */
302 
303 zend_string *soo_sign(php_so_object *soo, char *message, zval *cs, zval *ts, const oauth_sig_context *ctx) /* {{{ */
304 {
305 	const char *csec = cs?Z_STRVAL_P(cs):"", *tsec = ts?Z_STRVAL_P(ts):"";
306 
307 	if (OAUTH_SIGCTX_TYPE_HMAC==ctx->type) {
308 		return soo_sign_hmac(soo, message, csec, tsec, ctx);
309 	} else if (OAUTH_SIGCTX_TYPE_RSA==ctx->type) {
310 		return soo_sign_rsa(soo, message, ctx);
311 	} else if(OAUTH_SIGCTX_TYPE_PLAIN==ctx->type) {
312 		return soo_sign_plain(soo, csec, tsec);
313 	}
314 	return NULL;
315 }
316 /* }}} */
317 
318 static inline zval *soo_get_property(php_so_object *soo, char *prop_name) /* {{{ */
319 {
320 	return zend_hash_str_find(soo->properties, prop_name, strlen(prop_name));
321 }
322 /* }}} */
323 
324 /* XXX for auth type, need to make sure that the auth type is actually supported before setting */
325 static inline int soo_set_property(php_so_object *soo, zval *prop, char *prop_name) /* {{{ */
326 {
327 	return (zend_hash_str_update(soo->properties, prop_name, strlen(prop_name), prop) == NULL) ? FAILURE : SUCCESS;
328 }
329 /* }}} */
330 
331 zend_string *oauth_url_encode(char *url, int url_len) /* {{{ */
332 {
333 	zend_string *urlencoded = NULL;
334 	zend_string *ret = NULL;
335 
336 	if (url) {
337 		if (url_len < 0) {
338 			url_len = strlen(url);
339 		}
340 		urlencoded = php_raw_url_encode(url, url_len);
341 	}
342 
343 	if (urlencoded) {
344 		ret = php_str_to_str(ZSTR_VAL(urlencoded), ZSTR_LEN(urlencoded), "%7E", sizeof("%7E")-1, "~", sizeof("~")-1);
345 		zend_string_free(urlencoded);
346 		return ret;
347 	}
348 	return NULL;
349 }
350 /* }}} */
351 
352 zend_string *oauth_http_encode_value(zval *v) /* {{{ */
353 {
354 	zend_string *param_value = NULL;
355 
356 	switch (Z_TYPE_P(v)) {
357 		case IS_STRING:
358 			param_value = oauth_url_encode(Z_STRVAL_P(v), Z_STRLEN_P(v));
359 			break;
360 		default:
361 			SEPARATE_ZVAL(v);
362 			convert_to_string_ex(v);
363 			param_value = oauth_url_encode(Z_STRVAL_P(v), Z_STRLEN_P(v));
364 	}
365 
366 	return param_value;
367 }
368 /* }}} */
369 
370 static int oauth_strcmp(zval *first, zval *second) /* {{{ */
371 {
372 	int result;
373 	result = string_compare_function(first, second);
374 
375 	if (result < 0) {
376 		return -1;
377 	} else if (result > 0) {
378 		return 1;
379 	}
380 
381 	return 0;
382 }
383 /* }}} */
384 
385 #if PHP_VERSION_ID < 80000
386 static int oauth_compare_value(const void *a, const void *b) /* {{{ */
387 {
388 	Bucket *f, *s;
389 	f = (Bucket *)a;
390 	s = (Bucket *)b;
391 
392 #else
393 static int oauth_compare_value(Bucket *f, Bucket *s) /* {{{ */
394 {
395 #endif
396 	return oauth_strcmp(&f->val, &s->val);
397 }
398 /* }}} */
399 
400 #if PHP_VERSION_ID < 80000
401 static int oauth_compare_key(const void *a, const void *b) /* {{{ */
402 {
403     Bucket *f = (Bucket *)a, *s = (Bucket *)b;
404 #else
405 static int oauth_compare_key(Bucket *f, Bucket *s) /* {{{ */
406 {
407 #endif
408 	zval first, second;
409 	int result;
410 
411 	if (f->key == NULL) {
412 		ZVAL_LONG(&first, f->h);
413 	} else {
414 		ZVAL_STRINGL(&first, ZSTR_VAL(f->key), ZSTR_LEN(f->key));
415 	}
416 
417 	if (s->key == NULL) {
418 		ZVAL_LONG(&second, s->h);
419 	} else {
420 		ZVAL_STRINGL(&second, ZSTR_VAL(s->key), ZSTR_LEN(s->key));
421 	}
422 
423 	result = oauth_strcmp(&first, &second);
424 	zval_ptr_dtor(&first);
425 	zval_ptr_dtor(&second);
426 	return result;
427 }
428 /* }}} */
429 
430 /* build url-encoded string from args, optionally starting with & */
431 int oauth_http_build_query(php_so_object *soo, smart_string *s, HashTable *args, zend_bool prepend_amp) /* {{{ */
432 {
433 	zval *cur_val;
434 	zend_string *cur_key, *arg_key, *param_value;
435 	int numargs = 0, hash_key_type, skip_append = 0, i, found;
436 	zend_ulong num_index;
437 	HashPosition pos;
438 	smart_string keyname;
439 
440 	smart_string_0(s);
441 	if (args) {
442 		if (soo && !soo->is_multipart) {
443 			for (zend_hash_internal_pointer_reset_ex(args, &pos);
444 				 HASH_KEY_NON_EXISTENT != (hash_key_type = zend_hash_get_current_key_ex(args, &cur_key, &num_index, &pos));
445 				 zend_hash_move_forward_ex(args, &pos)) {
446 				cur_val = zend_hash_get_current_data_ex(args, &pos);
447 				if (hash_key_type == HASH_KEY_IS_STRING &&
448 					*ZSTR_VAL(cur_key) =='@' && *Z_STRVAL_P(cur_val) =='@') {
449 					soo->is_multipart = 1;
450 					break;
451 				}
452 			}
453 		}
454 
455 		for (zend_hash_internal_pointer_reset_ex(args, &pos);
456 				HASH_KEY_NON_EXISTENT != (hash_key_type = zend_hash_get_current_key_ex(args, &cur_key, &num_index, &pos));
457 				zend_hash_move_forward_ex(args, &pos)) {
458 			(cur_val = zend_hash_get_current_data_ex(args, &pos));
459 
460 			skip_append = 0;
461 
462 			switch (hash_key_type) {
463 				case HASH_KEY_IS_STRING:
464 					if (soo && soo->is_multipart && strncmp(ZSTR_VAL(cur_key), "oauth_", 6) != 0) {
465 						found = 0;
466 						for (i=0; i<soo->multipart_files_num; ++i) {
467 							if (0 == strcmp(soo->multipart_params[i], ZSTR_VAL(cur_key))) {
468 								found = 1;
469 								break;
470 							}
471 						}
472 
473 						if (found) {
474 							continue;
475 						}
476 
477 						soo->multipart_files = erealloc(soo->multipart_files, sizeof(char *) * (soo->multipart_files_num + 1));
478 						soo->multipart_params = erealloc(soo->multipart_params, sizeof(char *) * (soo->multipart_files_num + 1));
479 
480 						convert_to_string_ex(cur_val);
481 						soo->multipart_files[soo->multipart_files_num] = Z_STRVAL_P(cur_val);
482 						soo->multipart_params[soo->multipart_files_num] = ZSTR_VAL(cur_key);
483 
484 						++soo->multipart_files_num;
485 						/* we don't add multipart files to the params */
486 						skip_append = 1;
487 					} else {
488 						arg_key = oauth_url_encode(ZSTR_VAL(cur_key), ZSTR_LEN(cur_key));
489 					}
490 					break;
491 
492 				case HASH_KEY_IS_LONG:
493 					/* take value of num_index instead */
494 					arg_key = NULL;
495 					break;
496 
497 				default:
498 					continue;
499 			}
500 
501 			if (skip_append) {
502 				continue;
503 			}
504 
505 			INIT_smart_string(keyname);
506 			if (arg_key) {
507 				smart_string_appends(&keyname, ZSTR_VAL(arg_key));
508 				zend_string_release(arg_key);
509 			} else {
510 				smart_string_append_unsigned(&keyname, num_index);
511 			}
512 			if (IS_ARRAY == Z_TYPE_P(cur_val)) {
513 				HashPosition val_pos;
514 				zval *val_cur_val;
515 
516 				/* make shallow copy */
517 				SEPARATE_ZVAL(cur_val);
518 				/* sort array based on string comparison */
519 				zend_hash_sort(Z_ARRVAL_P(cur_val), oauth_compare_value, 1);
520 
521 				/* traverse array */
522 				zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(cur_val), &val_pos);
523 				while ((val_cur_val = zend_hash_get_current_data_ex(Z_ARRVAL_P(cur_val), &val_pos)) != NULL) {
524 					if (prepend_amp) {
525 						smart_string_appendc(s, '&');
526 					}
527 
528 					smart_string_append(s, &keyname);
529 					param_value = oauth_http_encode_value(val_cur_val);
530 					if (param_value) {
531 						smart_string_appendc(s, '=');
532 						smart_string_appends(s, ZSTR_VAL(param_value));
533 						zend_string_release(param_value);
534 					}
535 					prepend_amp = TRUE;
536 					++numargs;
537 					zend_hash_move_forward_ex(Z_ARRVAL_P(cur_val), &val_pos);
538 				}
539 				/* clean up */
540 			} else {
541 				if (prepend_amp) {
542 					smart_string_appendc(s, '&');
543 				}
544 				smart_string_append(s, &keyname);
545 				param_value = oauth_http_encode_value(cur_val);
546 				if (param_value) {
547 					smart_string_appendc(s, '=');
548 					smart_string_appends(s, ZSTR_VAL(param_value));
549 					zend_string_release(param_value);
550 				}
551 				prepend_amp = TRUE;
552 				++numargs;
553 			}
554 			smart_string_free(&keyname);
555 
556 			smart_string_0(s);
557 		}
558 	}
559 	return numargs;
560 }
561 /* }}} */
562 
563 /* retrieves parameter value from the _GET or _POST superglobal */
564 void get_request_param(char *arg_name, char **return_val, int *return_len) /* {{{ */
565 {
566 	zval *ptr;
567 	if (
568 	    (Z_TYPE(PG(http_globals)[TRACK_VARS_GET]) != IS_UNDEF && (ptr = zend_hash_str_find(HASH_OF(&(PG(http_globals)[TRACK_VARS_GET])), arg_name, strlen(arg_name)))  != NULL && IS_STRING == Z_TYPE_P(ptr)) ||
569 	    (Z_TYPE(PG(http_globals)[TRACK_VARS_POST])!= IS_UNDEF && (ptr = zend_hash_str_find(HASH_OF(&(PG(http_globals)[TRACK_VARS_POST])), arg_name, strlen(arg_name))) != NULL && IS_STRING == Z_TYPE_P(ptr))
570 	   ) {
571 		*return_val = Z_STRVAL_P(ptr);
572 		*return_len = Z_STRLEN_P(ptr);
573 		return;
574 	}
575 	*return_val = NULL;
576 	*return_len = 0;
577 }
578 /* }}} */
579 
580 /*
581  * This function does not currently care to respect parameter precedence, in the sense that if a common param is defined
582  * in POST/GET or Authorization header, the precendence is defined by: OAuth Core 1.0 section 9.1.1
583  */
584 zend_string *oauth_generate_sig_base(php_so_object *soo, const char *http_method, const char *uri, HashTable *post_args, HashTable *extra_args) /* {{{ */
585 {
586 	zval params;
587 	char *query;
588 	char *s_port = NULL;
589 	zend_string *bufz = NULL;
590 	zend_string *sbs_query_part = NULL, *sbs_scheme_part = NULL;
591 	php_url *urlparts;
592 	smart_string sbuf = {0};
593 
594 	urlparts = php_url_parse_ex(uri, strlen(uri));
595 
596 	if (urlparts) {
597 		if (!urlparts->host || !urlparts->scheme) {
598 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid url when trying to build base signature string", NULL, NULL);
599 			php_url_free(urlparts);
600 			return NULL;
601 		}
602 		php_strtolower(OAUTH_URL_STR(urlparts->scheme), OAUTH_URL_LEN(urlparts->scheme));
603 		php_strtolower(OAUTH_URL_STR(urlparts->host), OAUTH_URL_LEN(urlparts->host));
604 		smart_string_appends(&sbuf, OAUTH_URL_STR(urlparts->scheme));
605 		smart_string_appends(&sbuf, "://");
606 		smart_string_appends(&sbuf, OAUTH_URL_STR(urlparts->host));
607 
608 		if (urlparts->port && ((!strcmp("http", OAUTH_URL_STR(urlparts->scheme)) && OAUTH_HTTP_PORT != urlparts->port)
609 					|| (!strcmp("https", OAUTH_URL_STR(urlparts->scheme)) && OAUTH_HTTPS_PORT != urlparts->port))) {
610 			spprintf(&s_port, 0, "%d", urlparts->port);
611 			smart_string_appendc(&sbuf, ':');
612 			smart_string_appends(&sbuf, s_port);
613 			efree(s_port);
614 		}
615 
616 		if (urlparts->path) {
617 			smart_string squery = {0};
618 			smart_string_appends(&sbuf, OAUTH_URL_STR(urlparts->path));
619 			smart_string_0(&sbuf);
620 
621 			array_init(&params);
622 
623 			/* merge order = oauth_args - extra_args - query */
624 			if (post_args) {
625 				zend_hash_merge(Z_ARRVAL(params), post_args, (copy_ctor_func_t) zval_add_ref, 0);
626 			}
627 
628 			if (extra_args) {
629 				zend_hash_merge(Z_ARRVAL(params), extra_args, (copy_ctor_func_t) zval_add_ref, 0);
630 			}
631 
632 			if (urlparts->query) {
633 				query = estrdup(OAUTH_URL_STR(urlparts->query));
634 				oauth_parse_str(query, &params);
635 				efree(query);
636 			}
637 
638 			/* remove oauth_signature if it's in the hash */
639 			zend_hash_str_del(Z_ARRVAL(params), OAUTH_PARAM_SIGNATURE, sizeof(OAUTH_PARAM_SIGNATURE) - 1);
640 
641 			/* exret2 = uksort(&exargs2[0], "strnatcmp") */
642 			zend_hash_sort(Z_ARRVAL(params), oauth_compare_key, 0);
643 
644 			oauth_http_build_query(soo, &squery, Z_ARRVAL(params), FALSE);
645 			smart_string_0(&squery);
646 			zval_ptr_dtor(&params);
647 
648 			sbs_query_part = oauth_url_encode(squery.c, squery.len);
649 			sbs_scheme_part = oauth_url_encode(sbuf.c, sbuf.len);
650 
651 			bufz = strpprintf(0, "%s&%s&%s", http_method, ZSTR_VAL(sbs_scheme_part), sbs_query_part ? ZSTR_VAL(sbs_query_part) : "");
652 			/* TODO move this into oauth_get_http_method()
653 			   soo_handle_error(OAUTH_ERR_INTERNAL_ERROR, "Invalid auth type", NULL);
654 			   */
655 			if(sbs_query_part) {
656 				zend_string_release(sbs_query_part);
657 			}
658 			if(sbs_scheme_part) {
659 				zend_string_release(sbs_scheme_part);
660 			}
661 			smart_string_free(&squery);
662 		} else {
663 			/* Bug 22630 - throw exception if no path given */
664 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid path (perhaps you only specified the hostname? try adding a slash at the end)", NULL, NULL);
665 			return NULL;
666 		}
667 
668 		smart_string_free(&sbuf);
669 
670 		php_url_free(urlparts);
671 
672 		if(soo && soo->debug) {
673 			if(soo->debug_info->sbs) {
674 				zend_string_release(soo->debug_info->sbs);
675 			}
676 
677 			if (bufz) {
678 				soo->debug_info->sbs = bufz;
679 				zend_string_addref(soo->debug_info->sbs);
680 			} else {
681 				soo->debug_info->sbs = NULL;
682 			}
683 		}
684 		return bufz;
685 	}
686 	return NULL;
687 }
688 /* }}} */
689 
690 static void oauth_set_debug_info(php_so_object *soo) /* {{{ */
691 {
692 	zval *debugInfo;
693 	if (soo->debug_info) {
694 		debugInfo = &soo->debugArr;
695 
696 		if (Z_TYPE_P(debugInfo) != IS_UNDEF) {
697 			zval_ptr_dtor(debugInfo);
698 		}
699 		array_init(debugInfo);
700 
701 		if(soo->debug_info->sbs) {
702 			add_assoc_string(debugInfo, "sbs", ZSTR_VAL(soo->debug_info->sbs));
703 		}
704 
705 		ADD_DEBUG_INFO(debugInfo, "headers_sent", soo->debug_info->headers_out, 1);
706 		ADD_DEBUG_INFO(debugInfo, "headers_recv", soo->headers_in, 1);
707 		ADD_DEBUG_INFO(debugInfo, "body_sent", soo->debug_info->body_out, 0);
708 		ADD_DEBUG_INFO(debugInfo, "body_recv", soo->debug_info->body_in, 0);
709 		ADD_DEBUG_INFO(debugInfo, "info", soo->debug_info->curl_info, 0);
710 
711 		zend_update_property(soo_class_entry, OBJ_FOR_PROP(soo->this_ptr), "debugInfo", sizeof("debugInfo") - 1, debugInfo);
712 	} else {
713 		ZVAL_UNDEF(&soo->debugArr);
714 	}
715 }
716 /* }}} */
717 
718 static int add_arg_for_req(HashTable *ht, const char *arg, const char *val) /* {{{ */
719 {
720 	zval varg;
721 
722 	ZVAL_STRING(&varg, (char *)val);
723 	zend_hash_str_update(ht, (char *)arg, strlen(arg), &varg);
724 
725 	return SUCCESS;
726 }
727 /* }}} */
728 
729 void oauth_add_signature_header(HashTable *request_headers, HashTable *oauth_args, smart_string *header) /* {{{ */
730 {
731 	smart_string sheader = {0};
732 	zend_bool prepend_comma = FALSE;
733 
734 	zval *curval;
735 	zend_string *param_name, *param_val;
736 	zend_string *cur_key;
737 	zend_ulong num_key;
738 	HashPosition pos;
739 
740 	smart_string_appends(&sheader, "OAuth ");
741 
742 	for (zend_hash_internal_pointer_reset_ex(oauth_args, &pos);
743 			(curval = zend_hash_get_current_data_ex(oauth_args, &pos)) != NULL;
744 				zend_hash_move_forward_ex(oauth_args, &pos)) {
745 		zend_hash_get_current_key_ex(oauth_args, &cur_key, &num_key, &pos);
746 
747 		if (prepend_comma) {
748 			smart_string_appendc(&sheader, ',');
749 		}
750 		param_name = oauth_url_encode(ZSTR_VAL(cur_key), ZSTR_LEN(cur_key));
751 		param_val = oauth_url_encode(Z_STRVAL_P(curval), Z_STRLEN_P(curval));
752 
753 		smart_string_appends(&sheader, ZSTR_VAL(param_name));
754 		smart_string_appendc(&sheader, '=');
755 		smart_string_appends(&sheader, "\"");
756 		smart_string_appends(&sheader, ZSTR_VAL(param_val));
757 		smart_string_appends(&sheader, "\"");
758 
759 		efree(param_name);
760 		efree(param_val);
761 		prepend_comma = TRUE;
762 	}
763 	smart_string_0(&sheader);
764 
765 	if (!header) {
766 		add_arg_for_req(request_headers, "Authorization", sheader.c);
767 	} else {
768 		smart_string_appends(header, sheader.c);
769 	}
770 	smart_string_free(&sheader);
771 }
772 /* }}} */
773 
774 #define HTTP_RESPONSE_CAAS(zvalp, header, storkey) { \
775 	if (0==strncasecmp(Z_STRVAL_P(zvalp),header,sizeof(header)-1)) { \
776 		CAAS(storkey, (Z_STRVAL_P(zvalp)+sizeof(header)-1)); \
777 	} \
778 }
779 
780 #define HTTP_RESPONSE_CAAD(zvalp, header, storkey) { \
781 	if (0==strncasecmp(Z_STRVAL_P(zvalp),header,sizeof(header)-1)) { \
782 		CAAD(storkey, strtoul(Z_STRVAL_P(zvalp)+sizeof(header)-1,NULL,10)); \
783 	} \
784 }
785 
786 #define HTTP_RESPONSE_CODE(zvalp) \
787 	if (response_code < 0 && 0==strncasecmp(Z_STRVAL_P(zvalp),"HTTP/", 5) && Z_STRLEN_P(zvalp)>=12) { \
788 		response_code = strtol(Z_STRVAL_P(zvalp)+9, NULL, 10); \
789 		CAAL("http_code", response_code); \
790 	}
791 
792 #define HTTP_RESPONSE_LOCATION(zvalp) \
793 	if (0==strncasecmp(Z_STRVAL_P(zvalp), "Location: ", 10)) { \
794 		strlcpy(soo->last_location_header, Z_STRVAL_P(zvalp)+10, OAUTH_MAX_HEADER_LEN); \
795 	}
796 
797 static long make_req_streams(php_so_object *soo, const char *url, const smart_string *payload, const char *http_method, HashTable *request_headers) /* {{{ */
798 {
799 	php_stream_context *sc;
800 	zval zpayload, zmethod, zredirects, zerrign;
801 	long response_code = -1;
802 	php_stream *s;
803 	int set_form_content_type = 0;
804 	php_netstream_data_t *sock;
805 	struct timeval tv;
806 	int secs = 0;
807 
808 	sc = php_stream_context_alloc();
809 
810 	if (payload->len) {
811 		ZVAL_STRINGL(&zpayload, payload->c, payload->len);
812 		Z_STRVAL(zpayload)[Z_STRLEN(zpayload)] = '\0';
813 		php_stream_context_set_option(sc, "http", "content", &zpayload);
814 		zval_ptr_dtor(&zpayload);
815 		/**
816 		 * remember to set application/x-www-form-urlencoded content-type later on
817 		 * lest the php streams guys come and beat you up
818 		*/
819 		set_form_content_type = 1;
820 	}
821 
822 	if (request_headers) {
823 		HashPosition pos;
824 		zval *cur_val, zheaders;
825 		zend_string *cur_key;
826 		zend_ulong num_key;
827 		smart_string sheaders = {0};
828 		int first = 1;
829 
830 		for (zend_hash_internal_pointer_reset_ex(request_headers, &pos);
831 				(cur_val = zend_hash_get_current_data_ex(request_headers, &pos)) != NULL;
832 				zend_hash_move_forward_ex(request_headers, &pos)) {
833 			/* check if a string based key is used */
834 			smart_string sheaderline = {0};
835 			switch ((int)zend_hash_get_current_key_ex(request_headers, &cur_key, &num_key, &pos)) {
836 				case HASH_KEY_IS_STRING:
837 					smart_string_appendl(&sheaderline, ZSTR_VAL(cur_key), ZSTR_LEN(cur_key));
838 					break;
839 				default:
840 					continue;
841 			}
842 			smart_string_0(&sheaderline);
843 			if (!strcasecmp(sheaderline.c,"content-type")) {
844 				set_form_content_type = 0;
845 			}
846 			smart_string_appends(&sheaderline, ": ");
847 			switch (Z_TYPE_P(cur_val)) {
848 				case IS_STRING:
849 					smart_string_appendl(&sheaderline, Z_STRVAL_P(cur_val), Z_STRLEN_P(cur_val));
850 					break;
851 				default:
852 					smart_string_free(&sheaderline);
853 					continue;
854 			}
855 			if (!first) {
856 				smart_string_appends(&sheaders, "\r\n");
857 			} else {
858 				first = 0;
859 			}
860 			smart_string_append(&sheaders, &sheaderline);
861 			smart_string_free(&sheaderline);
862 		}
863 		if (set_form_content_type) {
864 			/* still need to add our own content-type? */
865 			if (!first) {
866 				smart_string_appends(&sheaders, "\r\n");
867 			}
868 			smart_string_appends(&sheaders, "Content-Type: application/x-www-form-urlencoded");
869 		}
870 		if (sheaders.len) {
871 			smart_string_0(&sheaders);
872 			ZVAL_STRINGL(&zheaders, sheaders.c, sheaders.len);
873 			php_stream_context_set_option(sc, "http", "header", &zheaders);
874 			zval_ptr_dtor(&zheaders);
875 			if (soo->debug) {
876 				smart_string_append(&soo->debug_info->headers_out, &sheaders);
877 			}
878 		}
879 		smart_string_free(&sheaders);
880 	}
881 	/* set method */
882 	ZVAL_STRING(&zmethod, http_method);
883 	php_stream_context_set_option(sc, "http", "method", &zmethod);
884 	zval_ptr_dtor(&zmethod);
885 	/* set maximum redirects; who came up with the ridiculous logic of <= 1 means no redirects ?? */
886 	ZVAL_LONG(&zredirects, 1L);
887 	php_stream_context_set_option(sc, "http", "max_redirects", &zredirects);
888 	/* using special extension to treat redirects as regular document (requires patch in php) */
889 	ZVAL_BOOL(&zerrign, TRUE);
890 	php_stream_context_set_option(sc, "http", "ignore_errors", &zerrign);
891 
892 	smart_string_free(&soo->lastresponse);
893 	smart_string_free(&soo->headers_in);
894 
895 	if ((s = php_stream_open_wrapper_ex((char*)url, "rb", REPORT_ERRORS, NULL, sc))) {
896 		zval info;
897 		zend_string *buf;
898 		size_t rb = 0;
899 
900 		array_init(&info);
901 
902 		CAAS("url", url);
903 
904 		if (Z_TYPE(s->wrapperdata) != IS_UNDEF) {
905 			zval *tmp;
906 			HashPosition pos;
907 
908 			zend_hash_internal_pointer_reset_ex(Z_ARRVAL(s->wrapperdata), &pos);
909 			while ((tmp = zend_hash_get_current_data_ex(Z_ARRVAL(s->wrapperdata), &pos)) != NULL) {
910 				smart_string_appendl(&soo->headers_in, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
911 				smart_string_appends(&soo->headers_in, "\r\n");
912 				HTTP_RESPONSE_CODE(tmp);
913 				HTTP_RESPONSE_LOCATION(tmp);
914 				HTTP_RESPONSE_CAAS(tmp, "Content-Type: ", "content_type");
915 				HTTP_RESPONSE_CAAD(tmp, "Content-Length: ", "download_content_length");
916 				zend_hash_move_forward_ex(Z_ARRVAL(s->wrapperdata), &pos);
917 			}
918 			if (HTTP_IS_REDIRECT(response_code) && soo->last_location_header) {
919 				CAAS("redirect_url", soo->last_location_header);
920 			}
921 		}
922 
923 		if(soo->timeout) {
924 			sock = (php_netstream_data_t*)s->abstract;
925 			secs = soo->timeout / 1000;
926 			tv.tv_sec = secs;
927 			tv.tv_usec = ((soo->timeout - (secs * 1000)) * 1000) % 1000000;
928 			sock->timeout = tv;
929 		}
930 
931 		if ((buf = php_stream_copy_to_mem(s, PHP_STREAM_COPY_ALL, 0)) != NULL) {
932 			smart_string_appendl(&soo->lastresponse, ZSTR_VAL(buf), ZSTR_LEN(buf));
933 			rb = ZSTR_LEN(buf);
934 			zend_string_release(buf);
935 		}
936 		smart_string_0(&soo->lastresponse);
937 		smart_string_0(&soo->headers_in);
938 
939 		CAAD("size_download", rb);
940 		CAAD("size_upload", payload->len);
941 
942 		so_set_response_info(soo->properties, &info);
943 
944 		php_stream_close(s);
945 	} else {
946 		char *bufz;
947 
948 		spprintf(&bufz, 0, "making the request failed (%s)", "dunno why");
949 		soo_handle_error(soo, -1, bufz, soo->lastresponse.c, NULL);
950 		efree(bufz);
951 	}
952 
953 	if(soo->debug) {
954 		smart_string_append(&soo->debug_info->body_in, &soo->lastresponse);
955 		smart_string_append(&soo->debug_info->body_out, payload);
956 	}
957 
958 	return response_code;
959 }
960 /* }}} */
961 
962 #if OAUTH_USE_CURL
963 static size_t soo_read_response(char *ptr, size_t size, size_t nmemb, void *ctx) /* {{{ */
964 {
965 	size_t relsize;
966 	php_so_object *soo = (php_so_object *)ctx;
967 
968 	relsize = size * nmemb;
969 	smart_string_appendl(&soo->lastresponse, ptr, relsize);
970 
971 	return relsize;
972 }
973 /* }}} */
974 
975 int oauth_debug_handler(CURL *ch, curl_infotype type, char *data, size_t data_len, void *ctx) /* {{{ */
976 {
977 	php_so_debug *sdbg;
978 	char *z_data = NULL;
979 	smart_string *dest;
980 
981 	if(data_len > 1 && data[0]=='\r' && data[1]=='\n') { /* ignore \r\n */
982 		return 0;
983 	}
984 
985 	sdbg = (php_so_debug *)ctx;
986 	z_data = emalloc(data_len + 2);
987 	memset(z_data, 0, data_len + 2);
988 	memcpy(z_data, data, data_len);
989 	z_data[data_len] = '\0';
990 
991 	switch(type) {
992 		case CURLINFO_TEXT:
993 			dest = &sdbg->curl_info;
994 			break;
995 		case CURLINFO_HEADER_OUT:
996 			dest = &sdbg->headers_out;
997 			break;
998 		case CURLINFO_DATA_IN:
999 			dest = &sdbg->body_in;
1000 			break;
1001 		case CURLINFO_DATA_OUT:
1002 			dest = &sdbg->body_out;
1003 			break;
1004 		default:
1005 			dest = NULL;
1006 	}
1007 
1008 	if(dest) {
1009 		smart_string_appends(dest, z_data);
1010 	}
1011 	efree(z_data);
1012 
1013 	return 0;
1014 }
1015 /* }}} */
1016 
1017 static size_t soo_read_header(void *ptr, size_t size, size_t nmemb, void *ctx) /* {{{ */
1018 {
1019 	char *header;
1020 	size_t hlen, vpos = sizeof("Location:") - 1;
1021 	php_so_object *soo;
1022 
1023 	header = (char *)ptr;
1024 	hlen = nmemb * size;
1025 	soo = (php_so_object *)ctx;
1026 
1027 	/* handle Location header */
1028 	if (hlen > vpos && 0==strncasecmp(header, "Location:", vpos)) {
1029 		size_t eol = hlen;
1030 		/* find value start */
1031 		while (vpos != eol && ' '==header[vpos]) {
1032 			++vpos;
1033 		}
1034 		/* POST: vpos == eol OR vpos < eol => value start found */
1035 		while (vpos != eol && strchr("\r\n\0", header[eol - 1])) {
1036 			--eol;
1037 		}
1038 		/* POST: vpos == eol OR vpos < eol => value end found */
1039 		if (vpos != eol) {
1040 			if (eol - vpos >= OAUTH_MAX_HEADER_LEN) {
1041 				eol = vpos + OAUTH_MAX_HEADER_LEN - 1;
1042 			}
1043 			/* POST: eol - vpos <= OAUTH_MAX_HEADER_LEN */
1044 			strncpy(soo->last_location_header, header + vpos, eol - vpos);
1045 		}
1046 		soo->last_location_header[eol - vpos] = '\0';
1047 	}
1048 	if(strncasecmp(header, "\r\n", 2)) {
1049 		smart_string_appendl(&soo->headers_in, header, hlen);
1050 	}
1051 	return hlen;
1052 }
1053 /* }}} */
1054 
1055 long make_req_curl(php_so_object *soo, const char *url, const smart_string *payload, const char *http_method, HashTable *request_headers) /* {{{ */
1056 {
1057 	CURLcode cres, ctres, crres;
1058 	CURL *curl;
1059 	struct curl_slist *curl_headers = NULL;
1060 	long l_code, response_code = -1;
1061 	double d_code;
1062 	zval info, *zca_info, *zca_path, *cur_val;
1063 	char *s_code, *content_type = NULL, *bufz = NULL;
1064 	uint32_t sslcheck;
1065 	zend_ulong num_key;
1066 	smart_string sheader = {0};
1067 	zend_string *cur_key;
1068 	HashPosition pos;
1069 
1070 	zca_info = soo_get_property(soo, OAUTH_ATTR_CA_INFO);
1071 	zca_path = soo_get_property(soo, OAUTH_ATTR_CA_PATH);
1072 	sslcheck = soo->sslcheck;
1073 
1074 	curl = curl_easy_init();
1075 
1076 	if (request_headers) {
1077 		for (zend_hash_internal_pointer_reset_ex(request_headers, &pos);
1078 				(cur_val = zend_hash_get_current_data_ex(request_headers, &pos)) != NULL;
1079 				zend_hash_move_forward_ex(request_headers, &pos)) {
1080 			/* check if a string based key is used */
1081 			switch ((int)zend_hash_get_current_key_ex(request_headers, &cur_key, &num_key, &pos)) {
1082 				case HASH_KEY_IS_STRING:
1083 					smart_string_appendl(&sheader, ZSTR_VAL(cur_key), ZSTR_LEN(cur_key));
1084 					break;
1085 				default:
1086 					continue;
1087 			}
1088 			smart_string_appends(&sheader, ": ");
1089 			switch (Z_TYPE_P(cur_val)) {
1090 				case IS_STRING:
1091 					smart_string_appendl(&sheader, Z_STRVAL_P(cur_val), Z_STRLEN_P(cur_val));
1092 					break;
1093 				default:
1094 					smart_string_free(&sheader);
1095 					continue;
1096 			}
1097 
1098 			smart_string_0(&sheader);
1099 			curl_headers = curl_slist_append(curl_headers, sheader.c);
1100 			smart_string_free(&sheader);
1101 		}
1102 	}
1103 
1104 	if(soo->is_multipart) {
1105 		struct curl_httppost *ff = NULL;
1106 		struct curl_httppost *lf = NULL;
1107 		int i;
1108 
1109 		for(i=0; i < soo->multipart_files_num; i++) {
1110 			char *type, *filename, *postval;
1111 
1112 			/* swiped from ext/curl/interface.c to help with consistency */
1113 			postval = estrdup(soo->multipart_files[i]);
1114 
1115 			if (postval[0] == '@' && soo->multipart_params[i][0] == '@') {
1116 				/* :< (chomp) @ */
1117 				++soo->multipart_params[i];
1118 				++postval;
1119 
1120 				if((type = (char *) php_memnstr(postval, ";type=", sizeof(";type=") - 1, postval + strlen(soo->multipart_files[i]) - 1))) {
1121 					*type = '\0';
1122 				}
1123 				if((filename = (char *) php_memnstr(postval, ";filename=", sizeof(";filename=") - 1, postval + strlen(soo->multipart_files[i]) - 1))) {
1124 					*filename = '\0';
1125 				}
1126 
1127 				/* open_basedir check */
1128 				if(php_check_open_basedir(postval)) {
1129 					char *em;
1130 					spprintf(&em, 0, "failed to open file for multipart request: %s", postval);
1131 					soo_handle_error(soo, -1, em, NULL, NULL);
1132 					efree(em);
1133 					return 1;
1134 				}
1135 
1136 				curl_formadd(&ff, &lf,
1137 							 CURLFORM_COPYNAME, soo->multipart_params[i],
1138 							 CURLFORM_NAMELENGTH, (long)strlen(soo->multipart_params[i]),
1139 							 CURLFORM_FILENAME, filename ? filename + sizeof(";filename=") - 1 : soo->multipart_files[i],
1140 							 CURLFORM_CONTENTTYPE, type ? type + sizeof(";type=") - 1 : "application/octet-stream",
1141 							 CURLFORM_FILE, postval,
1142 							 CURLFORM_END);
1143 			} else {
1144 				curl_formadd(&ff, &lf,
1145 							 CURLFORM_COPYNAME, soo->multipart_params[i],
1146 							 CURLFORM_NAMELENGTH, (long)strlen(soo->multipart_params[i]),
1147 							 CURLFORM_COPYCONTENTS, postval,
1148 							 CURLFORM_CONTENTSLENGTH, (long)strlen(postval),
1149 							 CURLFORM_END);
1150 			}
1151 		}
1152 
1153 		curl_easy_setopt(curl, CURLOPT_HTTPPOST, ff);
1154 	} else if (payload->len) {
1155 		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload->c);
1156 		curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload->len);
1157 	}
1158 
1159 	curl_easy_setopt(curl, CURLOPT_URL, url);
1160 
1161 	/* the fetch method takes precedence so figure it out after we've added the OAuth params */
1162 	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method);
1163 
1164 	/* Disable sending the 100 Expect header for POST requests */
1165 	/* Other notes: if there is a redirect the POST becomes a GET request, see curl_easy_setopt(3) and the CURLOPT_POSTREDIR option for more information */
1166 	curl_headers = curl_slist_append(curl_headers, "Expect:");
1167 	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
1168 	curl_easy_setopt(curl, CURLOPT_USERAGENT, OAUTH_USER_AGENT);
1169 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, soo_read_response);
1170 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, soo);
1171 	if(sslcheck == OAUTH_SSLCHECK_NONE) {
1172 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
1173 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
1174 	} else {
1175 		if (!(sslcheck & OAUTH_SSLCHECK_HOST)) {
1176 			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
1177 		}
1178 		if (!(sslcheck & OAUTH_SSLCHECK_PEER)) {
1179 			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
1180 		}
1181 		if(zca_path && Z_STRLEN_P(zca_path)) {
1182 			curl_easy_setopt(curl, CURLOPT_CAPATH, Z_STRVAL_P(zca_path));
1183 		}
1184 		if(zca_info && Z_STRLEN_P(zca_info)) {
1185 			curl_easy_setopt(curl, CURLOPT_CAINFO, Z_STRVAL_P(zca_info));
1186 		}
1187 	}
1188 	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, soo_read_header);
1189 	curl_easy_setopt(curl, CURLOPT_WRITEHEADER, soo);
1190 	if(soo->debug) {
1191 		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
1192 	}
1193 #if defined(ZTS)
1194 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
1195 #endif
1196 
1197 #if LIBCURL_VERSION_NUM >= 0x071304
1198 	curl_easy_setopt(curl, CURLOPT_PROTOCOLS, OAUTH_PROTOCOLS_ALLOWED);
1199 #endif
1200 
1201 #if LIBCURL_VERSION_NUM > 0x071002
1202 	if(soo->timeout) {
1203 		curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, soo->timeout);
1204 		curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, soo->timeout);
1205 	}
1206 #endif
1207 
1208 	smart_string_free(&soo->lastresponse);
1209 	smart_string_free(&soo->headers_in);
1210 
1211 	if(soo->debug) {
1212 		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, oauth_debug_handler);
1213 		curl_easy_setopt(curl, CURLOPT_DEBUGDATA, soo->debug_info);
1214 	}
1215 
1216 	cres = curl_easy_perform(curl);
1217 
1218 	smart_string_0(&soo->lastresponse);
1219 	smart_string_0(&soo->headers_in);
1220 
1221 	if (curl_headers) {
1222 		curl_slist_free_all(curl_headers);
1223 	}
1224 
1225 	if (CURLE_OK == cres) {
1226 		ctres = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
1227 		crres = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
1228 
1229 		if (CURLE_OK == crres && ctres == CURLE_OK) {
1230 			array_init(&info);
1231 
1232 			CAAL("http_code", response_code);
1233 
1234 			if (HTTP_IS_REDIRECT(response_code) && soo->last_location_header) {
1235 				CAAS("redirect_url", soo->last_location_header);
1236 			}
1237 
1238 			if (content_type != NULL) {
1239 				CAAS("content_type", content_type);
1240 			}
1241 			if (curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {
1242 				CAAS("url", s_code);
1243 			}
1244 
1245 			if (curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {
1246 				CAAL("header_size", l_code);
1247 			}
1248 			if (curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {
1249 				CAAL("request_size", l_code);
1250 			}
1251 			if (curl_easy_getinfo(curl, CURLINFO_FILETIME, &l_code) == CURLE_OK) {
1252 				CAAL("filetime", l_code);
1253 			}
1254 			if (curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {
1255 				CAAL("ssl_verify_result", l_code);
1256 			}
1257 			if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {
1258 				CAAL("redirect_count", l_code);
1259 			}
1260 			if (curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME,&d_code) == CURLE_OK) {
1261 				CAAD("total_time", d_code);
1262 			}
1263 			if (curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {
1264 				CAAD("namelookup_time", d_code);
1265 			}
1266 			if (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {
1267 				CAAD("connect_time", d_code);
1268 			}
1269 			if (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {
1270 				CAAD("pretransfer_time", d_code);
1271 			}
1272 			if (curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK){
1273 				CAAD("size_upload", d_code);
1274 			}
1275 			if (curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK){
1276 				CAAD("size_download", d_code);
1277 			}
1278 			if (curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK){
1279 				CAAD("speed_download", d_code);
1280 			}
1281 			if (curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK){
1282 				CAAD("speed_upload", d_code);
1283 			}
1284 			if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {
1285 				CAAD("download_content_length", d_code);
1286 			}
1287 			if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {
1288 				CAAD("upload_content_length", d_code);
1289 			}
1290 			if (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK){
1291 				CAAD("starttransfer_time", d_code);
1292 			}
1293 			if (curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK){
1294 				CAAD("redirect_time", d_code);
1295 			}
1296 
1297 			CAAS("headers_recv", soo->headers_in.c);
1298 
1299 			so_set_response_info(soo->properties, &info);
1300 		}
1301 	} else {
1302 		spprintf(&bufz, 0, "making the request failed (%s)", curl_easy_strerror(cres));
1303 		soo_handle_error(soo, -1, bufz, soo->lastresponse.c, NULL);
1304 		efree(bufz);
1305 	}
1306 	curl_easy_cleanup(curl);
1307 	return response_code;
1308 }
1309 /* }}} */
1310 #endif
1311 
1312 static void make_standard_query(HashTable *ht, php_so_object *soo) /* {{{ */
1313 {
1314 	char *ts, *nonce;
1315 
1316 	if (soo->timestamp) {
1317 		ts = estrdup(soo->timestamp);
1318 	} else {
1319 		time_t now = time(NULL);
1320 		/* XXX allow caller to set timestamp, if none set, then default to "now" */
1321 		spprintf(&ts, 0, "%d", (int)now);
1322 	}
1323 
1324 	if (soo->nonce) {
1325 		nonce = estrdup(soo->nonce);
1326 	} else {
1327 		struct timeval tv;
1328 		int sec, usec;
1329 		/* XXX maybe find a better way to generate a nonce... */
1330 		gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
1331 		sec = (int) tv.tv_sec;
1332 		usec = (int) (tv.tv_usec % 0x100000);
1333 		spprintf(&nonce, 0, "%ld%08x%05x%.8f", php_rand(), sec, usec, php_combined_lcg() * 10);
1334 	}
1335 
1336 	add_arg_for_req(ht, OAUTH_PARAM_CONSUMER_KEY, Z_STRVAL_P(soo_get_property(soo, OAUTH_ATTR_CONSUMER_KEY)));
1337 	add_arg_for_req(ht, OAUTH_PARAM_SIGNATURE_METHOD, Z_STRVAL_P(soo_get_property(soo, OAUTH_ATTR_SIGMETHOD)));
1338 
1339 	add_arg_for_req(ht, OAUTH_PARAM_NONCE, nonce);
1340 
1341 	add_arg_for_req(ht, OAUTH_PARAM_TIMESTAMP, ts);
1342 	add_arg_for_req(ht, OAUTH_PARAM_VERSION, Z_STRVAL_P(soo_get_property(soo, OAUTH_ATTR_OAUTH_VERSION)));
1343 
1344 	efree(ts); efree(nonce);
1345 }
1346 /* }}} */
1347 
1348 /*
1349 Returns the default http method to use with the different auth types
1350 */
1351 static const char *oauth_get_http_method(php_so_object *soo, const char *http_method) /* {{{ */
1352 {
1353 	long auth_type = Z_LVAL_P(soo_get_property(soo, OAUTH_ATTR_AUTHMETHOD));
1354 
1355 	if (http_method) {
1356 		/* TODO handle conflict with FORM auth and anything but POST or PUT */
1357 		return http_method;
1358 	}
1359 	/* http method not explicitly given, choose default one */
1360 	if (OAUTH_AUTH_TYPE_FORM == auth_type) {
1361 		return OAUTH_HTTP_METHOD_POST;
1362 	} else {
1363 		return OAUTH_HTTP_METHOD_GET;
1364 	}
1365 }
1366 /* }}} */
1367 
1368 /*
1369 Modifies (and returns) passed url parameter to be used for additional parameter appending
1370 */
1371 static smart_string *http_prepare_url_concat(smart_string *surl) /* {{{ */
1372 {
1373 	smart_string_0(surl);
1374 	if (!strchr(surl->c, '?')) {
1375 		smart_string_appendc(surl, '?');
1376 	} else {
1377 		smart_string_appendc(surl, '&');
1378 	}
1379 	return surl;
1380 }
1381 /* }}} */
1382 
1383 /*
1384 Modifies passed url based on the location header that was received in the response headers, depending on whether the redirection was relative or absolute
1385 */
1386 static void oauth_apply_url_redirect(smart_string *surl, const char *location) /* {{{ */
1387 {
1388 	php_url *urlparts;
1389 
1390 	/* determine whether location is relative */
1391 	if ('/'==*location) {
1392 		urlparts = php_url_parse_ex(surl->c, surl->len);
1393 
1394 		/* rebuild url from scratch */
1395 		smart_string_free(surl);
1396 		if (urlparts->scheme) {
1397 			smart_string_appends(surl, OAUTH_URL_STR(urlparts->scheme));
1398 			smart_string_appends(surl, "://");
1399 		}
1400 		if (urlparts->host) {
1401 			smart_string_appends(surl, OAUTH_URL_STR(urlparts->host));
1402 		}
1403 		if (urlparts->port) {
1404 			smart_string_appendc(surl, ':');
1405 			smart_string_append_unsigned(surl, urlparts->port);
1406 		}
1407 		smart_string_appends(surl, location);
1408 
1409 		php_url_free(urlparts);
1410 	} else {
1411 		smart_string_free(surl);
1412 		smart_string_appends(surl, location);
1413 	}
1414 }
1415 /* }}} */
1416 
1417 /*
1418 Prepares the request elements to be used by make_req(); this should allow for supporting streams in the future
1419 */
1420 static long oauth_fetch(php_so_object *soo, const char *url, const char *method, zval *request_params, zval *request_headers, HashTable *init_oauth_args, int fetch_flags) /* {{{ */
1421 {
1422 	char *bufz = NULL;
1423 	zend_string *sbs = NULL, *sig;
1424 	const char *final_http_method;
1425 	zval *token = NULL, *cs;
1426 	zval *ts = NULL, *token_secret = NULL;
1427 	zval zret;
1428 	HashTable *oauth_args = NULL;
1429 	HashTable *rargs = NULL, rheaders;
1430 	long http_response_code, auth_type;
1431 	smart_string surl = {0}, payload = {0}, postdata = {0};
1432 	uint32_t is_redirect = FALSE, follow_redirects = 0;
1433 
1434 	auth_type = Z_LVAL_P(soo_get_property(soo, OAUTH_ATTR_AUTHMETHOD));
1435 	if(fetch_flags & OAUTH_OVERRIDE_HTTP_METHOD) {
1436 		final_http_method = method;
1437 	} else {
1438 		final_http_method = oauth_get_http_method(soo, method ? method : OAUTH_HTTP_METHOD_POST);
1439 
1440 		if (OAUTH_AUTH_TYPE_FORM==auth_type && strcasecmp(final_http_method, OAUTH_HTTP_METHOD_POST)) {
1441 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "auth type is set to HTTP POST with a non-POST http method, use setAuthType to put OAuth parameters somewhere else in the request", NULL, NULL);
1442 		}
1443 	}
1444 
1445 
1446 	if(!final_http_method) {
1447 		final_http_method = "GET";
1448 	}
1449 
1450 	follow_redirects = soo->follow_redirects;
1451 	soo->redirects = 0;
1452 	soo->multipart_files = NULL;
1453 	soo->multipart_params = NULL;
1454 	soo->multipart_files_num = 0;
1455 	soo->is_multipart = 0;
1456 
1457 	/* request_params can be either NULL, a string containing arbitrary text (such as XML) or an array */
1458 	if (request_params) {
1459 		switch (Z_TYPE_P(request_params)) {
1460 		case IS_ARRAY:
1461 			rargs = HASH_OF(request_params);
1462 			oauth_http_build_query(soo, &postdata, rargs, FALSE);
1463 			break;
1464 		case IS_STRING:
1465 			smart_string_appendl(&postdata, Z_STRVAL_P(request_params), Z_STRLEN_P(request_params));
1466 			break;
1467 		}
1468 	}
1469 
1470 	/* additional http headers can be passed */
1471 	zend_hash_init(&rheaders, 0, NULL, ZVAL_PTR_DTOR, 0);
1472 	if (request_headers && zend_hash_num_elements(Z_ARRVAL_P(request_headers))) {
1473 		zend_hash_copy(&rheaders, Z_ARRVAL_P(request_headers), (copy_ctor_func_t) zval_add_ref);
1474 	}
1475 
1476 	/* initialize base url */
1477 	smart_string_appends(&surl, url);
1478 
1479 	do {
1480 		/* initialize response code */
1481 		http_response_code = -1;
1482 
1483 		/* prepare oauth arguments to be signed */
1484 		ALLOC_HASHTABLE(oauth_args);
1485 		zend_hash_init(oauth_args, 0, NULL, ZVAL_PTR_DTOR, 0);
1486 
1487 		/* an array can be passed to prime special oauth parameters */
1488 		if (init_oauth_args) {
1489 			/* populate oauth_args with given parameters */
1490 			zend_hash_copy(oauth_args, init_oauth_args, (copy_ctor_func_t) zval_add_ref);
1491 		}
1492 
1493 		/* fill in the standard set of oauth parameters */
1494 		make_standard_query(oauth_args, soo);
1495 
1496 		/* use token where applicable */
1497 		if (fetch_flags & OAUTH_FETCH_USETOKEN) {
1498 			token = soo_get_property(soo, OAUTH_ATTR_TOKEN);
1499 			if (token) {
1500 				add_arg_for_req(oauth_args, OAUTH_PARAM_TOKEN, Z_STRVAL_P(token));
1501 			}
1502 		}
1503 
1504 		/* generate sig base on the semi-final url */
1505 		smart_string_0(&surl);
1506 		sbs = oauth_generate_sig_base(soo, final_http_method, surl.c, oauth_args, rargs);
1507 		if (!sbs) {
1508 			FREE_ARGS_HASH(oauth_args);
1509 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid protected resource url, unable to generate signature base string", NULL, NULL);
1510 			break;
1511 		}
1512 
1513 		cs = soo_get_property(soo, OAUTH_ATTR_CONSUMER_SECRET);
1514 		SEPARATE_ZVAL(cs);
1515 
1516 		/* determine whether token should be used to sign the request */
1517 		if (fetch_flags & OAUTH_FETCH_USETOKEN) {
1518 			token_secret = soo_get_property(soo, OAUTH_ATTR_TOKEN_SECRET);
1519 			if (token_secret && Z_STRLEN_P(token_secret) > 0) {
1520 				ts = token_secret;
1521 			}
1522 		}
1523 
1524 		if(soo->signature) {
1525 			zend_string_release(soo->signature);
1526 		}
1527 		/* sign the request */
1528 		sig = soo_sign(soo, ZSTR_VAL(sbs), cs, ts, soo->sig_ctx);
1529 		soo->signature = sig;
1530 		zend_string_release(sbs);
1531 
1532 		if(fetch_flags & OAUTH_FETCH_SIGONLY) {
1533 			FREE_ARGS_HASH(oauth_args);
1534 			smart_string_free(&surl);
1535 			smart_string_free(&postdata);
1536 			zend_hash_destroy(&rheaders);
1537 			return SUCCESS;
1538 		}
1539 
1540 		if (!sig) {
1541 			FREE_ARGS_HASH(oauth_args);
1542 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Signature generation failed", NULL, NULL);
1543 			break;
1544 		}
1545 
1546 		/* and add signature to the oauth parameters */
1547 		add_arg_for_req(oauth_args, OAUTH_PARAM_SIGNATURE, ZSTR_VAL(sig));
1548 
1549 		if(fetch_flags & OAUTH_FETCH_HEADONLY) {
1550 			INIT_smart_string(soo->headers_out);
1551 			oauth_add_signature_header(&rheaders, oauth_args, &soo->headers_out);
1552 			smart_string_0(&payload);
1553 			FREE_ARGS_HASH(oauth_args);
1554 			smart_string_free(&surl);
1555 			smart_string_free(&postdata);
1556 			zend_hash_destroy(&rheaders);
1557 			return SUCCESS;
1558 		}
1559 
1560 		if (!strcmp(final_http_method, OAUTH_HTTP_METHOD_GET)) {
1561 			/* GET request means to extend the url, but not for redirects obviously */
1562 			if (!is_redirect && postdata.len) {
1563 				smart_string_append(http_prepare_url_concat(&surl), &postdata);
1564 			}
1565 		} else {
1566 			/* otherwise populate post data */
1567 			smart_string_append(&payload, &postdata);
1568 		}
1569 
1570 		switch (auth_type) {
1571 			case OAUTH_AUTH_TYPE_FORM:
1572 				/* append/set post data with oauth parameters */
1573 				oauth_http_build_query(soo, &payload, oauth_args, payload.len);
1574 				smart_string_0(&payload);
1575 				break;
1576 			case OAUTH_AUTH_TYPE_URI:
1577 				/* extend url request with oauth parameters */
1578 				if (!is_redirect) {
1579 					oauth_http_build_query(soo, http_prepare_url_concat(&surl), oauth_args, FALSE);
1580 				}
1581 				/* TODO look into merging oauth parameters if they occur in the current url */
1582 				break;
1583 			case OAUTH_AUTH_TYPE_AUTHORIZATION:
1584 				/* add http header with oauth parameters */
1585 				oauth_add_signature_header(&rheaders, oauth_args, NULL);
1586 				break;
1587 		}
1588 
1589 		/* finalize endpoint url */
1590 		smart_string_0(&surl);
1591 
1592 		if (soo->debug) {
1593 			if(soo->debug_info->sbs) {
1594 				FREE_DEBUG_INFO(soo->debug_info);
1595 			}
1596 			INIT_DEBUG_INFO(soo->debug_info);
1597 		}
1598 
1599 		switch (soo->reqengine) {
1600 			case OAUTH_REQENGINE_STREAMS:
1601 				http_response_code = make_req_streams(soo, surl.c, &payload, final_http_method, &rheaders);
1602 				break;
1603 #if OAUTH_USE_CURL
1604 			case OAUTH_REQENGINE_CURL:
1605 				http_response_code = make_req_curl(soo, surl.c, &payload, final_http_method, &rheaders);
1606 				if (soo->multipart_files_num) {
1607 					efree(soo->multipart_files);
1608 					efree(soo->multipart_params);
1609 					soo->multipart_files_num = 0;
1610 					soo->is_multipart = 0;
1611 				}
1612 				break;
1613 #endif
1614 		}
1615 
1616 		is_redirect = HTTP_IS_REDIRECT(http_response_code);
1617 
1618 		if(soo->debug) {
1619 			oauth_set_debug_info(soo);
1620 		}
1621 
1622 		FREE_ARGS_HASH(oauth_args);
1623 		smart_string_free(&payload);
1624 
1625 		if (is_redirect) {
1626 			if (follow_redirects) {
1627 				if (soo->redirects >= OAUTH_MAX_REDIRS) {
1628 					spprintf(&bufz, 0, "max redirections exceeded (max: %ld last redirect url: %s)", OAUTH_MAX_REDIRS, soo->last_location_header);
1629 					if (soo->lastresponse.len) {
1630 						ZVAL_STRING(&zret, soo->lastresponse.c);
1631 					} else {
1632 						ZVAL_STRING(&zret, "");
1633 					}
1634 					so_set_response_args(soo->properties, &zret, NULL);
1635 					soo_handle_error(soo, http_response_code, bufz, soo->lastresponse.c, NULL);
1636 					efree(bufz);
1637 					/* set http_response_code to error value */
1638 					http_response_code = -1;
1639 					break;
1640 				} else {
1641 					++soo->redirects;
1642 					oauth_apply_url_redirect(&surl, soo->last_location_header);
1643 					smart_string_0(&surl);
1644 /* bug 22628; keep same method when following redirects
1645 					final_http_method = OAUTH_HTTP_METHOD_GET;
1646 */
1647 				}
1648 			}
1649 		} else if (http_response_code < 0) {
1650 			/* exception would have been thrown already */
1651 		} else if (http_response_code < 200 || http_response_code > 209) {
1652 			spprintf(&bufz, 0, "Invalid auth/bad request (got a %ld, expected HTTP/1.1 20X or a redirect)", http_response_code);
1653 			if(soo->lastresponse.c) {
1654 				ZVAL_STRING(&zret, soo->lastresponse.c);
1655 			} else {
1656 				ZVAL_STRING(&zret, "");
1657 			}
1658 			so_set_response_args(soo->properties, &zret, NULL);
1659 			soo_handle_error(soo, http_response_code, bufz, soo->lastresponse.c, NULL);
1660 			efree(bufz);
1661 			/* set http_response_code to error value */
1662 			http_response_code = -1;
1663 			break;
1664 		} else {
1665 			/* valid response, time to get out of this loop */
1666 		}
1667 	} while (is_redirect && follow_redirects);
1668 
1669 	smart_string_free(&surl);
1670 	smart_string_free(&postdata);
1671 	zend_hash_destroy(&rheaders);
1672 
1673 	return http_response_code;
1674 }
1675 /* }}} */
1676 
1677 /* {{{ proto bool setRSACertificate(string $cert)
1678        Sets the RSA certificate */
1679 SO_METHOD(setRSACertificate)
1680 {
1681 	char *key;
1682 	size_t key_len;
1683 	zval args[1], func, retval;
1684 
1685 	php_so_object *soo;
1686 
1687 	soo = Z_SOO_P(getThis());
1688 
1689 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) {
1690 		return;
1691 	}
1692 
1693 	ZVAL_STRING(&func, "openssl_get_privatekey");
1694 
1695 	ZVAL_STRINGL(&args[0], key, key_len);
1696 
1697 
1698 	call_user_function(EG(function_table), NULL, &func, &retval, 1, args);
1699 
1700 	zval_ptr_dtor(&args[0]);
1701 	zval_ptr_dtor(&func);
1702 
1703 	switch (Z_TYPE(retval)) {
1704 	case IS_RESOURCE:
1705 	case IS_OBJECT:
1706 		OAUTH_SIGCTX_SET_PRIVATEKEY(soo->sig_ctx, retval);
1707 		RETURN_TRUE;
1708 		break;
1709 	default:
1710 		zval_ptr_dtor(&retval);
1711 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Could not parse RSA certificate", NULL, NULL);
1712 		return;
1713 	}
1714 }
1715 /* }}} */
1716 
1717 /* {{{ proto string oauth_urlencode(string uri)
1718    URI encoding according to RFC 3986, note: is not utf8 capable until the underlying phpapi is */
1719 PHP_FUNCTION(oauth_urlencode)
1720 {
1721 	size_t uri_len;
1722 	char *uri;
1723 
1724 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) {
1725 		return;
1726 	}
1727 
1728 	if (uri_len < 1) {
1729 		php_error_docref(NULL, E_WARNING, "Invalid uri length (0)");
1730 		RETURN_FALSE;
1731 	}
1732 	RETURN_STR(oauth_url_encode(uri, uri_len));
1733 }
1734 /* }}} */
1735 
1736 /* {{{ proto string oauth_get_sbs(string http_method, string uri, array parameters)
1737    Get a signature base string */
1738 PHP_FUNCTION(oauth_get_sbs)
1739 {
1740 	char *uri, *http_method;
1741 	zend_string *sbs;
1742 	size_t uri_len, http_method_len;
1743 	zval *req_params = NULL;
1744 	HashTable *rparams = NULL;
1745 
1746 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &http_method, &http_method_len, &uri, &uri_len, &req_params) == FAILURE) {
1747 		return;
1748 	}
1749 
1750 	if (uri_len < 1) {
1751 		php_error_docref(NULL, E_WARNING, "Invalid uri length (0)");
1752 		RETURN_FALSE;
1753 	}
1754 
1755 	if (http_method_len < 1) {
1756 		php_error_docref(NULL, E_WARNING, "Invalid http method length (0)");
1757 		RETURN_FALSE;
1758 	}
1759 
1760 	if (req_params) {
1761 		rparams = HASH_OF(req_params);
1762 	}
1763 
1764 	if ((sbs = oauth_generate_sig_base(NULL, http_method, uri, NULL, rparams))) {
1765 		RETURN_STR(sbs);
1766 	} else {
1767 		RETURN_FALSE;
1768 	}
1769 }
1770 /* }}} */
1771 
1772 /* only hmac-sha1 is supported at the moment (it is the most common implementation), still need to lay down the ground work for supporting plaintext and others */
1773 
1774 /* {{{ proto void OAuth::__construct(string consumer_key, string consumer_secret [, string signature_method, [, string auth_type ]])
1775    Instantiate a new OAuth object */
1776 SO_METHOD(__construct)
1777 {
1778 	HashTable *hasht;
1779 	char *ck, *cs, *sig_method = NULL;
1780 	zend_long auth_method = 0;
1781 	zval zck, zcs, zsm, zam, zver, *obj;
1782 	size_t ck_len = 0, cs_len = 0, sig_method_len = 0;
1783 	php_so_object *soo;
1784 
1785 	obj = getThis();
1786 
1787 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sssl", &ck, &ck_len, &cs, &cs_len, &sig_method, &sig_method_len, &auth_method) == FAILURE) {
1788 		return;
1789 	}
1790 
1791 	soo = Z_SOO_P(obj);
1792 
1793 	if(ck_len == 0) {
1794 		soo_handle_error(soo, -1, "The consumer key cannot be empty", NULL, NULL);
1795 		return;
1796 	}
1797 
1798 	if(cs_len == 0) {
1799 		soo_handle_error(soo, -1, "The consumer secret cannot be empty", NULL, NULL);
1800 		return;
1801 	}
1802 
1803 	memset(soo->last_location_header, 0, OAUTH_MAX_HEADER_LEN);
1804 	soo->redirects = 0;
1805 	soo->debug = 0;
1806 	soo->debug_info = emalloc(sizeof(php_so_debug));
1807 	soo->debug_info->sbs = NULL;
1808 	ZVAL_UNDEF(&soo->debugArr);
1809 
1810 	soo->nonce = NULL;
1811 	soo->timestamp = NULL;
1812 	soo->sig_ctx = NULL;
1813 	soo->signature = NULL;
1814 
1815 	INIT_DEBUG_INFO(soo->debug_info);
1816 
1817 	INIT_smart_string(soo->headers_in);
1818 
1819 	/* set default class members */
1820 	zend_update_property_null(soo_class_entry, OBJ_FOR_PROP(obj), "debugInfo", sizeof("debugInfo") - 1);
1821 	zend_update_property_bool(soo_class_entry, OBJ_FOR_PROP(obj), "debug", sizeof("debug") - 1, soo->debug);
1822 	zend_update_property_long(soo_class_entry, OBJ_FOR_PROP(obj), "sslChecks", sizeof("sslChecks") - 1, soo->sslcheck);
1823 
1824 #if PHP_VERSION_ID < 80000
1825 	TSRMLS_SET_CTX(soo->thread_ctx);
1826 #endif
1827 
1828 	if (!sig_method_len) {
1829 		sig_method = OAUTH_SIG_METHOD_HMACSHA1;
1830 	}
1831 
1832 	soo->sig_ctx = oauth_create_sig_context(sig_method);
1833 
1834 	if (!auth_method) {
1835 		auth_method = OAUTH_AUTH_TYPE_AUTHORIZATION;
1836 	}
1837 
1838 	if (soo->properties) {
1839 		zend_hash_clean(soo->properties);
1840 		hasht = soo->properties;
1841 	} else {
1842 		ALLOC_HASHTABLE(hasht);
1843 		zend_hash_init(hasht, 0, NULL, ZVAL_PTR_DTOR, 0);
1844 		soo->properties = hasht;
1845 	}
1846 
1847 	ZVAL_STRING(&zck, ck);
1848 	if (soo_set_property(soo, &zck, OAUTH_ATTR_CONSUMER_KEY) != SUCCESS) {
1849 		return;
1850 	}
1851 
1852 	if (cs_len > 0) {
1853 		ZVAL_STR(&zcs, oauth_url_encode(cs, cs_len));
1854 	} else {
1855 		ZVAL_EMPTY_STRING(&zcs);
1856 	}
1857 	if (soo_set_property(soo, &zcs, OAUTH_ATTR_CONSUMER_SECRET) != SUCCESS) {
1858 		return;
1859 	}
1860 
1861 	ZVAL_STRING(&zsm, sig_method);
1862 	if (soo_set_property(soo, &zsm, OAUTH_ATTR_SIGMETHOD) != SUCCESS) {
1863 		return;
1864 	}
1865 
1866 	ZVAL_LONG(&zam, auth_method);
1867 	if (soo_set_property(soo, &zam, OAUTH_ATTR_AUTHMETHOD) != SUCCESS) {
1868 		return;
1869 	}
1870 
1871 	ZVAL_STRING(&zver, OAUTH_DEFAULT_VERSION);
1872 	if (soo_set_property(soo, &zver, OAUTH_ATTR_OAUTH_VERSION) != SUCCESS) {
1873 		return;
1874 	}
1875 
1876 	soo->debug = 0;
1877 	soo->sslcheck = OAUTH_SSLCHECK_BOTH;
1878 	soo->follow_redirects = 1;
1879 
1880 	soo->lastresponse.c = NULL;
1881 #if OAUTH_USE_CURL
1882 	soo->reqengine = OAUTH_REQENGINE_CURL;
1883 #else
1884 	soo->reqengine = OAUTH_REQENGINE_STREAMS;
1885 #endif
1886 }
1887 /* }}} */
1888 
1889 void oauth_free_privatekey(zval *privatekey) /* {{{ */
1890 {
1891 	zval func, retval;
1892 	zval args[1];
1893 
1894 	if (Z_TYPE_P(privatekey)==IS_RESOURCE) {
1895 		ZVAL_STRING(&func, "openssl_freekey");
1896 		ZVAL_DUP(&args[0], privatekey);
1897 
1898 		call_user_function(EG(function_table), NULL, &func, &retval, 1, args);
1899 
1900 		zval_ptr_dtor(&func);
1901 		zval_ptr_dtor(&retval);
1902 	}
1903 
1904 	zval_ptr_dtor(privatekey);
1905 }
1906 /* }}} */
1907 
1908 /* {{{ proto array OAuth::setCAPath(string ca_path, string ca_info)
1909    Set the Certificate Authority information */
1910 SO_METHOD(setCAPath)
1911 {
1912 	php_so_object *soo;
1913 	char *ca_path, *ca_info;
1914 	size_t ca_path_len = 0, ca_info_len = 0;
1915 	zval zca_path, zca_info;
1916 
1917 	soo = Z_SOO_P(getThis());
1918 
1919 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &ca_path, &ca_path_len, &ca_info, &ca_info_len) == FAILURE) {
1920 		return;
1921 	}
1922 
1923 	if (ca_path_len) {
1924 		ZVAL_STRINGL(&zca_path, ca_path, ca_path_len);
1925 		if (soo_set_property(soo, &zca_path, OAUTH_ATTR_CA_PATH) != SUCCESS) {
1926 			RETURN_FALSE;
1927 		}
1928 	}
1929 
1930 	if (ca_info_len) {
1931 		ZVAL_STRINGL(&zca_info, ca_info, ca_info_len);
1932 		if (soo_set_property(soo, &zca_info, OAUTH_ATTR_CA_INFO) != SUCCESS) {
1933 			RETURN_FALSE;
1934 		}
1935 	}
1936 	RETURN_TRUE;
1937 }
1938 /* }}} */
1939 
1940 /* {{{ proto array OAuth::getCAPath(void)
1941    Get the Certificate Authority information */
1942 SO_METHOD(getCAPath)
1943 {
1944 	/* perhaps make this information available via members too? */
1945 	php_so_object *soo;
1946 	zval *zca_path, *zca_info;
1947 
1948 	soo = Z_SOO_P(getThis());
1949 
1950 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
1951 		return;
1952 	}
1953 
1954 	zca_info = soo_get_property(soo, OAUTH_ATTR_CA_INFO);
1955 	zca_path = soo_get_property(soo, OAUTH_ATTR_CA_PATH);
1956 
1957 	array_init(return_value);
1958 
1959 	if (zca_info || zca_path) {
1960 		if(zca_info) {
1961 			add_assoc_stringl(return_value, "ca_info", Z_STRVAL_P(zca_info), Z_STRLEN_P(zca_info));
1962 		}
1963 
1964 		if(zca_path) {
1965 			add_assoc_stringl(return_value, "ca_path", Z_STRVAL_P(zca_path), Z_STRLEN_P(zca_path));
1966 		}
1967 	}
1968 }
1969 /* }}} */
1970 
1971 /* {{{ proto array OAuth::getRequestToken(string request_token_url [, string callback_url [, http_method ] ])
1972    Get request token */
1973 SO_METHOD(getRequestToken)
1974 {
1975 	php_so_object *soo;
1976 	zval zret, *callback_url = NULL;
1977 	char *url, *http_method = OAUTH_HTTP_METHOD_POST;
1978 	size_t url_len = 0, http_method_len = sizeof(OAUTH_HTTP_METHOD_POST) - 1;
1979 	long retcode;
1980 	HashTable *args = NULL;
1981 
1982 	soo = Z_SOO_P(getThis());
1983 
1984 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|zs", &url, &url_len, &callback_url, &http_method, &http_method_len) == FAILURE) {
1985 		return;
1986 	}
1987 
1988 	if (url_len < 1) {
1989 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid request token url length", NULL, NULL);
1990 		RETURN_FALSE;
1991 	}
1992 
1993 	if (callback_url && IS_STRING==Z_TYPE_P(callback_url)) {
1994 		ALLOC_HASHTABLE(args);
1995 		zend_hash_init(args, 0, NULL, ZVAL_PTR_DTOR, 0);
1996 		if (Z_STRLEN_P(callback_url) > 0) {
1997 			add_arg_for_req(args, OAUTH_PARAM_CALLBACK, Z_STRVAL_P(callback_url));
1998 		} else {
1999 			/* empty callback url specified, treat as 1.0a */
2000 			add_arg_for_req(args, OAUTH_PARAM_CALLBACK, OAUTH_CALLBACK_OOB);
2001 		}
2002 	}
2003 
2004 	retcode = oauth_fetch(soo, url, oauth_get_http_method(soo, http_method), NULL, NULL, args, 0);
2005 
2006 	if (args) {
2007 		FREE_ARGS_HASH(args);
2008 	}
2009 
2010 	if (retcode != -1 && soo->lastresponse.c) {
2011 		array_init(return_value);
2012 		ZVAL_STRINGL(&zret, soo->lastresponse.c, soo->lastresponse.len);
2013 		so_set_response_args(soo->properties, &zret, return_value);
2014 		return;
2015 	}
2016 	RETURN_FALSE;
2017 }
2018 /* }}} */
2019 
2020 /* {{{ proto bool OAuth::enableRedirects(void)
2021    Follow and sign redirects automatically (enabled by default) */
2022 SO_METHOD(enableRedirects)
2023 {
2024 	php_so_object *soo;
2025 
2026 	soo = Z_SOO_P(getThis());
2027 
2028 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2029 		return;
2030 	}
2031 
2032 	soo->follow_redirects = 1;
2033 
2034 	RETURN_TRUE;
2035 }
2036 /* }}} */
2037 
2038 /* {{{ proto bool OAuth::disableRedirects(void)
2039    Don't follow redirects automatically, thus allowing the request to be manually redirected (enabled by default) */
2040 SO_METHOD(disableRedirects)
2041 {
2042 	php_so_object *soo;
2043 
2044 	soo = Z_SOO_P(getThis());
2045 
2046 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2047 		return;
2048 	}
2049 
2050 	soo->follow_redirects = 0;
2051 
2052 	RETURN_TRUE;
2053 }
2054 /* }}} */
2055 
2056 /* {{{ proto bool OAuth::disableDebug(void)
2057    Disable debug mode */
2058 SO_METHOD(disableDebug)
2059 {
2060 	php_so_object *soo;
2061 	zval *obj;
2062 
2063 	obj = getThis();
2064 	soo = Z_SOO_P(obj);
2065 
2066 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2067 		return;
2068 	}
2069 
2070 	soo->debug = 0;
2071 	zend_update_property_bool(soo_class_entry, OBJ_FOR_PROP(obj), "debug", sizeof("debug") - 1, 0);
2072 
2073 	RETURN_TRUE;
2074 }
2075 /* }}} */
2076 
2077 /* {{{ proto bool OAuth::enableDebug(void)
2078    Enable debug mode, will verbosely output http information about requests */
2079 SO_METHOD(enableDebug)
2080 {
2081 	php_so_object *soo;
2082 	zval *obj;
2083 
2084 	obj = getThis();
2085 	soo = Z_SOO_P(obj);
2086 
2087 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2088 		return;
2089 	}
2090 
2091 	soo->debug = 1;
2092 	zend_update_property_bool(soo_class_entry, OBJ_FOR_PROP(obj), "debug", sizeof("debug") - 1, 1);
2093 
2094 	RETURN_TRUE;
2095 }
2096 /* }}} */
2097 
2098 /* {{{ proto bool OAuth::enableSSLChecks(void)
2099    Enable SSL verification for requests, enabled by default */
2100 SO_METHOD(enableSSLChecks)
2101 {
2102 	php_so_object *soo;
2103 	zval *obj;
2104 
2105 	obj = getThis();
2106 	soo = Z_SOO_P(obj);
2107 
2108 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2109 		return;
2110 	}
2111 
2112 	soo->sslcheck = OAUTH_SSLCHECK_BOTH;
2113 	zend_update_property_long(soo_class_entry, OBJ_FOR_PROP(obj), "sslChecks", sizeof("sslChecks") - 1, 1);
2114 
2115 	RETURN_TRUE;
2116 }
2117 /* }}} */
2118 
2119 /* {{{ proto bool OAuth::disableSSLChecks(void)
2120    Disable SSL verification for requests (be careful using this for production) */
2121 SO_METHOD(disableSSLChecks)
2122 {
2123 	php_so_object *soo;
2124 	zval *obj;
2125 
2126 	obj = getThis();
2127 	soo = Z_SOO_P(obj);
2128 
2129 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2130 		return;
2131 	}
2132 
2133 	soo->sslcheck = OAUTH_SSLCHECK_NONE;
2134 	zend_update_property_long(soo_class_entry, OBJ_FOR_PROP(obj), "sslChecks", sizeof("sslChecks") - 1, 0);
2135 
2136 	RETURN_TRUE;
2137 }
2138 /* }}} */
2139 
2140 /* {{{ proto bool OAuth::setSSLChecks(long sslcheck)
2141    Tweak specific SSL checks for requests (be careful using this for production) */
2142 SO_METHOD(setSSLChecks)
2143 {
2144 	php_so_object *soo;
2145 	zval *obj;
2146 	zend_long sslcheck = OAUTH_SSLCHECK_BOTH;
2147 
2148 	obj = getThis();
2149 	soo = Z_SOO_P(obj);
2150 
2151 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &sslcheck) == FAILURE) {
2152 		return;
2153 	}
2154 
2155 	soo->sslcheck = sslcheck & OAUTH_SSLCHECK_BOTH;
2156 
2157 	zend_update_property_long(soo_class_entry, OBJ_FOR_PROP(obj), "sslChecks", sizeof("sslChecks") - 1,
2158 			soo->sslcheck);
2159 
2160 	RETURN_TRUE;
2161 }
2162 /* }}} */
2163 
2164 /* {{{ proto bool OAuth::setVersion(string version)
2165    Set oauth_version for requests (default 1.0) */
2166 SO_METHOD(setVersion)
2167 {
2168 	php_so_object *soo;
2169 	size_t ver_len = 0;
2170 	char *vers;
2171 	zval zver;
2172 
2173 	soo = Z_SOO_P(getThis());
2174 
2175 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &vers, &ver_len) == FAILURE) {
2176 		return;
2177 	}
2178 
2179 	if (ver_len < 1) {
2180 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid version", NULL, NULL);
2181 		RETURN_FALSE;
2182 	}
2183 
2184 	ZVAL_STRING(&zver, vers);
2185 	if (SUCCESS == soo_set_property(soo, &zver, OAUTH_ATTR_OAUTH_VERSION)) {
2186 		RETURN_TRUE;
2187 	}
2188 
2189 	RETURN_FALSE;
2190 }
2191 /* }}} */
2192 
2193 /* {{{ proto bool OAuth::setAuthType(string auth_type)
2194    Set the manner in which to send oauth parameters */
2195 SO_METHOD(setAuthType)
2196 {
2197 	php_so_object *soo;
2198 	zend_long auth;
2199 	zval zauth;
2200 
2201 	soo = Z_SOO_P(getThis());
2202 
2203 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &auth) == FAILURE) {
2204 		return;
2205 	}
2206 
2207 	switch (auth) {
2208 		case OAUTH_AUTH_TYPE_URI:
2209 		case OAUTH_AUTH_TYPE_FORM:
2210 		case OAUTH_AUTH_TYPE_AUTHORIZATION:
2211 		case OAUTH_AUTH_TYPE_NONE:
2212 			ZVAL_LONG(&zauth, auth);
2213 			if (SUCCESS == soo_set_property(soo, &zauth, OAUTH_ATTR_AUTHMETHOD)) {
2214 				RETURN_TRUE;
2215 			}
2216 		default:
2217 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid auth type", NULL, NULL);
2218 			RETURN_FALSE;
2219 	}
2220 
2221 	RETURN_FALSE;
2222 }
2223 /* }}} */
2224 
2225 /* {{{ proto bool OAuth::setTimeout(int milliseconds)
2226    Set the timeout, in milliseconds, for requests */
2227 SO_METHOD(setTimeout)
2228 {
2229 	php_so_object *soo;
2230 	zend_long timeout;
2231 
2232 	soo = Z_SOO_P(getThis());
2233 
2234 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &timeout) == FAILURE) {
2235 		return;
2236 	}
2237 
2238 	if (timeout < 0) {
2239 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid timeout", NULL, NULL);
2240 		RETURN_FALSE;
2241 	}
2242 
2243 	soo->timeout = timeout;
2244 
2245 	RETURN_TRUE;
2246 }
2247 /* }}} */
2248 
2249 /* {{{ proto bool OAuth::setNonce(string nonce)
2250    Set oauth_nonce for subsequent requests, if none is set a random nonce will be generated using uniqid */
2251 SO_METHOD(setNonce)
2252 {
2253 	php_so_object *soo;
2254 	size_t nonce_len;
2255 	char *nonce;
2256 
2257 	soo = Z_SOO_P(getThis());
2258 
2259 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &nonce, &nonce_len) == FAILURE) {
2260 		return;
2261 	}
2262 
2263 	if (nonce_len < 1) {
2264 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid nonce", NULL, NULL);
2265 		RETURN_FALSE;
2266 	}
2267 
2268 	if (soo->nonce) {
2269 		efree(soo->nonce);
2270 	}
2271 	soo->nonce = estrndup(nonce, nonce_len);
2272 
2273 	RETURN_TRUE;
2274 }
2275 /* }}} */
2276 
2277 /* {{{ proto bool setTimestamp(string $timestamp)
2278 Sets the OAuth timestamp for subsequent requests */
2279 SO_METHOD(setTimestamp)
2280 {
2281 	php_so_object *soo;
2282 	size_t ts_len;
2283 	char *ts;
2284 
2285 	soo = Z_SOO_P(getThis());
2286 
2287 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &ts, &ts_len) == FAILURE) {
2288 		return;
2289 	}
2290 
2291 	if (ts_len < 1) {
2292 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid timestamp", NULL, NULL);
2293 		RETURN_FALSE;
2294 	}
2295 
2296 	if (soo->timestamp) {
2297 		efree(soo->timestamp);
2298 	}
2299 	soo->timestamp = estrndup(ts, ts_len);
2300 
2301 	RETURN_TRUE;
2302 }
2303 /* }}} */
2304 
2305 /* {{{ proto bool OAuth::setToken(string token, string token_secret)
2306    Set a request or access token and token secret to be used in subsequent requests */
2307 SO_METHOD(setToken)
2308 {
2309 	php_so_object *soo;
2310 	size_t token_len, token_secret_len;
2311 	char *token, *token_secret;
2312 	zval t,ts;
2313 
2314 	soo = Z_SOO_P(getThis());
2315 
2316 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &token, &token_len, &token_secret, &token_secret_len) == FAILURE) {
2317 		return;
2318 	}
2319 
2320 	ZVAL_STRING(&t, token);
2321 	soo_set_property(soo, &t, OAUTH_ATTR_TOKEN);
2322 
2323 	if (token_secret_len > 1) {
2324 		ZVAL_STR(&ts, oauth_url_encode(token_secret, token_secret_len));
2325 		soo_set_property(soo, &ts, OAUTH_ATTR_TOKEN_SECRET);
2326 	}
2327 	RETURN_TRUE;
2328 }
2329 /* }}} */
2330 
2331 /* {{{ proto void OAuth::setRequestEngine(long reqengine) */
2332 SO_METHOD(setRequestEngine)
2333 {
2334 	php_so_object *soo;
2335 	zend_long reqengine;
2336 
2337 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &reqengine) == FAILURE) {
2338 		return;
2339 	}
2340 	soo = Z_SOO_P(getThis());
2341 
2342 	switch (reqengine) {
2343 		case OAUTH_REQENGINE_STREAMS:
2344 #if OAUTH_USE_CURL
2345 		case OAUTH_REQENGINE_CURL:
2346 #endif
2347 			soo->reqengine = reqengine;
2348 			break;
2349 		default:
2350 			soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid request engine specified", NULL, NULL);
2351 	}
2352 }
2353 /* }}} */
2354 
2355 /* {{{ proto bool OAuth::generateSignature(string http_method, string url [, string|array extra_parameters ])
2356    Generate a signature based on the final HTTP method, URL and a string/array of parameters */
2357 SO_METHOD(generateSignature)
2358 {
2359 	php_so_object *soo;
2360 	size_t url_len, http_method_len = 0;
2361 	char *url;
2362 	zval *request_args = NULL;
2363 	char *http_method = NULL;
2364 
2365 	soo = Z_SOO_P(getThis());
2366 
2367 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &http_method, &http_method_len, &url, &url_len, &request_args) == FAILURE) {
2368 		return;
2369 	}
2370 
2371 	if (url_len < 1) {
2372 		RETURN_BOOL(FALSE);
2373 	}
2374 
2375 	if (oauth_fetch(soo, url, http_method, request_args, NULL, NULL, (OAUTH_FETCH_USETOKEN | OAUTH_FETCH_SIGONLY)) < 0) {
2376 		RETURN_BOOL(FALSE);
2377 	} else {
2378 		zend_string_addref(soo->signature);
2379 		RETURN_STR(soo->signature);
2380 	}
2381 }
2382 /* }}} */
2383 
2384 /* {{{ proto bool OAuth::fetch(string protected_resource_url [, string|array extra_parameters [, string request_type [, array request_headers]]])
2385    fetch a protected resource, pass in extra_parameters (array(name => value) or "custom body") */
2386 SO_METHOD(fetch)
2387 {
2388 	php_so_object *soo;
2389 	size_t fetchurl_len, http_method_len = 0;
2390 	char *fetchurl;
2391 	zval zret, *request_args = NULL, *request_headers = NULL;
2392 	char *http_method = NULL;
2393 	long retcode;
2394 
2395 	soo = Z_SOO_P(getThis());
2396 
2397 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|zsa", &fetchurl, &fetchurl_len, &request_args, &http_method, &http_method_len, &request_headers) == FAILURE) {
2398 		return;
2399 	}
2400 
2401 	if (fetchurl_len < 1) {
2402 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid protected resource url length", NULL, NULL);
2403 		RETURN_FALSE;
2404 	}
2405 
2406 	retcode = oauth_fetch(soo, fetchurl, http_method, request_args, request_headers, NULL, OAUTH_FETCH_USETOKEN | OAUTH_OVERRIDE_HTTP_METHOD);
2407 
2408 	ZVAL_STRINGL(&zret, soo->lastresponse.c, soo->lastresponse.len);
2409 	so_set_response_args(soo->properties, &zret, NULL);
2410 
2411 	if ((retcode < 200 || retcode > 206)) {
2412 		RETURN_FALSE;
2413 	} else {
2414 		RETURN_BOOL(TRUE);
2415 	}
2416 }
2417 /* }}} */
2418 
2419 /* {{{ proto array OAuth::getAccessToken(string access_token_url [, string auth_session_handle [, string auth_verifier [, http_method ]]])
2420 	Get access token,
2421 	If the server supports Scalable OAuth pass in the auth_session_handle to refresh the token (http://wiki.oauth.net/ScalableOAuth)
2422 	For 1.0a implementation, a verifier token must be passed; this token is not passed unless a value is explicitly assigned via the function arguments or $_GET/$_POST['oauth_verifier'] is set
2423 */
2424 SO_METHOD(getAccessToken)
2425 {
2426 	php_so_object *soo;
2427 	size_t aturi_len = 0, ash_len = 0, verifier_len_size_t = 0, http_method_len = sizeof(OAUTH_HTTP_METHOD_POST) - 1;
2428 	int verifier_len;
2429 	char *aturi, *ash, *verifier, *http_method = OAUTH_HTTP_METHOD_POST;
2430 	zval zret;
2431 	HashTable *args = NULL;
2432 	long retcode;
2433 
2434 	soo = Z_SOO_P(getThis());
2435 
2436 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss", &aturi, &aturi_len, &ash, &ash_len, &verifier, &verifier_len_size_t, &http_method, &http_method_len) == FAILURE) {
2437 		return;
2438 	}
2439 	verifier_len = verifier_len_size_t;
2440 
2441 	if (aturi_len < 1) {
2442 		soo_handle_error(soo, OAUTH_ERR_INTERNAL_ERROR, "Invalid access token url length", NULL, NULL);
2443 		RETURN_FALSE;
2444 	}
2445 
2446 	if (!verifier_len) {
2447 		/* try to get from _GET/_POST */
2448 		get_request_param(OAUTH_PARAM_VERIFIER, &verifier, &verifier_len);
2449 	}
2450 
2451 	if (ash_len > 0 || verifier_len > 0) {
2452 		ALLOC_HASHTABLE(args);
2453 		zend_hash_init(args, 0, NULL, ZVAL_PTR_DTOR, 0);
2454 		if (ash_len > 0) {
2455 			add_arg_for_req(args, OAUTH_PARAM_ASH, ash);
2456 		}
2457 		if (verifier_len > 0) {
2458 			add_arg_for_req(args, OAUTH_PARAM_VERIFIER, verifier);
2459 		}
2460 	}
2461 
2462 	retcode = oauth_fetch(soo, aturi, oauth_get_http_method(soo, http_method), NULL, NULL, args, OAUTH_FETCH_USETOKEN);
2463 
2464 	if (args) {
2465 		FREE_ARGS_HASH(args);
2466 	}
2467 
2468 	if (retcode != -1 && soo->lastresponse.c) {
2469 		array_init(return_value);
2470 		ZVAL_STRINGL(&zret, soo->lastresponse.c, soo->lastresponse.len);
2471 		so_set_response_args(soo->properties, &zret, return_value);
2472 		return;
2473 	}
2474 	RETURN_FALSE;
2475 }
2476 /* }}} */
2477 
2478 /* {{{ proto array OAuth::getLastResponseInfo(void)
2479    Get information about the last response */
2480 SO_METHOD(getLastResponseInfo)
2481 {
2482 	php_so_object *soo;
2483 	zval *data_ptr;
2484 
2485 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2486 		return;
2487 	}
2488 
2489 	soo = Z_SOO_P(getThis());
2490 
2491 	if ((data_ptr = zend_hash_str_find(soo->properties, OAUTH_ATTR_LAST_RES_INFO, sizeof(OAUTH_ATTR_LAST_RES_INFO) - 1)) != NULL) {
2492 		if (Z_TYPE_P(data_ptr) == IS_ARRAY) {
2493 			convert_to_array_ex(data_ptr);
2494 		}
2495 		RETURN_ZVAL(data_ptr, 1, 0);
2496 	}
2497 	RETURN_FALSE;
2498 }
2499 /* }}} */
2500 
2501 /* {{{ proto array OAuth::getLastResponse(void)
2502    Get last response */
2503 SO_METHOD(getLastResponse)
2504 {
2505 	php_so_object *soo;
2506 
2507 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
2508 		return;
2509 	}
2510 
2511 	soo = Z_SOO_P(getThis());
2512 
2513 	if (soo->lastresponse.c) {
2514 		RETURN_STRINGL(soo->lastresponse.c, soo->lastresponse.len);
2515 	}
2516 }
2517 /* }}} */
2518 
2519 /* {{{ proto string getLastResponseHeaders(void)
2520 Get headers for last response */
2521 SO_METHOD(getLastResponseHeaders)
2522 {
2523 	php_so_object *soo;
2524 
2525 	if (FAILURE==zend_parse_parameters(ZEND_NUM_ARGS(), "")) {
2526 		return;
2527 	}
2528 
2529 	soo = Z_SOO_P(getThis());
2530 	if (soo->headers_in.c) {
2531 		RETURN_STRINGL(soo->headers_in.c, soo->headers_in.len);
2532 	}
2533 	RETURN_FALSE;
2534 }
2535 /* }}} */
2536 
2537 /* {{{ proto string OAuth::getRequestHeader(string http_method, string url [, string|array extra_parameters ])
2538    Generate OAuth header string signature based on the final HTTP method, URL and a string/array of parameters */
2539 SO_METHOD(getRequestHeader)
2540 {
2541 	php_so_object *soo;
2542 	size_t url_len, http_method_len = 0;
2543 	char *url;
2544 	zval *request_args = NULL;
2545 	char *http_method = NULL;
2546 
2547 	soo = Z_SOO_P(getThis());
2548 
2549 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &http_method, &http_method_len, &url, &url_len, &request_args) == FAILURE) {
2550 		return;
2551 	}
2552 
2553 	if (url_len < 1) {
2554 		RETURN_BOOL(FALSE);
2555 	}
2556 
2557 	if (oauth_fetch(soo, url, http_method, request_args, NULL, NULL,
2558 				(OAUTH_FETCH_USETOKEN | OAUTH_FETCH_HEADONLY)) < 0) {
2559 		RETURN_BOOL(FALSE);
2560 	} else {
2561 		RETURN_STRINGL(soo->headers_out.c, soo->headers_out.len);
2562 	}
2563 
2564 	RETURN_FALSE;
2565 }
2566 
2567 /* {{{ arginfo */
2568 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_urlencode, 0, 0, 1)
2569 	ZEND_ARG_INFO(0, uri)
2570 ZEND_END_ARG_INFO()
2571 
2572 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_sbs, 0, 0, 3)
2573 	ZEND_ARG_INFO(0, http_method)
2574 	ZEND_ARG_INFO(0, uri)
2575 	ZEND_ARG_INFO(0, parameters)
2576 ZEND_END_ARG_INFO()
2577 
2578 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth__construct, 0, 0, 2)
2579 	ZEND_ARG_INFO(0, consumer_key)
2580 	ZEND_ARG_INFO(0, consumer_secret)
2581 	ZEND_ARG_INFO(0, signature_method)
2582 	ZEND_ARG_INFO(0, auth_type)
2583 ZEND_END_ARG_INFO()
2584 
2585 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_getrequesttoken, 0, 0, 1)
2586 	ZEND_ARG_INFO(0, request_token_url)
2587 	ZEND_ARG_INFO(0, callback_url)
2588 ZEND_END_ARG_INFO()
2589 
2590 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setversion, 0, 0, 1)
2591 	ZEND_ARG_INFO(0, version)
2592 ZEND_END_ARG_INFO()
2593 
2594 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_noparams, 0, 0, 0)
2595 ZEND_END_ARG_INFO()
2596 
2597 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setauthtype, 0, 0, 1)
2598 	ZEND_ARG_INFO(0, auth_type)
2599 ZEND_END_ARG_INFO()
2600 
2601 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setnonce, 0, 0, 1)
2602 	ZEND_ARG_INFO(0, nonce)
2603 ZEND_END_ARG_INFO()
2604 
2605 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_settimestamp, 0, 0, 1)
2606 	ZEND_ARG_INFO(0, ts)
2607 ZEND_END_ARG_INFO()
2608 
2609 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_settimeout, 0, 0, 1)
2610 	ZEND_ARG_INFO(0, timeout_in_milliseconds)
2611 ZEND_END_ARG_INFO()
2612 
2613 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setcapath, 0, 0, 2)
2614 	ZEND_ARG_INFO(0, ca_path)
2615 	ZEND_ARG_INFO(0, ca_info)
2616 ZEND_END_ARG_INFO()
2617 
2618 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_settoken, 0, 0, 2)
2619 	ZEND_ARG_INFO(0, token)
2620 	ZEND_ARG_INFO(0, token_secret)
2621 ZEND_END_ARG_INFO()
2622 
2623 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setrequestengine, 0, 0, 1)
2624 	ZEND_ARG_INFO(0, reqengine)
2625 ZEND_END_ARG_INFO()
2626 
2627 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_fetch, 0, 0, 1)
2628 	ZEND_ARG_INFO(0, protected_resource_url)
2629 	ZEND_ARG_INFO(0, extra_parameters) /* ARRAY_INFO(1, arg, 0) */
2630 	ZEND_ARG_INFO(0, http_method)
2631 	ZEND_ARG_INFO(0, request_headers)
2632 ZEND_END_ARG_INFO()
2633 
2634 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_getaccesstoken, 0, 0, 1)
2635 	ZEND_ARG_INFO(0, access_token_url)
2636 	ZEND_ARG_INFO(0, auth_session_handle)
2637 	ZEND_ARG_INFO(0, auth_verifier)
2638 ZEND_END_ARG_INFO()
2639 
2640 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setrsacertificate, 0, 0, 1)
2641 	ZEND_ARG_INFO(0, cert)
2642 ZEND_END_ARG_INFO()
2643 
2644 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_gensig, 0, 0, 2)
2645 	ZEND_ARG_INFO(0, http_method)
2646 	ZEND_ARG_INFO(0, url)
2647 	ZEND_ARG_INFO(0, extra_parameters) /* ARRAY_INFO(1, arg, 0) */
2648 ZEND_END_ARG_INFO()
2649 
2650 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_setsslchecks, 0, 0, 1)
2651 	ZEND_ARG_INFO(0, sslcheck)
2652 ZEND_END_ARG_INFO()
2653 
2654 ZEND_BEGIN_ARG_INFO_EX(arginfo_oauth_getrequestheader, 0, 0, 2)
2655 	ZEND_ARG_INFO(0, http_method)
2656 	ZEND_ARG_INFO(0, url)
2657 	ZEND_ARG_INFO(0, extra_parameters) /* ARRAY_INFO(1, arg, 0) */
2658 ZEND_END_ARG_INFO()
2659 
2660 
2661 /* }}} */
2662 
2663 
2664 static zend_function_entry so_functions[] = { /* {{{ */
2665 	SO_ME(__construct,			arginfo_oauth__construct,		ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
2666 	SO_ME(setRSACertificate,	arginfo_oauth_setrsacertificate,	ZEND_ACC_PUBLIC)
2667 	SO_ME(getRequestToken,		arginfo_oauth_getrequesttoken,	ZEND_ACC_PUBLIC)
2668 	SO_ME(getAccessToken,		arginfo_oauth_getaccesstoken,	ZEND_ACC_PUBLIC)
2669 	SO_ME(getLastResponse,		arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2670 	SO_ME(getLastResponseInfo,	arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2671 	SO_ME(getLastResponseHeaders,	arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2672 	SO_ME(setToken,				arginfo_oauth_settoken,			ZEND_ACC_PUBLIC)
2673 	SO_ME(setRequestEngine,		arginfo_oauth_setrequestengine, 		ZEND_ACC_PUBLIC)
2674 	SO_ME(setVersion,			arginfo_oauth_setversion,		ZEND_ACC_PUBLIC)
2675 	SO_ME(setAuthType,			arginfo_oauth_setauthtype,		ZEND_ACC_PUBLIC)
2676 	SO_ME(setNonce,				arginfo_oauth_setnonce,			ZEND_ACC_PUBLIC)
2677 	SO_ME(setTimestamp,			arginfo_oauth_settimestamp,		ZEND_ACC_PUBLIC)
2678 	SO_ME(fetch,				arginfo_oauth_fetch,			ZEND_ACC_PUBLIC)
2679 	SO_ME(enableDebug,			arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2680 	SO_ME(disableDebug,			arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2681 	SO_ME(enableSSLChecks,		arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2682 	SO_ME(disableSSLChecks,		arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2683 	SO_ME(enableRedirects,		arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2684 	SO_ME(disableRedirects,		arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2685 	SO_ME(setCAPath,			arginfo_oauth_setcapath,		ZEND_ACC_PUBLIC)
2686 	SO_ME(getCAPath,			arginfo_oauth_noparams,			ZEND_ACC_PUBLIC)
2687 	SO_ME(generateSignature,	arginfo_oauth_gensig,			ZEND_ACC_PUBLIC)
2688 	SO_ME(setTimeout,			arginfo_oauth_settimeout,		ZEND_ACC_PUBLIC)
2689 	SO_ME(setSSLChecks,			arginfo_oauth_setsslchecks,		ZEND_ACC_PUBLIC)
2690 	SO_ME(getRequestHeader,		arginfo_oauth_getrequestheader,	ZEND_ACC_PUBLIC)
2691 	{NULL, NULL, NULL}
2692 };
2693 /* }}} */
2694 
2695 
2696 #if PHP_VERSION_ID < 80000
2697 zval *oauth_read_member(zval *obj, zval *mem, int type, void **cache_slot, zval *rv) /* {{{ */
2698 {
2699 	php_so_object *soo = Z_SOO_P(obj);
2700     char *name = Z_STRVAL_P(mem);
2701 #else
2702 zval *oauth_read_member(zend_object *obj, zend_string *mem, int type, void **cache_slot, zval *rv) /* {{{ */
2703 {
2704 	php_so_object *soo = so_object_from_obj(obj);
2705     char *name = ZSTR_VAL(mem);
2706 #endif
2707 	zval *return_value = NULL;
2708 
2709 	return_value = std_object_handlers.read_property(obj, mem, type, cache_slot, rv);
2710 
2711 	if(!strcasecmp(name, "debug")) {
2712 		convert_to_boolean(return_value);
2713 		ZVAL_BOOL(return_value, soo->debug);
2714 	} else if(!strcasecmp(name, "sslChecks")) {
2715 		ZVAL_LONG(return_value, soo->sslcheck);
2716 	}
2717 	return return_value;
2718 } /* }}} */
2719 
2720 
2721 #if PHP_VERSION_ID < 80000
2722 static
2723 #if PHP_VERSION_ID >= 70400
2724 zval *
2725 #else
2726 void
2727 #endif
2728 oauth_write_member(zval *obj, zval *mem, zval *value, void **cache_slot) /* {{{ */
2729 {
2730 	php_so_object *soo = Z_SOO_P(obj);
2731 	char *property = Z_STRVAL_P(mem);
2732 #else
2733 static zval *oauth_write_member(zend_object *obj, zend_string *mem, zval *value, void **cache_slot) /* {{{ */
2734 {
2735 	php_so_object *soo = so_object_from_obj(obj);
2736 	char *property = ZSTR_VAL(mem);
2737 #endif
2738 
2739 	if(!strcmp(property,"debug")) {
2740 		soo->debug = Z_TYPE_P(value) == IS_TRUE ? 1 : 0;
2741 	} else if(!strcmp(property,"sslChecks")) {
2742 		soo->sslcheck = Z_LVAL_P(value);
2743 	}
2744 #if PHP_VERSION_ID >= 70400
2745 	return
2746 #endif
2747 			std_object_handlers.write_property(obj, mem, value, cache_slot);
2748 } /* }}} */
2749 
2750 /* {{{ PHP_MINIT_FUNCTION
2751 */
2752 PHP_MINIT_FUNCTION(oauth)
2753 {
2754 	zend_class_entry soce, soo_ex_ce;
2755 
2756 #if OAUTH_USE_CURL
2757 	if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
2758 		return FAILURE;
2759 	}
2760 #endif
2761 
2762 	INIT_CLASS_ENTRY(soce, "OAuth", so_functions);
2763 	soce.create_object = php_so_object_new;
2764 
2765 	soo_class_entry = zend_register_internal_class(&soce);
2766 	memcpy(&so_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2767 	so_object_handlers.offset = XtOffsetOf(php_so_object, zo);
2768 
2769 	so_object_handlers.read_property = oauth_read_member;
2770 	so_object_handlers.write_property = oauth_write_member;
2771 	so_object_handlers.clone_obj = oauth_clone_obj;
2772 	so_object_handlers.free_obj = so_object_free_storage;
2773 
2774 
2775 	zend_declare_property_long(soo_class_entry, "debug", sizeof("debug")-1, 0, ZEND_ACC_PUBLIC);
2776 	zend_declare_property_long(soo_class_entry, "sslChecks", sizeof("sslChecks")-1, 1, ZEND_ACC_PUBLIC);
2777 	zend_declare_property_string(soo_class_entry, "debugInfo", sizeof("debugInfo")-1, "", ZEND_ACC_PUBLIC);
2778 
2779 	INIT_CLASS_ENTRY(soo_ex_ce, "OAuthException", NULL);
2780 
2781 	soo_exception_ce = zend_register_internal_class_ex(&soo_ex_ce, zend_exception_get_default());
2782 	zend_declare_property_null(soo_exception_ce, "lastResponse", sizeof("lastResponse")-1, ZEND_ACC_PUBLIC);
2783 	zend_declare_property_null(soo_exception_ce, "debugInfo", sizeof("debugInfo")-1, ZEND_ACC_PUBLIC);
2784 
2785 	REGISTER_STRING_CONSTANT("OAUTH_SIG_METHOD_HMACSHA1", OAUTH_SIG_METHOD_HMACSHA1, CONST_CS | CONST_PERSISTENT);
2786 	REGISTER_STRING_CONSTANT("OAUTH_SIG_METHOD_HMACSHA256", OAUTH_SIG_METHOD_HMACSHA256, CONST_CS | CONST_PERSISTENT);
2787 	REGISTER_STRING_CONSTANT("OAUTH_SIG_METHOD_RSASHA1", OAUTH_SIG_METHOD_RSASHA1, CONST_CS | CONST_PERSISTENT);
2788 	REGISTER_STRING_CONSTANT("OAUTH_SIG_METHOD_PLAINTEXT", OAUTH_SIG_METHOD_PLAINTEXT, CONST_CS | CONST_PERSISTENT);
2789 	REGISTER_LONG_CONSTANT("OAUTH_AUTH_TYPE_AUTHORIZATION", OAUTH_AUTH_TYPE_AUTHORIZATION, CONST_CS | CONST_PERSISTENT);
2790 	REGISTER_LONG_CONSTANT("OAUTH_AUTH_TYPE_URI", OAUTH_AUTH_TYPE_URI, CONST_CS | CONST_PERSISTENT);
2791 	REGISTER_LONG_CONSTANT("OAUTH_AUTH_TYPE_FORM", OAUTH_AUTH_TYPE_FORM, CONST_CS | CONST_PERSISTENT);
2792 	REGISTER_LONG_CONSTANT("OAUTH_AUTH_TYPE_NONE", OAUTH_AUTH_TYPE_NONE, CONST_CS | CONST_PERSISTENT);
2793 	REGISTER_STRING_CONSTANT("OAUTH_HTTP_METHOD_GET", OAUTH_HTTP_METHOD_GET, CONST_CS | CONST_PERSISTENT);
2794 	REGISTER_STRING_CONSTANT("OAUTH_HTTP_METHOD_POST", OAUTH_HTTP_METHOD_POST, CONST_CS | CONST_PERSISTENT);
2795 	REGISTER_STRING_CONSTANT("OAUTH_HTTP_METHOD_PUT", OAUTH_HTTP_METHOD_PUT, CONST_CS | CONST_PERSISTENT);
2796 	REGISTER_STRING_CONSTANT("OAUTH_HTTP_METHOD_HEAD", OAUTH_HTTP_METHOD_HEAD, CONST_CS | CONST_PERSISTENT);
2797 	REGISTER_STRING_CONSTANT("OAUTH_HTTP_METHOD_DELETE", OAUTH_HTTP_METHOD_DELETE, CONST_CS | CONST_PERSISTENT);
2798 	REGISTER_LONG_CONSTANT("OAUTH_REQENGINE_STREAMS", OAUTH_REQENGINE_STREAMS, CONST_CS | CONST_PERSISTENT);
2799 #ifdef OAUTH_USE_CURL
2800 	REGISTER_LONG_CONSTANT("OAUTH_REQENGINE_CURL", OAUTH_REQENGINE_CURL, CONST_CS | CONST_PERSISTENT);
2801 #endif
2802 	REGISTER_LONG_CONSTANT("OAUTH_SSLCHECK_NONE", OAUTH_SSLCHECK_NONE, CONST_CS | CONST_PERSISTENT);
2803 	REGISTER_LONG_CONSTANT("OAUTH_SSLCHECK_HOST", OAUTH_SSLCHECK_HOST, CONST_CS | CONST_PERSISTENT);
2804 	REGISTER_LONG_CONSTANT("OAUTH_SSLCHECK_PEER", OAUTH_SSLCHECK_PEER, CONST_CS | CONST_PERSISTENT);
2805 	REGISTER_LONG_CONSTANT("OAUTH_SSLCHECK_BOTH", OAUTH_SSLCHECK_BOTH, CONST_CS | CONST_PERSISTENT);
2806 
2807 	oauth_provider_register_class();
2808 	REGISTER_LONG_CONSTANT("OAUTH_OK", OAUTH_OK, CONST_CS | CONST_PERSISTENT);
2809 	REGISTER_LONG_CONSTANT("OAUTH_BAD_NONCE", OAUTH_BAD_NONCE, CONST_CS | CONST_PERSISTENT);
2810 	REGISTER_LONG_CONSTANT("OAUTH_BAD_TIMESTAMP", OAUTH_BAD_TIMESTAMP, CONST_CS | CONST_PERSISTENT);
2811 	REGISTER_LONG_CONSTANT("OAUTH_CONSUMER_KEY_UNKNOWN", OAUTH_CONSUMER_KEY_UNKNOWN, CONST_CS | CONST_PERSISTENT);
2812 	REGISTER_LONG_CONSTANT("OAUTH_CONSUMER_KEY_REFUSED", OAUTH_CONSUMER_KEY_REFUSED, CONST_CS | CONST_PERSISTENT);
2813 	REGISTER_LONG_CONSTANT("OAUTH_INVALID_SIGNATURE", OAUTH_INVALID_SIGNATURE, CONST_CS | CONST_PERSISTENT);
2814 	REGISTER_LONG_CONSTANT("OAUTH_TOKEN_USED", OAUTH_TOKEN_USED, CONST_CS | CONST_PERSISTENT);
2815 	REGISTER_LONG_CONSTANT("OAUTH_TOKEN_EXPIRED", OAUTH_TOKEN_EXPIRED, CONST_CS | CONST_PERSISTENT);
2816 	REGISTER_LONG_CONSTANT("OAUTH_TOKEN_REVOKED", OAUTH_TOKEN_REVOKED, CONST_CS | CONST_PERSISTENT);
2817 	REGISTER_LONG_CONSTANT("OAUTH_TOKEN_REJECTED", OAUTH_TOKEN_REJECTED, CONST_CS | CONST_PERSISTENT);
2818 	REGISTER_LONG_CONSTANT("OAUTH_VERIFIER_INVALID", OAUTH_VERIFIER_INVALID, CONST_CS | CONST_PERSISTENT);
2819 	REGISTER_LONG_CONSTANT("OAUTH_PARAMETER_ABSENT", OAUTH_PARAMETER_ABSENT, CONST_CS | CONST_PERSISTENT);
2820 	REGISTER_LONG_CONSTANT("OAUTH_SIGNATURE_METHOD_REJECTED", OAUTH_SIGNATURE_METHOD_REJECTED, CONST_CS | CONST_PERSISTENT);
2821 	return SUCCESS;
2822 }
2823 /* }}} */
2824 
2825 /* {{{ PHP_MSHUTDOWN_FUNCTION
2826 */
2827 PHP_MSHUTDOWN_FUNCTION(oauth)
2828 {
2829 	soo_class_entry = NULL;
2830 	soo_exception_ce = NULL;
2831 #if OAUTH_USE_CURL
2832 	curl_global_cleanup();
2833 #endif
2834 	return SUCCESS;
2835 }
2836 /* }}} */
2837 
2838 /* {{{ PHP_MINFO_FUNCTION
2839 */
2840 PHP_MINFO_FUNCTION(oauth)
2841 {
2842 	php_info_print_table_start();
2843 	php_info_print_table_header(2, "OAuth support", "enabled");
2844 	php_info_print_table_row(2, "PLAINTEXT support", "enabled");
2845 #if HAVE_OPENSSL_EXT
2846 	php_info_print_table_row(2, "RSA-SHA1 support", "enabled");
2847 #else
2848 	php_info_print_table_row(2, "RSA-SHA1 support", "not supported");
2849 #endif
2850 	php_info_print_table_row(2, "HMAC-SHA1 support", "enabled");
2851 #if OAUTH_USE_CURL
2852 	php_info_print_table_row(2, "Request engine support", "php_streams, curl");
2853 #else
2854 	php_info_print_table_row(2, "Request engine support", "php_streams");
2855 #endif
2856 	php_info_print_table_row(2, "version", OAUTH_EXT_VER);
2857 	php_info_print_table_end();
2858 }
2859 /* }}} */
2860 
2861 /* TODO expose a function for base sig string */
2862 zend_function_entry oauth_functions[] = { /* {{{ */
2863 	PHP_FE(oauth_urlencode,		arginfo_oauth_urlencode)
2864 	PHP_FE(oauth_get_sbs,		arginfo_oauth_sbs)
2865 	{ NULL, NULL, NULL }
2866 };
2867 /* }}} */
2868 
2869 /* {{{ oauth_module_entry */
2870 zend_module_entry oauth_module_entry = {
2871 	STANDARD_MODULE_HEADER_EX, NULL,
2872 	NULL,
2873 	"OAuth",
2874 	oauth_functions,
2875 	PHP_MINIT(oauth),
2876 	PHP_MSHUTDOWN(oauth),
2877 	NULL,
2878 	NULL,
2879 	PHP_MINFO(oauth),
2880 	OAUTH_EXT_VER,
2881 	STANDARD_MODULE_PROPERTIES
2882 };
2883 /* }}} */
2884 
2885 #if COMPILE_DL_OAUTH
2886 ZEND_GET_MODULE(oauth)
2887 #endif
2888 
2889 /**
2890  * Local Variables:
2891  * c-basic-offset: 4
2892  * tab-width: 4
2893  * indent-tabs-mode: t
2894  * End:
2895  * vim600: fdm=marker
2896  * vim: noet sw=4 ts=4 noexpandtab
2897  */
2898