X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=http_functions.c;h=e894b19ebe4ec892a94e3fb7fab5f0c1533c5b5b;hp=b1e2256c6ab72dc129a2454ee39786c7776d10e9;hb=dbc4ec02cd319000b5c8589910047cfe6d5738be;hpb=af3c9945f927e8e2ebdc8e7707b70ec3ba7addfc diff --git a/http_functions.c b/http_functions.c index b1e2256..e894b19 100644 --- a/http_functions.c +++ b/http_functions.c @@ -62,7 +62,7 @@ PHP_FUNCTION(http_date) } /* }}} */ -/* {{{ proto string http_build_url(mixed url[, mixed parts[, int flags = HTTP_URL_REPLACE[, array &new_url]]]) +/* {{{ proto string http_build_url([mixed url[, mixed parts[, int flags = HTTP_URL_REPLACE[, array &new_url]]]]) * * Build an URL. * @@ -77,7 +77,7 @@ PHP_FUNCTION(http_date) *
  *	- HTTP_URL_REPLACE:        (default) set parts of the second url will replace the parts in the first
  *	- HTTP_URL_JOIN_PATH:      the path of the second url will be merged into the one of the first
- *	- HTTP_URL_JOIN_QUERY:     the two querystrings will be merged naivly; no replacements are done
+ *	- HTTP_URL_JOIN_QUERY:     the two querystrings will be merged recursively
  *	- HTTP_URL_STRIP_USER:     the user part will not appear in the result
  *	- HTTP_URL_STRIP_PASS:     the password part will not appear in the result
  *	- HTTP_URL_STRIP_AUTH:     neither the user nor the password part will appear in the result
@@ -112,13 +112,13 @@ PHP_FUNCTION(http_build_url)
 	zval *z_old_url = NULL, *z_new_url = NULL, *z_composed_url = NULL;
 	php_url *old_url = NULL, *new_url = NULL, *composed_url = NULL;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|z/lz", &z_old_url, &z_new_url, &flags, &z_composed_url) != SUCCESS) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!/z!/lz", &z_old_url, &z_new_url, &flags, &z_composed_url) != SUCCESS) {
 		RETURN_FALSE;
 	}
 	
 	if (z_new_url) {
 		if (Z_TYPE_P(z_new_url) == IS_ARRAY || Z_TYPE_P(z_new_url) == IS_OBJECT) {
-			new_url = array2url(HASH_OF(z_new_url));
+			new_url = http_url_from_struct(NULL, HASH_OF(z_new_url));
 		} else {
 			convert_to_string(z_new_url);
 			if (!(new_url = php_url_parse_ex(Z_STRVAL_P(z_new_url), Z_STRLEN_P(z_new_url)))) {
@@ -128,48 +128,24 @@ PHP_FUNCTION(http_build_url)
 		}
 	}
 	
-	if (Z_TYPE_P(z_old_url) == IS_ARRAY || Z_TYPE_P(z_old_url) == IS_OBJECT) {
-		old_url = array2url(HASH_OF(z_old_url));
-	} else {
-		convert_to_string(z_old_url);
-		if (!(old_url = php_url_parse_ex(Z_STRVAL_P(z_old_url), Z_STRLEN_P(z_old_url)))) {
-			if (new_url) {
-				php_url_free(new_url);
+	if (z_old_url) {
+		if (Z_TYPE_P(z_old_url) == IS_ARRAY || Z_TYPE_P(z_old_url) == IS_OBJECT) {
+			old_url = http_url_from_struct(NULL, HASH_OF(z_old_url));
+		} else {
+			convert_to_string(z_old_url);
+			if (!(old_url = php_url_parse_ex(Z_STRVAL_P(z_old_url), Z_STRLEN_P(z_old_url)))) {
+				if (new_url) {
+					php_url_free(new_url);
+				}
+				http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_old_url));
+				RETURN_FALSE;
 			}
-			http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_old_url));
-			RETURN_FALSE;
 		}
 	}
 	
 	if (z_composed_url) {
 		http_build_url(flags, old_url, new_url, &composed_url, &url_str, &url_len);
-		
-		zval_dtor(z_composed_url);
-		array_init(z_composed_url);
-		if (composed_url->scheme) {
-			add_assoc_string(z_composed_url, "scheme", composed_url->scheme, 1);
-		}
-		if (composed_url->user) {
-			add_assoc_string(z_composed_url, "user", composed_url->user, 1);
-		}
-		if (composed_url->pass) {
-			add_assoc_string(z_composed_url, "pass", composed_url->pass, 1);
-		}
-		if (composed_url->host) {
-			add_assoc_string(z_composed_url, "host", composed_url->host, 1);
-		}
-		if (composed_url->port) {
-			add_assoc_long(z_composed_url, "port", composed_url->port);
-		}
-		if (composed_url->path) {
-			add_assoc_string(z_composed_url, "path", composed_url->path, 1);
-		}
-		if (composed_url->query) {
-			add_assoc_string(z_composed_url, "query", composed_url->query, 1);
-		}
-		if (composed_url->fragment) {
-			add_assoc_string(z_composed_url, "fragment", composed_url->fragment, 1);
-		}
+		http_url_tostruct(composed_url, z_composed_url);
 		php_url_free(composed_url);
 	} else {
 		http_build_url(flags, old_url, new_url, NULL, &url_str, &url_len);
@@ -178,7 +154,9 @@ PHP_FUNCTION(http_build_url)
 	if (new_url) {
 		php_url_free(new_url);
 	}
-	php_url_free(old_url);
+	if (old_url) {
+		php_url_free(old_url);
+	}
 	
 	RETURN_STRINGL(url_str, url_len, 0);
 }
@@ -250,7 +228,7 @@ PHP_FUNCTION(http_build_str)
 		zval **value; \
 		 \
 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported)); \
-		if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void **) &value)) { \
+		if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void *) &value)) { \
 			RETVAL_ZVAL(*value, 1, 0); \
 		} else { \
 			RETVAL_NULL(); \
@@ -739,7 +717,7 @@ PHP_FUNCTION(http_throttle)
  */
 PHP_FUNCTION(http_redirect)
 {
-	int url_len;
+	int url_len = 0;
 	size_t query_len = 0;
 	zend_bool session = 0, free_params = 0;
 	zval *params = NULL;
@@ -803,20 +781,18 @@ PHP_FUNCTION(http_redirect)
 		FREE_ZVAL(params);
 	}
 	
-	switch (status)
-	{
+	switch (status) {
 		case 300:
 			RETVAL_SUCCESS(http_send_status_header(status, LOC));
 			efree(LOC);
 			return;
-		break;
 		
 		case HTTP_REDIRECT_PERM:
 		case HTTP_REDIRECT_FOUND:
 		case HTTP_REDIRECT_POST:
 		case HTTP_REDIRECT_PROXY:
 		case HTTP_REDIRECT_TEMP:
-		break;
+			break;
 		
 		case 306:
 		default:
@@ -829,7 +805,7 @@ PHP_FUNCTION(http_redirect)
 			} else {
 				status = HTTP_REDIRECT_FOUND;
 			}
-		break;
+			break;
 	}
 	
 	RETURN_SUCCESS(http_exit_ex(status, LOC, RED, 1));
@@ -1034,6 +1010,7 @@ PHP_FUNCTION(http_parse_headers)
 	array_init(return_value);
 	if (SUCCESS != http_parse_headers(header, return_value)) {
 		zval_dtor(return_value);
+		http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Failed to parse headers");
 		RETURN_FALSE;
 	}
 }
@@ -1112,6 +1089,33 @@ PHP_FUNCTION(http_parse_cookie)
 	}
 }
 
+/* {{{ proto object http_parse_params(string param[, int flags = HTTP_PARAMS_DEFAULT])
+ *
+ * Parse parameter list.
+ */
+PHP_FUNCTION(http_parse_params)
+{
+	char *param;
+	int param_len;
+	zval *params;
+	long flags = HTTP_PARAMS_DEFAULT;
+	
+	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", ¶m, ¶m_len, &flags)) {
+		RETURN_FALSE;
+	}
+	
+	params = ecalloc(1, sizeof(zval));
+	array_init(params);
+	if (SUCCESS != http_parse_params(param, flags, Z_ARRVAL_P(params))) {
+		zval_dtor(params);
+		FREE_ZVAL(params);
+		RETURN_FALSE;
+	}
+	object_init(return_value);
+	add_property_zval(return_value, "params", params);
+}
+/* }}} */
+
 /* {{{ proto array http_get_request_headers(void)
  *
  * Get a list of incoming HTTP headers.
@@ -1206,7 +1210,7 @@ PHP_FUNCTION(http_match_request_header)
 		zval **bodyonly; \
 		 \
 		/* check if only the body should be returned */ \
-		if (options && (SUCCESS == zend_hash_find(Z_ARRVAL_P(options), "bodyonly", sizeof("bodyonly"), (void **) &bodyonly)) && zval_is_true(*bodyonly)) { \
+		if (options && (SUCCESS == zend_hash_find(Z_ARRVAL_P(options), "bodyonly", sizeof("bodyonly"), (void *) &bodyonly)) && zval_is_true(*bodyonly)) { \
 			http_message *msg = http_message_parse(PHPSTR_VAL(&request.conv.response), PHPSTR_LEN(&request.conv.response)); \
 			 \
 			if (msg) { \
@@ -1230,6 +1234,7 @@ PHP_FUNCTION(http_match_request_header)
  *                      redirects to a different host
  *  - proxyhost:        string, proxy host in "host[:port]" format
  *  - proxyport:        int, use another proxy port as specified in proxyhost
+ *  - proxytype:        int, HTTP_PROXY_HTTP, SOCKS4 or SOCKS5
  *  - proxyauth:        string, proxy credentials in "user:pass" format
  *  - proxyauthtype:    int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
  *  - httpauth:         string, http credentials in "user:pass" format
@@ -1244,11 +1249,13 @@ PHP_FUNCTION(http_match_request_header)
  *  - cookies:          array, list of cookies as associative array
  *                      like array("cookie" => "value")
  *  - encodecookies:    bool, whether to urlencode the cookies (default: true)
- *  - resetcookies:     bool, wheter to reset the cookies
  *  - cookiestore:      string, path to a file where cookies are/will be stored
- *  - cookiesession:    bool, accept (true) or reset (false) sessioncookies
+ *  - cookiesession:    bool, don't load session cookies from cookiestore if TRUE
  *  - resume:           int, byte offset to start the download from;
  *                      if the server supports ranges
+ *  - range:            array, array of arrays, each containing two integers,
+ *                      specifying the ranges to download if server support is
+ *                      given; only recognized if the resume option is empty
  *  - maxfilesize:      int, maximum file size that should be downloaded;
  *                      has no effect, if the size of the requested entity is not known
  *  - lastmodified:     int, timestamp for If-(Un)Modified-Since header
@@ -1502,7 +1509,7 @@ PHP_FUNCTION(http_put_file)
 		RETURN_FALSE;
 	}
 
-	if (!(stream = php_stream_open_wrapper(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, HTTP_DEFAULT_STREAM_CONTEXT))) {
+	if (!(stream = php_stream_open_wrapper_ex(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT))) {
 		RETURN_FALSE;
 	}
 	if (php_stream_stat(stream, &ssb)) {
@@ -1517,12 +1524,8 @@ PHP_FUNCTION(http_put_file)
 
 	RETVAL_FALSE;
 
-	body.type = HTTP_REQUEST_BODY_UPLOADFILE;
-	body.data = stream;
-	body.size = ssb.sb.st_size;
-
 	http_request_init_ex(&request, NULL, HTTP_PUT, URL);
-	request.body = &body;
+	request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1);
 	if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
 		http_request_exec(&request);
 		if (info) {
@@ -1530,8 +1533,6 @@ PHP_FUNCTION(http_put_file)
 		}
 		RETVAL_RESPONSE_OR_BODY(request);
 	}
-	http_request_body_dtor(&body);
-	request.body = NULL;
 	http_request_dtor(&request);
 }
 /* }}} */
@@ -1544,7 +1545,7 @@ PHP_FUNCTION(http_put_file)
  * opened stream, from which the data to upload should be read.
  * See http_get() for a full list of available options.
  * 
- * Returns the HTTP response(s) as string on success. or FALSE on failure.
+ * Returns the HTTP response(s) as string on success, or FALSE on failure.
  */
 PHP_FUNCTION(http_put_stream)
 {
@@ -1572,12 +1573,49 @@ PHP_FUNCTION(http_put_stream)
 
 	RETVAL_FALSE;
 
-	body.type = HTTP_REQUEST_BODY_UPLOADFILE;
-	body.data = stream;
-	body.size = ssb.sb.st_size;
+	http_request_init_ex(&request, NULL, HTTP_PUT, URL);
+	request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 0);
+	if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
+		http_request_exec(&request);
+		if (info) {
+			http_request_info(&request, Z_ARRVAL_P(info));
+		}
+		RETVAL_RESPONSE_OR_BODY(request);
+	}
+	http_request_dtor(&request);
+}
+/* }}} */
 
-	http_request_init_ex(&request, NULL, HTTP_POST, URL);
-	request.body = &body;
+/* {{{ proto string http_put_data(string url, string data[, array options[, array &info]])
+ *
+ * Performs an HTTP PUT request on the supplied url.
+ * 
+ * Expects the second parameter to be a string containing the data to upload.
+ * See http_get() for a full list of available options.
+ * 
+ * Returns the HTTP response(s) as string on success, or FALSE on failure.
+ */
+PHP_FUNCTION(http_put_data)
+{
+	char *URL, *data;
+	int URL_len, data_len;
+	zval *options = NULL, *info = NULL;
+	http_request_body body;
+	http_request request;
+	
+	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &data, &data_len, &options, &info)) {
+		RETURN_FALSE;
+	}
+	
+	if (info) {
+		zval_dtor(info);
+		array_init(info);
+	}
+	
+	RETVAL_FALSE;
+	
+	http_request_init_ex(&request, NULL, HTTP_PUT, URL);
+	request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0);
 	if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
 		http_request_exec(&request);
 		if (info) {
@@ -1585,10 +1623,146 @@ PHP_FUNCTION(http_put_stream)
 		}
 		RETVAL_RESPONSE_OR_BODY(request);
 	}
-	request.body = NULL;
 	http_request_dtor(&request);
 }
 /* }}} */
+
+/* {{{ proto string http_request(int method, string url[, string body[, array options[, array &info]]])
+ *
+ * Performs a custom HTTP request on the supplied url.
+ *
+ * Expects the first parameter to be an integer specifying the request method to use.
+ * Accepts an optional third string parameter containing the raw request body.
+ * See http_get() for a full list of available options.
+ *
+ * Returns the HTTP response(s) as string on success, or FALSE on failure.
+ */
+PHP_FUNCTION(http_request)
+{
+	long meth;
+	char *URL, *data = NULL;
+	int URL_len, data_len = 0;
+	zval *options = NULL, *info = NULL;
+	http_request_body body;
+	http_request request;
+	
+	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|sa/!z", &meth, &URL, &URL_len, &data, &data_len, &options, &info)) {
+		RETURN_FALSE;
+	}
+	
+	if (info) {
+		zval_dtor(info);
+		array_init(info);
+	}
+	
+	RETVAL_FALSE;
+	
+	http_request_init_ex(&request, NULL, meth, URL);
+	request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0);
+	if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
+		http_request_exec(&request);
+		if (info) {
+			http_request_info(&request, Z_ARRVAL_P(info));
+		}
+		RETVAL_RESPONSE_OR_BODY(request);
+	}
+	http_request_dtor(&request);
+}
+/* }}} */
+
+static char *file_get_contents(char *file, size_t *len TSRMLS_DC)
+{
+	php_stream *s = NULL;
+	char *buf = NULL;
+	
+	if ((s = php_stream_open_wrapper_ex(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT))) {
+		*len = php_stream_copy_to_mem(s, &buf, (size_t) -1, 0);
+		php_stream_close(s);
+	} else {
+		*len = 0;
+	}
+	return buf;
+}
+struct FormData {
+	struct FormData *next;
+	int type;
+	char *line;
+	size_t length;
+};
+CURLcode Curl_getFormData(struct FormData **, struct curl_httppost *post, curl_off_t *size);
+
+/* {{{ proto string http_request_body_encode(array fields, array files)
+ *
+ * Generate x-www-form-urlencoded resp. form-data encoded request body.
+ *
+ * Returns encoded string on success, or FALSE on failure.
+ */
+PHP_FUNCTION(http_request_body_encode)
+{
+	zval *fields = NULL, *files = NULL;
+	HashTable *fields_ht, *files_ht;
+	http_request_body body;
+	phpstr rbuf;
+	struct FormData *data, *ptr;
+	curl_off_t size;
+	char *fdata = NULL;
+	size_t fsize = 0;
+	CURLcode rc;
+	int fgc_error = 0;
+	
+	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!", &fields, &files)) {
+		RETURN_FALSE;
+	}
+	
+	fields_ht = (fields && Z_TYPE_P(fields) == IS_ARRAY) ? Z_ARRVAL_P(fields) : NULL;
+	files_ht = (files && Z_TYPE_P(files) == IS_ARRAY) ? Z_ARRVAL_P(files) : NULL;
+	if (!http_request_body_fill(&body, fields_ht, files_ht)) {
+		RETURN_FALSE;
+	}
+	
+	switch (body.type) {
+		case HTTP_REQUEST_BODY_CURLPOST:
+			if (CURLE_OK != (rc = Curl_getFormData(&data, body.data, &size))) {
+				http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Could not encode request body: %s", curl_easy_strerror(rc));
+				RETVAL_FALSE;
+			} else {
+				phpstr_init_ex(&rbuf, (size_t) size, PHPSTR_INIT_PREALLOC);
+				for (ptr = data; ptr; ptr = ptr->next) {
+					if (!fgc_error) {
+						if (ptr->type) {
+							if ((fdata = file_get_contents(ptr->line, &fsize TSRMLS_CC))) {
+								phpstr_append(&rbuf, fdata, fsize);
+								efree(fdata);
+							} else {
+								fgc_error = 1;
+							}
+						} else {
+							phpstr_append(&rbuf, ptr->line, ptr->length);
+						}
+					}
+					curl_free(ptr->line);
+				}
+				curl_free(data);
+				if (fgc_error) {
+					phpstr_dtor(&rbuf);
+					RETVAL_FALSE;
+				} else {
+					RETVAL_PHPSTR_VAL(&rbuf);
+				}
+			}
+			http_request_body_dtor(&body);
+			break;
+		
+		case HTTP_REQUEST_BODY_CSTRING:
+			RETVAL_STRINGL(body.data, body.size, 0);
+			break;
+		
+		default:
+			http_request_body_dtor(&body);
+			RETVAL_FALSE;
+			break;
+	}
+}
 #endif /* HTTP_HAVE_CURL */
 /* }}} HAVE_CURL */
 
@@ -1633,8 +1807,7 @@ PHP_FUNCTION(http_request_method_unregister)
 		RETURN_FALSE;
 	}
 
-	switch (Z_TYPE_P(method))
-	{
+	switch (Z_TYPE_P(method)) {
 		case IS_OBJECT:
 			convert_to_string(method);
 		case IS_STRING:
@@ -1666,15 +1839,14 @@ PHP_FUNCTION(http_request_method_unregister)
  */
 PHP_FUNCTION(http_request_method_exists)
 {
-	IF_RETVAL_USED {
+	if (return_value_used) {
 		zval *method;
 
 		if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) {
 			RETURN_FALSE;
 		}
 
-		switch (Z_TYPE_P(method))
-		{
+		switch (Z_TYPE_P(method)) {
 			case IS_OBJECT:
 				convert_to_string(method);
 			case IS_STRING:
@@ -1702,7 +1874,7 @@ PHP_FUNCTION(http_request_method_exists)
  */
 PHP_FUNCTION(http_request_method_name)
 {
-	IF_RETVAL_USED {
+	if (return_value_used) {
 		long method;
 
 		if ((SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &method)) || (method < 0)) {