-/* {{{ STATUS http_send_ranges(zval *, void *, size_t, http_send_mode) */
-PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC)
-{
- int c;
- long **begin, **end;
- zval **zrange;
-
- /* Send HTTP 206 Partial Content */
- http_send_status(206);
-
- /* single range */
- if ((c = zend_hash_num_elements(Z_ARRVAL_P(zranges))) == 1) {
- char range_header[256] = {0};
-
- zend_hash_index_find(Z_ARRVAL_P(zranges), 0, (void **) &zrange);
- zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin);
- zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end);
-
- /* send content range header */
- snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", **begin, **end, size);
- http_send_header(range_header);
-
- /* send requested chunk */
- return http_send_chunk(data, **begin, **end + 1, mode);
- }
-
- /* multi range */
- else {
- int i;
- char bound[23] = {0}, preface[1024] = {0},
- multi_header[68] = "Content-Type: multipart/byteranges; boundary=";
-
- snprintf(bound, 22, "--%d%0.9f", time(NULL), php_combined_lcg(TSRMLS_C));
- strncat(multi_header, bound + 2, 21);
- http_send_header(multi_header);
-
- /* send each requested chunk */
- for ( i = 0, zend_hash_internal_pointer_reset(Z_ARRVAL_P(zranges));
- i < c;
- i++, zend_hash_move_forward(Z_ARRVAL_P(zranges))) {
- if ( HASH_KEY_NON_EXISTANT == zend_hash_get_current_data(
- Z_ARRVAL_P(zranges), (void **) &zrange) ||
- SUCCESS != zend_hash_index_find(
- Z_ARRVAL_PP(zrange), 0, (void **) &begin) ||
- SUCCESS != zend_hash_index_find(
- Z_ARRVAL_PP(zrange), 1, (void **) &end)) {
- break;
- }
-
- snprintf(preface, 1023,
- HTTP_CRLF "%s"
- HTTP_CRLF "Content-Type: %s"
- HTTP_CRLF "Content-Range: bytes %ld-%ld/%ld"
- HTTP_CRLF
- HTTP_CRLF,
-
- bound,
- HTTP_G(ctype) ? HTTP_G(ctype) : "application/x-octetstream",
- **begin,
- **end,
- size
- );
-
- php_body_write(preface, strlen(preface) TSRMLS_CC);
- http_send_chunk(data, **begin, **end + 1, mode);
- }
-
- /* write boundary once more */
- php_body_write(HTTP_CRLF, 2 TSRMLS_CC);
- php_body_write(bound, strlen(bound) TSRMLS_CC);
-
- return SUCCESS;
- }
-}
-/* }}} */
-
-/* {{{ STATUS http_send(void *, sizezo_t, http_send_mode) */
-PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
- const http_send_mode data_mode TSRMLS_DC)
-{
- char *new_etag = NULL;
- int is_range_request = http_is_range_request();
-
- if (!data_ptr) {
- return FAILURE;
- }
-
- /* etag handling */
- if (HTTP_G(etag_started)) {
- new_etag = (char *) emalloc(33);
-
- /* never ever use the output to compute the ETag if http_send() is used */
- HTTP_G(etag_started) = 0;
- php_end_ob_buffer(0, 0 TSRMLS_CC);
- if (NULL == http_etag(&new_etag, data_ptr, data_size, data_mode)) {
- efree(new_etag);
- return FAILURE;
- }
-
- /* send 304 Not Modified if etag matches */
- if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) {
- efree(new_etag);
- return http_send_status(304);
- }
-
- http_send_etag(new_etag, 32);
- efree(new_etag);
- }
-
- /* send 304 Not Modified if last-modified matches*/
- if ((!is_range_request) && http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) {
- return http_send_status(304);
- }
-
- if (is_range_request) {
-
- /* only send ranges if entity hasn't changed */
- if (
- ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_MATCH", 13)) ||
- http_etag_match("HTTP_IF_MATCH", HTTP_G(etag)))
- &&
- ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_UNMODIFIED_SINCE", 25)) ||
- http_modified_match("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod)))
- ) {
-
- STATUS result = FAILURE;
- zval *zranges = NULL;
- MAKE_STD_ZVAL(zranges);
- array_init(zranges);
-
- switch (http_get_request_ranges(zranges, data_size))
- {
- case RANGE_NO:
- zval_dtor(zranges);
- efree(zranges);
- /* go ahead and send all */
- break;
-
- case RANGE_OK:
- result = http_send_ranges(zranges, data_ptr, data_size, data_mode);
- zval_dtor(zranges);
- efree(zranges);
- return result;
- break;
-
- case RANGE_ERR:
- zval_dtor(zranges);
- efree(zranges);
- http_send_status(416);
- return FAILURE;
- break;
-
- default:
- return FAILURE;
- break;
- }
- }
- }
- /* send all */
- return http_send_chunk(data_ptr, 0, data_size, data_mode);
-}
-/* }}} */
-
-/* {{{ STATUS http_send_data(zval *) */
-PHP_HTTP_API STATUS _http_send_data(const zval *zdata TSRMLS_DC)
-{
- if (!Z_STRLEN_P(zdata)) {
- return SUCCESS;
- }
- if (!Z_STRVAL_P(zdata)) {
- return FAILURE;
- }
-
- return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
-}
-/* }}} */
-
-/* {{{ STATUS http_send_stream(php_stream *) */
-PHP_HTTP_API STATUS _http_send_stream(const php_stream *file TSRMLS_DC)
-{
- if (php_stream_stat((php_stream *) file, &HTTP_G(ssb))) {
- return FAILURE;
- }
-
- return http_send(file, HTTP_G(ssb).sb.st_size, SEND_RSRC);
-}
-/* }}} */
-
-/* {{{ STATUS http_send_file(zval *) */
-PHP_HTTP_API STATUS _http_send_file(const zval *zfile TSRMLS_DC)
-{
- php_stream *file;
- STATUS ret;
-
- if (!Z_STRLEN_P(zfile)) {
- return FAILURE;
- }
-
- if (!(file = php_stream_open_wrapper(Z_STRVAL_P(zfile), "rb",
- REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL))) {
- return FAILURE;
- }
-
- ret = http_send_stream(file);
- php_stream_close(file);
- return ret;
-}
-/* }}} */
-
-/* {{{ proto STATUS http_chunked_decode(char *, size_t, char **, size_t *) */
-PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded,
- const size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
-{
- const char *e_ptr;
- char *d_ptr;
-
- *decoded_len = 0;
- *decoded = (char *) ecalloc(encoded_len, 1);
- d_ptr = *decoded;
- e_ptr = encoded;
-
- while (((e_ptr - encoded) - encoded_len) > 0) {
- char hex_len[9] = {0};
- size_t chunk_len = 0;
- int i = 0;
-
- /* read in chunk size */
- while (isxdigit(*e_ptr)) {
- if (i == 9) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Chunk size is too long: 0x%s...", hex_len);
- efree(*decoded);
- return FAILURE;
- }
- hex_len[i++] = *e_ptr++;
- }
-
- /* reached the end */
- if (!strcmp(hex_len, "0")) {
- break;
- }
-
- /* new line */
- if (strncmp(e_ptr, HTTP_CRLF, 2)) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Invalid character (expected 0x0D 0x0A; got: %x %x)",
- *e_ptr, *(e_ptr + 1));
- efree(*decoded);
- return FAILURE;
- }
-
- /* hex to long */
- {
- char *error = NULL;
- chunk_len = strtol(hex_len, &error, 16);
- if (error == hex_len) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Invalid chunk size string: '%s'", hex_len);
- efree(*decoded);
- return FAILURE;
- }
- }
-
- memcpy(d_ptr, e_ptr += 2, chunk_len);
- d_ptr += chunk_len;
- e_ptr += chunk_len + 2;
- *decoded_len += chunk_len;
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ proto STATUS http_split_response(zval *, zval *, zval *) */
-PHP_HTTP_API STATUS _http_split_response(const zval *zresponse, zval *zheaders,
- zval *zbody TSRMLS_DC)
-{
- char *header, *response, *body = NULL;
- long response_len = Z_STRLEN_P(zresponse);
- header = response = Z_STRVAL_P(zresponse);
-
- while ((response - Z_STRVAL_P(zresponse) + 3) < response_len) {
- if ( (*response++ == '\r') &&
- (*response++ == '\n') &&
- (*response++ == '\r') &&
- (*response++ == '\n')) {
- body = response;
- break;
- }
- }
-
- if (body && (response_len - (body - header))) {
- ZVAL_STRINGL(zbody, body, response_len - (body - header) - 1, 1);
- } else {
- Z_TYPE_P(zbody) = IS_NULL;
- }
-
- return http_parse_headers(header, body - Z_STRVAL_P(zresponse), zheaders);
-}
-/* }}} */
-
-/* {{{ STATUS http_parse_headers(char *, long, zval *) */
-PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *array TSRMLS_DC)
-{
- char *colon = NULL, *line = NULL, *begin = header;
-
- if (header_len < 8) {
- return FAILURE;
- }
-
- /* status code */
- if (!strncmp(header, "HTTP/1.", 7)) {
- char *end = strstr(header, HTTP_CRLF);
- add_assoc_stringl(array, "Status",
- header + strlen("HTTP/1.x "),
- end - (header + strlen("HTTP/1.x ")), 1);
- header = end + 2;
- }
-
- line = header;
-
- while (header_len > (line - begin)) {
- switch (*line++)
- {
- case 0:
- case '\n':
- if (colon && (*line != ' ') && (*line != '\t')) {
- char *key = estrndup(header, colon - header);
- add_assoc_stringl(array, key, colon + 2, line - colon - 4, 1);
- efree(key);
-
- colon = NULL;
- header += line - header;
- }
- break;
-
- case ':':
- if (!colon) {
- colon = line - 1;
- }
- break;
- }
- }
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ HAVE_CURL */
-#ifdef HTTP_HAVE_CURL
-
-/* {{{ STATUS http_get(char *, HashTable *, HashTable *, char **, size_t *) */
-PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options,
- HashTable *info, char **data, size_t *data_len TSRMLS_DC)
-{
- CURL *ch = curl_easy_init();
-
- if (!ch) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
- return FAILURE;
- }
-
- http_curl_initbuf(CURLBUF_EVRY);
- http_curl_setopts(ch, URL, options);
-
- if (CURLE_OK != curl_easy_perform(ch)) {
- curl_easy_cleanup(ch);
- http_curl_freebuf(CURLBUF_EVRY);
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
- return FAILURE;
- }
- if (info) {
- http_curl_getinfo(ch, info);
- }
- curl_easy_cleanup(ch);
-
- http_curl_movebuf(CURLBUF_EVRY, data, data_len);
-
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ STATUS http_head(char *, HashTable *, HashTable *, char **data, size_t *) */
-PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options,
- HashTable *info, char **data, size_t *data_len TSRMLS_DC)
-{
- CURL *ch = curl_easy_init();
-
- if (!ch) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
- return FAILURE;
- }
-
- http_curl_initbuf(CURLBUF_HDRS);
- http_curl_setopts(ch, URL, options);
- curl_easy_setopt(ch, CURLOPT_NOBODY, 1);
-
- if (CURLE_OK != curl_easy_perform(ch)) {
- curl_easy_cleanup(ch);
- http_curl_freebuf(CURLBUF_HDRS);
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
- return FAILURE;
- }
- if (info) {
- http_curl_getinfo(ch, info);
- }
- curl_easy_cleanup(ch);
-
- http_curl_movebuf(CURLBUF_HDRS, data, data_len);
-
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ STATUS http_post_data(char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */
-PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata,
- size_t postdata_len, HashTable *options, HashTable *info, char **data,
- size_t *data_len TSRMLS_DC)
-{
- CURL *ch = curl_easy_init();
-
- if (!ch) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
- return FAILURE;
- }
-
- http_curl_initbuf(CURLBUF_EVRY);
- http_curl_setopts(ch, URL, options);
- curl_easy_setopt(ch, CURLOPT_POST, 1);
- curl_easy_setopt(ch, CURLOPT_POSTFIELDS, postdata);
- curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, postdata_len);
-
- if (CURLE_OK != curl_easy_perform(ch)) {
- curl_easy_cleanup(ch);
- http_curl_freebuf(CURLBUF_EVRY);
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
- return FAILURE;
- }
- if (info) {
- http_curl_getinfo(ch, info);
- }
- curl_easy_cleanup(ch);
-
- http_curl_movebuf(CURLBUF_EVRY, data, data_len);
-
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ STATUS http_post_array(char *, HashTable *, HashTable *, HashTable *, char **, size_t *) */
-PHP_HTTP_API STATUS _http_post_array(const char *URL, HashTable *postarray,
- HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC)
-{
- smart_str qstr = {0};
- STATUS status;
-
- if (php_url_encode_hash_ex(postarray, &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) {
- if (qstr.c) {
- efree(qstr.c);
- }
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode post data");
- return FAILURE;
- }
- smart_str_0(&qstr);
-
- status = http_post_data(URL, qstr.c, qstr.len, options, info, data, data_len);
- if (qstr.c) {
- efree(qstr.c);
- }
- return status;
-}
-/* }}} */
-
-#endif
-/* }}} HAVE_CURL */
-
-/* {{{ void http_auth_header(char *, char*) */
-PHP_HTTP_API void _http_auth_header(const char *type, const char *realm TSRMLS_DC)
-{
- char realm_header[1024];
- snprintf(realm_header, 1024, "WWW-Authenticate: %s realm=\"%s\"", type, realm);
- http_send_status_header(401, realm_header);
-}
-/* }}} */
-
-/* {{{ STATUS http_auth_credentials(char **, char **) */
-PHP_HTTP_API STATUS _http_auth_credentials(char **user, char **pass TSRMLS_DC)
-{
- if (strncmp(sapi_module.name, "isapi", 5)) {
- zval *zuser, *zpass;
-
- HTTP_GSC(zuser, "PHP_AUTH_USER", FAILURE);
- HTTP_GSC(zpass, "PHP_AUTH_PW", FAILURE);
-
- *user = estrndup(Z_STRVAL_P(zuser), Z_STRLEN_P(zuser));
- *pass = estrndup(Z_STRVAL_P(zpass), Z_STRLEN_P(zpass));
-
- return SUCCESS;
- } else {
- zval *zauth = NULL;
- HTTP_GSC(zauth, "HTTP_AUTHORIZATION", FAILURE);
- {
- char *decoded, *colon;
- int decoded_len;
- decoded = php_base64_decode(Z_STRVAL_P(zauth), Z_STRLEN_P(zauth),
- &decoded_len);
-
- if (colon = strchr(decoded + 6, ':')) {
- *user = estrndup(decoded + 6, colon - decoded - 6);
- *pass = estrndup(colon + 1, decoded + decoded_len - colon - 6 - 1);
-
- return SUCCESS;
- } else {
- return FAILURE;
- }
- }
- }
-}
-/* }}} */
-
-/* }}} public API */
-