php_http.c 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2016 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Brad Lafountain <rodif_bl@yahoo.com> |
  16. | Shane Caraveo <shane@caraveo.com> |
  17. | Dmitry Stogov <dmitry@zend.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /* $Id$ */
  21. #include "php_soap.h"
  22. #include "ext/standard/base64.h"
  23. #include "ext/standard/md5.h"
  24. #include "ext/standard/php_rand.h"
  25. static char *get_http_header_value(char *headers, char *type);
  26. static int get_http_body(php_stream *socketd, int close, char *headers, char **response, int *out_size TSRMLS_DC);
  27. static int get_http_headers(php_stream *socketd,char **response, int *out_size TSRMLS_DC);
  28. #define smart_str_append_const(str, const) \
  29. smart_str_appendl(str,const,sizeof(const)-1)
  30. /* Proxy HTTP Authentication */
  31. int proxy_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC)
  32. {
  33. zval **login, **password;
  34. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_login", sizeof("_proxy_login"), (void **)&login) == SUCCESS &&
  35. Z_TYPE_PP(login) == IS_STRING) {
  36. unsigned char* buf;
  37. int len;
  38. smart_str auth = {0};
  39. smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
  40. smart_str_appendc(&auth, ':');
  41. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_password", sizeof("_proxy_password"), (void **)&password) == SUCCESS &&
  42. Z_TYPE_PP(password) == IS_STRING) {
  43. smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
  44. }
  45. smart_str_0(&auth);
  46. buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len);
  47. smart_str_append_const(soap_headers, "Proxy-Authorization: Basic ");
  48. smart_str_appendl(soap_headers, (char*)buf, len);
  49. smart_str_append_const(soap_headers, "\r\n");
  50. efree(buf);
  51. smart_str_free(&auth);
  52. return 1;
  53. }
  54. return 0;
  55. }
  56. /* HTTP Authentication */
  57. int basic_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC)
  58. {
  59. zval **login, **password;
  60. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
  61. Z_TYPE_PP(login) == IS_STRING &&
  62. !zend_hash_exists(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"))) {
  63. unsigned char* buf;
  64. int len;
  65. smart_str auth = {0};
  66. smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
  67. smart_str_appendc(&auth, ':');
  68. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
  69. Z_TYPE_PP(password) == IS_STRING) {
  70. smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
  71. }
  72. smart_str_0(&auth);
  73. buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len);
  74. smart_str_append_const(soap_headers, "Authorization: Basic ");
  75. smart_str_appendl(soap_headers, (char*)buf, len);
  76. smart_str_append_const(soap_headers, "\r\n");
  77. efree(buf);
  78. smart_str_free(&auth);
  79. return 1;
  80. }
  81. return 0;
  82. }
  83. /* Additional HTTP headers */
  84. void http_context_headers(php_stream_context* context,
  85. zend_bool has_authorization,
  86. zend_bool has_proxy_authorization,
  87. zend_bool has_cookies,
  88. smart_str* soap_headers TSRMLS_DC)
  89. {
  90. zval **tmp;
  91. if (context &&
  92. php_stream_context_get_option(context, "http", "header", &tmp) == SUCCESS &&
  93. Z_TYPE_PP(tmp) == IS_STRING && Z_STRLEN_PP(tmp)) {
  94. char *s = Z_STRVAL_PP(tmp);
  95. char *p;
  96. int name_len;
  97. while (*s) {
  98. /* skip leading newlines and spaces */
  99. while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
  100. s++;
  101. }
  102. /* extract header name */
  103. p = s;
  104. name_len = -1;
  105. while (*p) {
  106. if (*p == ':') {
  107. if (name_len < 0) name_len = p - s;
  108. break;
  109. } else if (*p == ' ' || *p == '\t') {
  110. if (name_len < 0) name_len = p - s;
  111. } else if (*p == '\r' || *p == '\n') {
  112. break;
  113. }
  114. p++;
  115. }
  116. if (*p == ':') {
  117. /* extract header value */
  118. while (*p && *p != '\r' && *p != '\n') {
  119. p++;
  120. }
  121. /* skip some predefined headers */
  122. if ((name_len != sizeof("host")-1 ||
  123. strncasecmp(s, "host", sizeof("host")-1) != 0) &&
  124. (name_len != sizeof("connection")-1 ||
  125. strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
  126. (name_len != sizeof("user-agent")-1 ||
  127. strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
  128. (name_len != sizeof("content-length")-1 ||
  129. strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
  130. (name_len != sizeof("content-type")-1 ||
  131. strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
  132. (!has_cookies ||
  133. name_len != sizeof("cookie")-1 ||
  134. strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
  135. (!has_authorization ||
  136. name_len != sizeof("authorization")-1 ||
  137. strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
  138. (!has_proxy_authorization ||
  139. name_len != sizeof("proxy-authorization")-1 ||
  140. strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
  141. /* add header */
  142. smart_str_appendl(soap_headers, s, p-s);
  143. smart_str_append_const(soap_headers, "\r\n");
  144. }
  145. }
  146. s = (*p) ? (p + 1) : p;
  147. }
  148. }
  149. }
  150. static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, php_stream_context *context, int *use_proxy TSRMLS_DC)
  151. {
  152. php_stream *stream;
  153. zval **proxy_host, **proxy_port, **tmp;
  154. char *host;
  155. char *name;
  156. char *protocol;
  157. long namelen;
  158. int port;
  159. int old_error_reporting;
  160. struct timeval tv;
  161. struct timeval *timeout = NULL;
  162. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_host", sizeof("_proxy_host"), (void **) &proxy_host) == SUCCESS &&
  163. Z_TYPE_PP(proxy_host) == IS_STRING &&
  164. zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_port", sizeof("_proxy_port"), (void **) &proxy_port) == SUCCESS &&
  165. Z_TYPE_PP(proxy_port) == IS_LONG) {
  166. host = Z_STRVAL_PP(proxy_host);
  167. port = Z_LVAL_PP(proxy_port);
  168. *use_proxy = 1;
  169. } else {
  170. host = phpurl->host;
  171. port = phpurl->port;
  172. }
  173. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_connection_timeout", sizeof("_connection_timeout"), (void **) &tmp) == SUCCESS &&
  174. Z_TYPE_PP(tmp) == IS_LONG && Z_LVAL_PP(tmp) > 0) {
  175. tv.tv_sec = Z_LVAL_PP(tmp);
  176. tv.tv_usec = 0;
  177. timeout = &tv;
  178. }
  179. old_error_reporting = EG(error_reporting);
  180. EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);
  181. /* Changed ternary operator to an if/else so that additional comparisons can be done on the ssl_method property */
  182. if (use_ssl && !*use_proxy) {
  183. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method"), (void **) &tmp) == SUCCESS &&
  184. Z_TYPE_PP(tmp) == IS_LONG) {
  185. /* uses constants declared in soap.c to determine ssl uri protocol */
  186. switch (Z_LVAL_PP(tmp)) {
  187. case SOAP_SSL_METHOD_TLS:
  188. protocol = "tls";
  189. break;
  190. case SOAP_SSL_METHOD_SSLv2:
  191. protocol = "sslv2";
  192. break;
  193. case SOAP_SSL_METHOD_SSLv3:
  194. protocol = "sslv3";
  195. break;
  196. case SOAP_SSL_METHOD_SSLv23:
  197. protocol = "ssl";
  198. break;
  199. default:
  200. protocol = "ssl";
  201. break;
  202. }
  203. } else {
  204. protocol = "ssl";
  205. }
  206. } else {
  207. protocol = "tcp";
  208. }
  209. namelen = spprintf(&name, 0, "%s://%s:%d", protocol, host, port);
  210. stream = php_stream_xport_create(name, namelen,
  211. REPORT_ERRORS,
  212. STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
  213. NULL /*persistent_id*/,
  214. timeout,
  215. context,
  216. NULL, NULL);
  217. efree(name);
  218. /* SSL & proxy */
  219. if (stream && *use_proxy && use_ssl) {
  220. smart_str soap_headers = {0};
  221. char *http_headers;
  222. int http_header_size;
  223. smart_str_append_const(&soap_headers, "CONNECT ");
  224. smart_str_appends(&soap_headers, phpurl->host);
  225. smart_str_appendc(&soap_headers, ':');
  226. smart_str_append_unsigned(&soap_headers, phpurl->port);
  227. smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
  228. smart_str_append_const(&soap_headers, "Host: ");
  229. smart_str_appends(&soap_headers, phpurl->host);
  230. if (phpurl->port != 80) {
  231. smart_str_appendc(&soap_headers, ':');
  232. smart_str_append_unsigned(&soap_headers, phpurl->port);
  233. }
  234. smart_str_append_const(&soap_headers, "\r\n");
  235. proxy_authentication(this_ptr, &soap_headers TSRMLS_CC);
  236. smart_str_append_const(&soap_headers, "\r\n");
  237. if (php_stream_write(stream, soap_headers.c, soap_headers.len) != soap_headers.len) {
  238. php_stream_close(stream);
  239. stream = NULL;
  240. }
  241. smart_str_free(&soap_headers);
  242. if (stream) {
  243. if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC) || http_headers == NULL) {
  244. php_stream_close(stream);
  245. stream = NULL;
  246. }
  247. if (http_headers) {
  248. efree(http_headers);
  249. }
  250. }
  251. /* enable SSL transport layer */
  252. if (stream) {
  253. /* if a stream is created without encryption, check to see if SSL method parameter is specified and use
  254. proper encrypyion method based on constants defined in soap.c */
  255. int crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
  256. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method"), (void **) &tmp) == SUCCESS &&
  257. Z_TYPE_PP(tmp) == IS_LONG) {
  258. switch (Z_LVAL_PP(tmp)) {
  259. case SOAP_SSL_METHOD_TLS:
  260. crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  261. break;
  262. case SOAP_SSL_METHOD_SSLv2:
  263. crypto_method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
  264. break;
  265. case SOAP_SSL_METHOD_SSLv3:
  266. crypto_method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
  267. break;
  268. case SOAP_SSL_METHOD_SSLv23:
  269. crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
  270. break;
  271. default:
  272. crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  273. break;
  274. }
  275. }
  276. if (php_stream_xport_crypto_setup(stream, crypto_method, NULL TSRMLS_CC) < 0 ||
  277. php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
  278. php_stream_close(stream);
  279. stream = NULL;
  280. }
  281. }
  282. }
  283. EG(error_reporting) = old_error_reporting;
  284. return stream;
  285. }
  286. static int in_domain(const char *host, const char *domain)
  287. {
  288. if (domain[0] == '.') {
  289. int l1 = strlen(host);
  290. int l2 = strlen(domain);
  291. if (l1 > l2) {
  292. return strcmp(host+l1-l2,domain) == 0;
  293. } else {
  294. return 0;
  295. }
  296. } else {
  297. return strcmp(host,domain) == 0;
  298. }
  299. }
  300. int make_http_soap_request(zval *this_ptr,
  301. char *buf,
  302. int buf_size,
  303. char *location,
  304. char *soapaction,
  305. int soap_version,
  306. char **buffer,
  307. int *buffer_len TSRMLS_DC)
  308. {
  309. char *request;
  310. smart_str soap_headers = {0};
  311. smart_str soap_headers_z = {0};
  312. int request_size, err;
  313. php_url *phpurl = NULL;
  314. php_stream *stream;
  315. zval **trace, **tmp;
  316. int use_proxy = 0;
  317. int use_ssl;
  318. char *http_headers, *http_body, *content_type, *http_version, *cookie_itt;
  319. int http_header_size, http_body_size, http_close;
  320. char *connection;
  321. int http_1_1;
  322. int http_status;
  323. int content_type_xml = 0;
  324. long redirect_max = 20;
  325. char *content_encoding;
  326. char *http_msg = NULL;
  327. zend_bool old_allow_url_fopen;
  328. php_stream_context *context = NULL;
  329. zend_bool has_authorization = 0;
  330. zend_bool has_proxy_authorization = 0;
  331. zend_bool has_cookies = 0;
  332. if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) {
  333. return FALSE;
  334. }
  335. request = buf;
  336. request_size = buf_size;
  337. /* Compress request */
  338. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "compression", sizeof("compression"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_LONG) {
  339. int level = Z_LVAL_PP(tmp) & 0x0f;
  340. int kind = Z_LVAL_PP(tmp) & SOAP_COMPRESSION_DEFLATE;
  341. if (level > 9) {level = 9;}
  342. if ((Z_LVAL_PP(tmp) & SOAP_COMPRESSION_ACCEPT) != 0) {
  343. smart_str_append_const(&soap_headers_z,"Accept-Encoding: gzip, deflate\r\n");
  344. }
  345. if (level > 0) {
  346. zval func;
  347. zval retval;
  348. zval param1, param2, param3;
  349. zval *params[3];
  350. int n;
  351. params[0] = &param1;
  352. INIT_PZVAL(params[0]);
  353. params[1] = &param2;
  354. INIT_PZVAL(params[1]);
  355. params[2] = &param3;
  356. INIT_PZVAL(params[2]);
  357. ZVAL_STRINGL(params[0], buf, buf_size, 0);
  358. ZVAL_LONG(params[1], level);
  359. if (kind == SOAP_COMPRESSION_DEFLATE) {
  360. n = 2;
  361. ZVAL_STRING(&func, "gzcompress", 0);
  362. smart_str_append_const(&soap_headers_z,"Content-Encoding: deflate\r\n");
  363. } else {
  364. n = 3;
  365. ZVAL_STRING(&func, "gzencode", 0);
  366. smart_str_append_const(&soap_headers_z,"Content-Encoding: gzip\r\n");
  367. ZVAL_LONG(params[2], 0x1f);
  368. }
  369. if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, n, params TSRMLS_CC) == SUCCESS &&
  370. Z_TYPE(retval) == IS_STRING) {
  371. request = Z_STRVAL(retval);
  372. request_size = Z_STRLEN(retval);
  373. } else {
  374. if (request != buf) {efree(request);}
  375. smart_str_free(&soap_headers_z);
  376. return FALSE;
  377. }
  378. }
  379. }
  380. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"), (void **)&tmp) == SUCCESS) {
  381. php_stream_from_zval_no_verify(stream,tmp);
  382. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_LONG) {
  383. use_proxy = Z_LVAL_PP(tmp);
  384. }
  385. } else {
  386. stream = NULL;
  387. }
  388. if (location != NULL && location[0] != '\000') {
  389. phpurl = php_url_parse(location);
  390. }
  391. if (SUCCESS == zend_hash_find(Z_OBJPROP_P(this_ptr),
  392. "_stream_context", sizeof("_stream_context"), (void**)&tmp)) {
  393. context = php_stream_context_from_zval(*tmp, 0);
  394. }
  395. if (context &&
  396. php_stream_context_get_option(context, "http", "max_redirects", &tmp) == SUCCESS) {
  397. if (Z_TYPE_PP(tmp) != IS_STRING || !is_numeric_string(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), &redirect_max, NULL, 1)) {
  398. if (Z_TYPE_PP(tmp) == IS_LONG)
  399. redirect_max = Z_LVAL_PP(tmp);
  400. }
  401. }
  402. try_again:
  403. if (phpurl == NULL || phpurl->host == NULL) {
  404. if (phpurl != NULL) {php_url_free(phpurl);}
  405. if (request != buf) {efree(request);}
  406. add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL TSRMLS_CC);
  407. smart_str_free(&soap_headers_z);
  408. return FALSE;
  409. }
  410. use_ssl = 0;
  411. if (phpurl->scheme != NULL && strcmp(phpurl->scheme, "https") == 0) {
  412. use_ssl = 1;
  413. } else if (phpurl->scheme == NULL || strcmp(phpurl->scheme, "http") != 0) {
  414. php_url_free(phpurl);
  415. if (request != buf) {efree(request);}
  416. add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL TSRMLS_CC);
  417. smart_str_free(&soap_headers_z);
  418. return FALSE;
  419. }
  420. old_allow_url_fopen = PG(allow_url_fopen);
  421. PG(allow_url_fopen) = 1;
  422. if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) == NULL) {
  423. php_url_free(phpurl);
  424. if (request != buf) {efree(request);}
  425. add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL TSRMLS_CC);
  426. PG(allow_url_fopen) = old_allow_url_fopen;
  427. smart_str_free(&soap_headers_z);
  428. return FALSE;
  429. }
  430. if (phpurl->port == 0) {
  431. phpurl->port = use_ssl ? 443 : 80;
  432. }
  433. /* Check if request to the same host */
  434. if (stream != NULL) {
  435. php_url *orig;
  436. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"), (void **)&tmp) == SUCCESS &&
  437. (orig = (php_url *) zend_fetch_resource(tmp TSRMLS_CC, -1, "httpurl", NULL, 1, le_url)) != NULL &&
  438. ((use_proxy && !use_ssl) ||
  439. (((use_ssl && orig->scheme != NULL && strcmp(orig->scheme, "https") == 0) ||
  440. (!use_ssl && orig->scheme == NULL) ||
  441. (!use_ssl && strcmp(orig->scheme, "https") != 0)) &&
  442. strcmp(orig->host, phpurl->host) == 0 &&
  443. orig->port == phpurl->port))) {
  444. } else {
  445. php_stream_close(stream);
  446. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
  447. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  448. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  449. stream = NULL;
  450. use_proxy = 0;
  451. }
  452. }
  453. /* Check if keep-alive connection is still opened */
  454. if (stream != NULL && php_stream_eof(stream)) {
  455. php_stream_close(stream);
  456. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
  457. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  458. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  459. stream = NULL;
  460. use_proxy = 0;
  461. }
  462. if (!stream) {
  463. stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy TSRMLS_CC);
  464. if (stream) {
  465. php_stream_auto_cleanup(stream);
  466. add_property_resource(this_ptr, "httpsocket", php_stream_get_resource_id(stream));
  467. add_property_long(this_ptr, "_use_proxy", use_proxy);
  468. } else {
  469. php_url_free(phpurl);
  470. if (request != buf) {efree(request);}
  471. add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL TSRMLS_CC);
  472. PG(allow_url_fopen) = old_allow_url_fopen;
  473. smart_str_free(&soap_headers_z);
  474. return FALSE;
  475. }
  476. }
  477. PG(allow_url_fopen) = old_allow_url_fopen;
  478. if (stream) {
  479. zval **cookies, **login, **password;
  480. int ret = zend_list_insert(phpurl, le_url TSRMLS_CC);
  481. add_property_resource(this_ptr, "httpurl", ret);
  482. /*zend_list_addref(ret);*/
  483. if (context &&
  484. php_stream_context_get_option(context, "http", "protocol_version", &tmp) == SUCCESS &&
  485. Z_TYPE_PP(tmp) == IS_DOUBLE &&
  486. Z_DVAL_PP(tmp) == 1.0) {
  487. http_1_1 = 0;
  488. } else {
  489. http_1_1 = 1;
  490. }
  491. smart_str_append_const(&soap_headers, "POST ");
  492. if (use_proxy && !use_ssl) {
  493. smart_str_appends(&soap_headers, phpurl->scheme);
  494. smart_str_append_const(&soap_headers, "://");
  495. smart_str_appends(&soap_headers, phpurl->host);
  496. smart_str_appendc(&soap_headers, ':');
  497. smart_str_append_unsigned(&soap_headers, phpurl->port);
  498. }
  499. if (phpurl->path) {
  500. smart_str_appends(&soap_headers, phpurl->path);
  501. } else {
  502. smart_str_appendc(&soap_headers, '/');
  503. }
  504. if (phpurl->query) {
  505. smart_str_appendc(&soap_headers, '?');
  506. smart_str_appends(&soap_headers, phpurl->query);
  507. }
  508. if (phpurl->fragment) {
  509. smart_str_appendc(&soap_headers, '#');
  510. smart_str_appends(&soap_headers, phpurl->fragment);
  511. }
  512. if (http_1_1) {
  513. smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
  514. } else {
  515. smart_str_append_const(&soap_headers, " HTTP/1.0\r\n");
  516. }
  517. smart_str_append_const(&soap_headers, "Host: ");
  518. smart_str_appends(&soap_headers, phpurl->host);
  519. if (phpurl->port != (use_ssl?443:80)) {
  520. smart_str_appendc(&soap_headers, ':');
  521. smart_str_append_unsigned(&soap_headers, phpurl->port);
  522. }
  523. if (!http_1_1 ||
  524. (zend_hash_find(Z_OBJPROP_P(this_ptr), "_keep_alive", sizeof("_keep_alive"), (void **)&tmp) == SUCCESS &&
  525. (Z_TYPE_PP(tmp) == IS_BOOL || Z_TYPE_PP(tmp) == IS_LONG) &&
  526. Z_LVAL_PP(tmp) == 0)) {
  527. smart_str_append_const(&soap_headers, "\r\n"
  528. "Connection: close\r\n");
  529. } else {
  530. smart_str_append_const(&soap_headers, "\r\n"
  531. "Connection: Keep-Alive\r\n");
  532. }
  533. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_user_agent", sizeof("_user_agent"), (void **)&tmp) == SUCCESS &&
  534. Z_TYPE_PP(tmp) == IS_STRING) {
  535. if (Z_STRLEN_PP(tmp) > 0) {
  536. smart_str_append_const(&soap_headers, "User-Agent: ");
  537. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  538. smart_str_append_const(&soap_headers, "\r\n");
  539. }
  540. } else if (context &&
  541. php_stream_context_get_option(context, "http", "user_agent", &tmp) == SUCCESS &&
  542. Z_TYPE_PP(tmp) == IS_STRING) {
  543. if (Z_STRLEN_PP(tmp) > 0) {
  544. smart_str_append_const(&soap_headers, "User-Agent: ");
  545. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  546. smart_str_append_const(&soap_headers, "\r\n");
  547. }
  548. } else if (FG(user_agent)) {
  549. smart_str_append_const(&soap_headers, "User-Agent: ");
  550. smart_str_appends(&soap_headers, FG(user_agent));
  551. smart_str_append_const(&soap_headers, "\r\n");
  552. } else {
  553. smart_str_append_const(&soap_headers, "User-Agent: PHP-SOAP/"PHP_VERSION"\r\n");
  554. }
  555. smart_str_append(&soap_headers, &soap_headers_z);
  556. if (soap_version == SOAP_1_2) {
  557. smart_str_append_const(&soap_headers,"Content-Type: application/soap+xml; charset=utf-8");
  558. if (soapaction) {
  559. smart_str_append_const(&soap_headers,"; action=\"");
  560. smart_str_appends(&soap_headers, soapaction);
  561. smart_str_append_const(&soap_headers,"\"");
  562. }
  563. smart_str_append_const(&soap_headers,"\r\n");
  564. } else {
  565. smart_str_append_const(&soap_headers,"Content-Type: text/xml; charset=utf-8\r\n");
  566. if (soapaction) {
  567. smart_str_append_const(&soap_headers, "SOAPAction: \"");
  568. smart_str_appends(&soap_headers, soapaction);
  569. smart_str_append_const(&soap_headers, "\"\r\n");
  570. }
  571. }
  572. smart_str_append_const(&soap_headers,"Content-Length: ");
  573. smart_str_append_long(&soap_headers, request_size);
  574. smart_str_append_const(&soap_headers, "\r\n");
  575. /* HTTP Authentication */
  576. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
  577. Z_TYPE_PP(login) == IS_STRING) {
  578. zval **digest;
  579. has_authorization = 1;
  580. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"), (void **)&digest) == SUCCESS) {
  581. if (Z_TYPE_PP(digest) == IS_ARRAY) {
  582. char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
  583. PHP_MD5_CTX md5ctx;
  584. unsigned char hash[16];
  585. PHP_MD5Init(&md5ctx);
  586. snprintf(cnonce, sizeof(cnonce), "%ld", php_rand(TSRMLS_C));
  587. PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
  588. PHP_MD5Final(hash, &md5ctx);
  589. make_digest(cnonce, hash);
  590. if (zend_hash_find(Z_ARRVAL_PP(digest), "nc", sizeof("nc"), (void **)&tmp) == SUCCESS &&
  591. Z_TYPE_PP(tmp) == IS_LONG) {
  592. Z_LVAL_PP(tmp)++;
  593. snprintf(nc, sizeof(nc), "%08ld", Z_LVAL_PP(tmp));
  594. } else {
  595. add_assoc_long(*digest, "nc", 1);
  596. strcpy(nc, "00000001");
  597. }
  598. PHP_MD5Init(&md5ctx);
  599. PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_PP(login), Z_STRLEN_PP(login));
  600. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  601. if (zend_hash_find(Z_ARRVAL_PP(digest), "realm", sizeof("realm"), (void **)&tmp) == SUCCESS &&
  602. Z_TYPE_PP(tmp) == IS_STRING) {
  603. PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  604. }
  605. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  606. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
  607. Z_TYPE_PP(password) == IS_STRING) {
  608. PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_PP(password), Z_STRLEN_PP(password));
  609. }
  610. PHP_MD5Final(hash, &md5ctx);
  611. make_digest(HA1, hash);
  612. if (zend_hash_find(Z_ARRVAL_PP(digest), "algorithm", sizeof("algorithm"), (void **)&tmp) == SUCCESS &&
  613. Z_TYPE_PP(tmp) == IS_STRING &&
  614. Z_STRLEN_PP(tmp) == sizeof("md5-sess")-1 &&
  615. stricmp(Z_STRVAL_PP(tmp), "md5-sess") == 0) {
  616. PHP_MD5Init(&md5ctx);
  617. PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
  618. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  619. if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
  620. Z_TYPE_PP(tmp) == IS_STRING) {
  621. PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  622. }
  623. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  624. PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
  625. PHP_MD5Final(hash, &md5ctx);
  626. make_digest(HA1, hash);
  627. }
  628. PHP_MD5Init(&md5ctx);
  629. PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1);
  630. if (phpurl->path) {
  631. PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->path, strlen(phpurl->path));
  632. } else {
  633. PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1);
  634. }
  635. if (phpurl->query) {
  636. PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1);
  637. PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->query, strlen(phpurl->query));
  638. }
  639. /* TODO: Support for qop="auth-int" */
  640. /*
  641. if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
  642. Z_TYPE_PP(tmp) == IS_STRING &&
  643. Z_STRLEN_PP(tmp) == sizeof("auth-int")-1 &&
  644. stricmp(Z_STRVAL_PP(tmp), "auth-int") == 0) {
  645. PHP_MD5Update(&md5ctx, ":", 1);
  646. PHP_MD5Update(&md5ctx, HEntity, HASHHEXLEN);
  647. }
  648. */
  649. PHP_MD5Final(hash, &md5ctx);
  650. make_digest(HA2, hash);
  651. PHP_MD5Init(&md5ctx);
  652. PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
  653. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  654. if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
  655. Z_TYPE_PP(tmp) == IS_STRING) {
  656. PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  657. }
  658. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  659. if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
  660. Z_TYPE_PP(tmp) == IS_STRING) {
  661. PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8);
  662. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  663. PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
  664. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  665. /* TODO: Support for qop="auth-int" */
  666. PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
  667. PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
  668. }
  669. PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32);
  670. PHP_MD5Final(hash, &md5ctx);
  671. make_digest(response, hash);
  672. smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
  673. smart_str_appendl(&soap_headers, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
  674. if (zend_hash_find(Z_ARRVAL_PP(digest), "realm", sizeof("realm"), (void **)&tmp) == SUCCESS &&
  675. Z_TYPE_PP(tmp) == IS_STRING) {
  676. smart_str_append_const(&soap_headers, "\", realm=\"");
  677. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  678. }
  679. if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
  680. Z_TYPE_PP(tmp) == IS_STRING) {
  681. smart_str_append_const(&soap_headers, "\", nonce=\"");
  682. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  683. }
  684. smart_str_append_const(&soap_headers, "\", uri=\"");
  685. if (phpurl->path) {
  686. smart_str_appends(&soap_headers, phpurl->path);
  687. } else {
  688. smart_str_appendc(&soap_headers, '/');
  689. }
  690. if (phpurl->query) {
  691. smart_str_appendc(&soap_headers, '?');
  692. smart_str_appends(&soap_headers, phpurl->query);
  693. }
  694. if (phpurl->fragment) {
  695. smart_str_appendc(&soap_headers, '#');
  696. smart_str_appends(&soap_headers, phpurl->fragment);
  697. }
  698. if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
  699. Z_TYPE_PP(tmp) == IS_STRING) {
  700. /* TODO: Support for qop="auth-int" */
  701. smart_str_append_const(&soap_headers, "\", qop=\"auth");
  702. smart_str_append_const(&soap_headers, "\", nc=\"");
  703. smart_str_appendl(&soap_headers, nc, 8);
  704. smart_str_append_const(&soap_headers, "\", cnonce=\"");
  705. smart_str_appendl(&soap_headers, cnonce, 8);
  706. }
  707. smart_str_append_const(&soap_headers, "\", response=\"");
  708. smart_str_appendl(&soap_headers, response, 32);
  709. if (zend_hash_find(Z_ARRVAL_PP(digest), "opaque", sizeof("opaque"), (void **)&tmp) == SUCCESS &&
  710. Z_TYPE_PP(tmp) == IS_STRING) {
  711. smart_str_append_const(&soap_headers, "\", opaque=\"");
  712. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  713. }
  714. if (zend_hash_find(Z_ARRVAL_PP(digest), "algorithm", sizeof("algorithm"), (void **)&tmp) == SUCCESS &&
  715. Z_TYPE_PP(tmp) == IS_STRING) {
  716. smart_str_append_const(&soap_headers, "\", algorithm=\"");
  717. smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
  718. }
  719. smart_str_append_const(&soap_headers, "\"\r\n");
  720. }
  721. } else {
  722. unsigned char* buf;
  723. int len;
  724. smart_str auth = {0};
  725. smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
  726. smart_str_appendc(&auth, ':');
  727. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
  728. Z_TYPE_PP(password) == IS_STRING) {
  729. smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
  730. }
  731. smart_str_0(&auth);
  732. buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len);
  733. smart_str_append_const(&soap_headers, "Authorization: Basic ");
  734. smart_str_appendl(&soap_headers, (char*)buf, len);
  735. smart_str_append_const(&soap_headers, "\r\n");
  736. efree(buf);
  737. smart_str_free(&auth);
  738. }
  739. }
  740. /* Proxy HTTP Authentication */
  741. if (use_proxy && !use_ssl) {
  742. has_proxy_authorization = proxy_authentication(this_ptr, &soap_headers TSRMLS_CC);
  743. }
  744. /* Send cookies along with request */
  745. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), (void **)&cookies) == SUCCESS &&
  746. Z_TYPE_PP(cookies) == IS_ARRAY) {
  747. zval **data;
  748. char *key;
  749. uint key_len;
  750. int i, n;
  751. has_cookies = 1;
  752. n = zend_hash_num_elements(Z_ARRVAL_PP(cookies));
  753. if (n > 0) {
  754. zend_hash_internal_pointer_reset(Z_ARRVAL_PP(cookies));
  755. smart_str_append_const(&soap_headers, "Cookie: ");
  756. for (i = 0; i < n; i++) {
  757. ulong numindx;
  758. int res = zend_hash_get_current_key_ex(Z_ARRVAL_PP(cookies), &key, &key_len, &numindx, 0, NULL);
  759. zend_hash_get_current_data(Z_ARRVAL_PP(cookies), (void **)&data);
  760. if (res == HASH_KEY_IS_STRING && Z_TYPE_PP(data) == IS_ARRAY) {
  761. zval** value;
  762. if (zend_hash_index_find(Z_ARRVAL_PP(data), 0, (void**)&value) == SUCCESS &&
  763. Z_TYPE_PP(value) == IS_STRING) {
  764. zval **tmp;
  765. if ((zend_hash_index_find(Z_ARRVAL_PP(data), 1, (void**)&tmp) == FAILURE ||
  766. Z_TYPE_PP(tmp) != IS_STRING ||
  767. strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_PP(tmp),Z_STRLEN_PP(tmp)) == 0) &&
  768. (zend_hash_index_find(Z_ARRVAL_PP(data), 2, (void**)&tmp) == FAILURE ||
  769. Z_TYPE_PP(tmp) != IS_STRING ||
  770. in_domain(phpurl->host,Z_STRVAL_PP(tmp))) &&
  771. (use_ssl || zend_hash_index_find(Z_ARRVAL_PP(data), 3, (void**)&tmp) == FAILURE)) {
  772. smart_str_appendl(&soap_headers, key, key_len-1);
  773. smart_str_appendc(&soap_headers, '=');
  774. smart_str_appendl(&soap_headers, Z_STRVAL_PP(value), Z_STRLEN_PP(value));
  775. smart_str_appendc(&soap_headers, ';');
  776. }
  777. }
  778. }
  779. zend_hash_move_forward(Z_ARRVAL_PP(cookies));
  780. }
  781. smart_str_append_const(&soap_headers, "\r\n");
  782. }
  783. }
  784. http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers TSRMLS_CC);
  785. smart_str_append_const(&soap_headers, "\r\n");
  786. smart_str_0(&soap_headers);
  787. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
  788. (Z_TYPE_PP(trace) == IS_BOOL || Z_TYPE_PP(trace) == IS_LONG) && Z_LVAL_PP(trace) != 0) {
  789. add_property_stringl(this_ptr, "__last_request_headers", soap_headers.c, soap_headers.len, 1);
  790. }
  791. smart_str_appendl(&soap_headers, request, request_size);
  792. smart_str_0(&soap_headers);
  793. err = php_stream_write(stream, soap_headers.c, soap_headers.len);
  794. if (err != soap_headers.len) {
  795. if (request != buf) {efree(request);}
  796. php_stream_close(stream);
  797. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
  798. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  799. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  800. add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL TSRMLS_CC);
  801. smart_str_free(&soap_headers_z);
  802. return FALSE;
  803. }
  804. smart_str_free(&soap_headers);
  805. } else {
  806. add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL TSRMLS_CC);
  807. smart_str_free(&soap_headers_z);
  808. return FALSE;
  809. }
  810. if (!buffer) {
  811. php_stream_close(stream);
  812. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  813. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  814. smart_str_free(&soap_headers_z);
  815. return TRUE;
  816. }
  817. do {
  818. if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC)) {
  819. if (http_headers) {efree(http_headers);}
  820. if (request != buf) {efree(request);}
  821. php_stream_close(stream);
  822. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  823. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  824. add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL TSRMLS_CC);
  825. smart_str_free(&soap_headers_z);
  826. return FALSE;
  827. }
  828. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
  829. (Z_TYPE_PP(trace) == IS_BOOL || Z_TYPE_PP(trace) == IS_LONG) && Z_LVAL_PP(trace) != 0) {
  830. add_property_stringl(this_ptr, "__last_response_headers", http_headers, http_header_size, 1);
  831. }
  832. /* Check to see what HTTP status was sent */
  833. http_1_1 = 0;
  834. http_status = 0;
  835. http_version = get_http_header_value(http_headers,"HTTP/");
  836. if (http_version) {
  837. char *tmp;
  838. if (!strncmp(http_version,"1.1", 3)) {
  839. http_1_1 = 1;
  840. }
  841. tmp = strstr(http_version," ");
  842. if (tmp != NULL) {
  843. tmp++;
  844. http_status = atoi(tmp);
  845. }
  846. tmp = strstr(tmp," ");
  847. if (tmp != NULL) {
  848. tmp++;
  849. if (http_msg) {
  850. efree(http_msg);
  851. }
  852. http_msg = estrdup(tmp);
  853. }
  854. efree(http_version);
  855. /* Try and get headers again */
  856. if (http_status == 100) {
  857. efree(http_headers);
  858. }
  859. }
  860. } while (http_status == 100);
  861. /* Grab and send back every cookie */
  862. /* Not going to worry about Path: because
  863. we shouldn't be changing urls so path dont
  864. matter too much
  865. */
  866. cookie_itt = strstr(http_headers,"Set-Cookie: ");
  867. while (cookie_itt) {
  868. char *end_pos, *cookie;
  869. char *eqpos, *sempos;
  870. zval **cookies;
  871. if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), (void **)&cookies) == FAILURE ||
  872. Z_TYPE_PP(cookies) != IS_ARRAY) {
  873. zval *tmp_cookies;
  874. MAKE_STD_ZVAL(tmp_cookies);
  875. array_init(tmp_cookies);
  876. zend_hash_update(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), &tmp_cookies, sizeof(zval *), (void **)&cookies);
  877. }
  878. end_pos = strstr(cookie_itt,"\r\n");
  879. cookie = get_http_header_value(cookie_itt,"Set-Cookie: ");
  880. eqpos = strstr(cookie, "=");
  881. sempos = strstr(cookie, ";");
  882. if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
  883. smart_str name = {0};
  884. int cookie_len;
  885. zval *zcookie;
  886. if (sempos != NULL) {
  887. cookie_len = sempos-(eqpos+1);
  888. } else {
  889. cookie_len = strlen(cookie)-(eqpos-cookie)-1;
  890. }
  891. smart_str_appendl(&name, cookie, eqpos - cookie);
  892. smart_str_0(&name);
  893. ALLOC_INIT_ZVAL(zcookie);
  894. array_init(zcookie);
  895. add_index_stringl(zcookie, 0, eqpos + 1, cookie_len, 1);
  896. if (sempos != NULL) {
  897. char *options = cookie + cookie_len+1;
  898. while (*options) {
  899. while (*options == ' ') {options++;}
  900. sempos = strstr(options, ";");
  901. if (strstr(options,"path=") == options) {
  902. eqpos = options + sizeof("path=")-1;
  903. add_index_stringl(zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
  904. } else if (strstr(options,"domain=") == options) {
  905. eqpos = options + sizeof("domain=")-1;
  906. add_index_stringl(zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
  907. } else if (strstr(options,"secure") == options) {
  908. add_index_bool(zcookie, 3, 1);
  909. }
  910. if (sempos != NULL) {
  911. options = sempos+1;
  912. } else {
  913. break;
  914. }
  915. }
  916. }
  917. if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 1)) {
  918. char *t = phpurl->path?phpurl->path:"/";
  919. char *c = strrchr(t, '/');
  920. if (c) {
  921. add_index_stringl(zcookie, 1, t, c-t, 1);
  922. }
  923. }
  924. if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 2)) {
  925. add_index_string(zcookie, 2, phpurl->host, 1);
  926. }
  927. add_assoc_zval_ex(*cookies, name.c, name.len+1, zcookie);
  928. smart_str_free(&name);
  929. }
  930. cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: ");
  931. efree(cookie);
  932. }
  933. /* See if the server requested a close */
  934. if (http_1_1) {
  935. http_close = FALSE;
  936. if (use_proxy && !use_ssl) {
  937. connection = get_http_header_value(http_headers,"Proxy-Connection: ");
  938. if (connection) {
  939. if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
  940. http_close = TRUE;
  941. }
  942. efree(connection);
  943. }
  944. }
  945. if (http_close == FALSE) {
  946. connection = get_http_header_value(http_headers,"Connection: ");
  947. if (connection) {
  948. if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
  949. http_close = TRUE;
  950. }
  951. efree(connection);
  952. }
  953. }
  954. } else {
  955. http_close = TRUE;
  956. if (use_proxy && !use_ssl) {
  957. connection = get_http_header_value(http_headers,"Proxy-Connection: ");
  958. if (connection) {
  959. if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
  960. http_close = FALSE;
  961. }
  962. efree(connection);
  963. }
  964. }
  965. if (http_close == TRUE) {
  966. connection = get_http_header_value(http_headers,"Connection: ");
  967. if (connection) {
  968. if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
  969. http_close = FALSE;
  970. }
  971. efree(connection);
  972. }
  973. }
  974. }
  975. if (!get_http_body(stream, http_close, http_headers, &http_body, &http_body_size TSRMLS_CC)) {
  976. if (request != buf) {efree(request);}
  977. php_stream_close(stream);
  978. efree(http_headers);
  979. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  980. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  981. add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL TSRMLS_CC);
  982. if (http_msg) {
  983. efree(http_msg);
  984. }
  985. smart_str_free(&soap_headers_z);
  986. return FALSE;
  987. }
  988. if (request != buf) {efree(request);}
  989. if (http_close) {
  990. php_stream_close(stream);
  991. zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
  992. zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
  993. stream = NULL;
  994. }
  995. /* Process HTTP status codes */
  996. if (http_status >= 300 && http_status < 400) {
  997. char *loc;
  998. if ((loc = get_http_header_value(http_headers,"Location: ")) != NULL) {
  999. php_url *new_url = php_url_parse(loc);
  1000. if (new_url != NULL) {
  1001. efree(http_headers);
  1002. efree(http_body);
  1003. efree(loc);
  1004. if (new_url->scheme == NULL && new_url->path != NULL) {
  1005. new_url->scheme = phpurl->scheme ? estrdup(phpurl->scheme) : NULL;
  1006. new_url->host = phpurl->host ? estrdup(phpurl->host) : NULL;
  1007. new_url->port = phpurl->port;
  1008. if (new_url->path && new_url->path[0] != '/') {
  1009. if (phpurl->path) {
  1010. char *t = phpurl->path;
  1011. char *p = strrchr(t, '/');
  1012. if (p) {
  1013. char *s = emalloc((p - t) + strlen(new_url->path) + 2);
  1014. strncpy(s, t, (p - t) + 1);
  1015. s[(p - t) + 1] = 0;
  1016. strcat(s, new_url->path);
  1017. efree(new_url->path);
  1018. new_url->path = s;
  1019. }
  1020. } else {
  1021. char *s = emalloc(strlen(new_url->path) + 2);
  1022. s[0] = '/'; s[1] = 0;
  1023. strcat(s, new_url->path);
  1024. efree(new_url->path);
  1025. new_url->path = s;
  1026. }
  1027. }
  1028. }
  1029. phpurl = new_url;
  1030. if (--redirect_max < 1) {
  1031. add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL TSRMLS_CC);
  1032. smart_str_free(&soap_headers_z);
  1033. return FALSE;
  1034. }
  1035. goto try_again;
  1036. }
  1037. }
  1038. } else if (http_status == 401) {
  1039. /* Digest authentication */
  1040. zval **digest, **login, **password;
  1041. char *auth = get_http_header_value(http_headers, "WWW-Authenticate: ");
  1042. if (auth &&
  1043. strstr(auth, "Digest") == auth &&
  1044. (zend_hash_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"), (void **)&digest) == FAILURE ||
  1045. Z_TYPE_PP(digest) != IS_ARRAY) &&
  1046. zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
  1047. Z_TYPE_PP(login) == IS_STRING &&
  1048. zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
  1049. Z_TYPE_PP(password) == IS_STRING) {
  1050. char *s;
  1051. zval *digest = NULL;
  1052. s = auth + sizeof("Digest")-1;
  1053. while (*s != '\0') {
  1054. char *name, *val;
  1055. while (*s == ' ') ++s;
  1056. name = s;
  1057. while (*s != '\0' && *s != '=') ++s;
  1058. if (*s == '=') {
  1059. *s = '\0';
  1060. ++s;
  1061. if (*s == '"') {
  1062. ++s;
  1063. val = s;
  1064. while (*s != '\0' && *s != '"') ++s;
  1065. } else {
  1066. val = s;
  1067. while (*s != '\0' && *s != ' ' && *s != ',') ++s;
  1068. }
  1069. if (*s != '\0') {
  1070. if (*s != ',') {
  1071. *s = '\0';
  1072. ++s;
  1073. while (*s != '\0' && *s != ',') ++s;
  1074. if (*s != '\0') ++s;
  1075. } else {
  1076. *s = '\0';
  1077. ++s;
  1078. }
  1079. }
  1080. if (digest == NULL) {
  1081. ALLOC_INIT_ZVAL(digest);
  1082. array_init(digest);
  1083. }
  1084. add_assoc_string(digest, name, val ,1);
  1085. }
  1086. }
  1087. if (digest != NULL) {
  1088. php_url *new_url = emalloc(sizeof(php_url));
  1089. Z_DELREF_P(digest);
  1090. add_property_zval_ex(this_ptr, "_digest", sizeof("_digest"), digest TSRMLS_CC);
  1091. *new_url = *phpurl;
  1092. if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme);
  1093. if (phpurl->user) phpurl->user = estrdup(phpurl->user);
  1094. if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass);
  1095. if (phpurl->host) phpurl->host = estrdup(phpurl->host);
  1096. if (phpurl->path) phpurl->path = estrdup(phpurl->path);
  1097. if (phpurl->query) phpurl->query = estrdup(phpurl->query);
  1098. if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment);
  1099. phpurl = new_url;
  1100. efree(auth);
  1101. efree(http_headers);
  1102. efree(http_body);
  1103. goto try_again;
  1104. }
  1105. }
  1106. if (auth) efree(auth);
  1107. }
  1108. smart_str_free(&soap_headers_z);
  1109. /* Check and see if the server even sent a xml document */
  1110. content_type = get_http_header_value(http_headers,"Content-Type: ");
  1111. if (content_type) {
  1112. char *pos = NULL;
  1113. int cmplen;
  1114. pos = strstr(content_type,";");
  1115. if (pos != NULL) {
  1116. cmplen = pos - content_type;
  1117. } else {
  1118. cmplen = strlen(content_type);
  1119. }
  1120. if (strncmp(content_type, "text/xml", cmplen) == 0 ||
  1121. strncmp(content_type, "application/soap+xml", cmplen) == 0) {
  1122. content_type_xml = 1;
  1123. /*
  1124. if (strncmp(http_body, "<?xml", 5)) {
  1125. zval *err;
  1126. MAKE_STD_ZVAL(err);
  1127. ZVAL_STRINGL(err, http_body, http_body_size, 1);
  1128. add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err TSRMLS_CC);
  1129. efree(content_type);
  1130. efree(http_headers);
  1131. efree(http_body);
  1132. return FALSE;
  1133. }
  1134. */
  1135. }
  1136. efree(content_type);
  1137. }
  1138. /* Decompress response */
  1139. content_encoding = get_http_header_value(http_headers,"Content-Encoding: ");
  1140. if (content_encoding) {
  1141. zval func;
  1142. zval retval;
  1143. zval param;
  1144. zval *params[1];
  1145. if ((strcmp(content_encoding,"gzip") == 0 ||
  1146. strcmp(content_encoding,"x-gzip") == 0) &&
  1147. zend_hash_exists(EG(function_table), "gzinflate", sizeof("gzinflate"))) {
  1148. ZVAL_STRING(&func, "gzinflate", 0);
  1149. params[0] = &param;
  1150. ZVAL_STRINGL(params[0], http_body+10, http_body_size-10, 0);
  1151. INIT_PZVAL(params[0]);
  1152. } else if (strcmp(content_encoding,"deflate") == 0 &&
  1153. zend_hash_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress"))) {
  1154. ZVAL_STRING(&func, "gzuncompress", 0);
  1155. params[0] = &param;
  1156. ZVAL_STRINGL(params[0], http_body, http_body_size, 0);
  1157. INIT_PZVAL(params[0]);
  1158. } else {
  1159. efree(content_encoding);
  1160. efree(http_headers);
  1161. efree(http_body);
  1162. if (http_msg) {
  1163. efree(http_msg);
  1164. }
  1165. add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL TSRMLS_CC);
  1166. return FALSE;
  1167. }
  1168. if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, 1, params TSRMLS_CC) == SUCCESS &&
  1169. Z_TYPE(retval) == IS_STRING) {
  1170. efree(http_body);
  1171. *buffer = Z_STRVAL(retval);
  1172. *buffer_len = Z_STRLEN(retval);
  1173. } else {
  1174. efree(content_encoding);
  1175. efree(http_headers);
  1176. efree(http_body);
  1177. add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL TSRMLS_CC);
  1178. if (http_msg) {
  1179. efree(http_msg);
  1180. }
  1181. return FALSE;
  1182. }
  1183. efree(content_encoding);
  1184. } else {
  1185. *buffer = http_body;
  1186. *buffer_len = http_body_size;
  1187. }
  1188. efree(http_headers);
  1189. if (http_status >= 400) {
  1190. int error = 0;
  1191. if (*buffer_len == 0) {
  1192. error = 1;
  1193. } else if (*buffer_len > 0) {
  1194. if (!content_type_xml) {
  1195. char *s = *buffer;
  1196. while (*s != '\0' && *s < ' ') {
  1197. s++;
  1198. }
  1199. if (strncmp(s, "<?xml", 5)) {
  1200. error = 1;
  1201. }
  1202. }
  1203. }
  1204. if (error) {
  1205. efree(*buffer);
  1206. add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL TSRMLS_CC);
  1207. efree(http_msg);
  1208. return FALSE;
  1209. }
  1210. }
  1211. if (http_msg) {
  1212. efree(http_msg);
  1213. }
  1214. return TRUE;
  1215. }
  1216. static char *get_http_header_value(char *headers, char *type)
  1217. {
  1218. char *pos, *tmp = NULL;
  1219. int typelen, headerslen;
  1220. typelen = strlen(type);
  1221. headerslen = strlen(headers);
  1222. /* header `titles' can be lower case, or any case combination, according
  1223. * to the various RFC's. */
  1224. pos = headers;
  1225. do {
  1226. /* start of buffer or start of line */
  1227. if (strncasecmp(pos, type, typelen) == 0) {
  1228. char *eol;
  1229. /* match */
  1230. tmp = pos + typelen;
  1231. eol = strchr(tmp, '\n');
  1232. if (eol == NULL) {
  1233. eol = headers + headerslen;
  1234. } else if (eol > tmp && *(eol-1) == '\r') {
  1235. eol--;
  1236. }
  1237. return estrndup(tmp, eol - tmp);
  1238. }
  1239. /* find next line */
  1240. pos = strchr(pos, '\n');
  1241. if (pos) {
  1242. pos++;
  1243. }
  1244. } while (pos);
  1245. return NULL;
  1246. }
  1247. static int get_http_body(php_stream *stream, int close, char *headers, char **response, int *out_size TSRMLS_DC)
  1248. {
  1249. char *header, *http_buf = NULL;
  1250. int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
  1251. if (!close) {
  1252. header = get_http_header_value(headers, "Connection: ");
  1253. if (header) {
  1254. if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
  1255. efree(header);
  1256. }
  1257. }
  1258. header = get_http_header_value(headers, "Transfer-Encoding: ");
  1259. if (header) {
  1260. if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
  1261. efree(header);
  1262. }
  1263. header = get_http_header_value(headers, "Content-Length: ");
  1264. if (header) {
  1265. header_length = atoi(header);
  1266. efree(header);
  1267. if (!header_length && !header_chunked) {
  1268. /* Empty response */
  1269. http_buf = emalloc(1);
  1270. http_buf[0] = '\0';
  1271. (*response) = http_buf;
  1272. (*out_size) = 0;
  1273. return TRUE;
  1274. }
  1275. }
  1276. if (header_chunked) {
  1277. char ch, done, headerbuf[8192];
  1278. done = FALSE;
  1279. while (!done) {
  1280. int buf_size = 0;
  1281. php_stream_gets(stream, headerbuf, sizeof(headerbuf));
  1282. if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
  1283. if (buf_size > 0) {
  1284. int len_size = 0;
  1285. if (http_buf_size + buf_size + 1 < 0) {
  1286. efree(http_buf);
  1287. return FALSE;
  1288. }
  1289. http_buf = erealloc(http_buf, http_buf_size + buf_size + 1);
  1290. while (len_size < buf_size) {
  1291. int len_read = php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size);
  1292. if (len_read <= 0) {
  1293. /* Error or EOF */
  1294. done = TRUE;
  1295. break;
  1296. }
  1297. len_size += len_read;
  1298. http_buf_size += len_read;
  1299. }
  1300. /* Eat up '\r' '\n' */
  1301. ch = php_stream_getc(stream);
  1302. if (ch == '\r') {
  1303. ch = php_stream_getc(stream);
  1304. }
  1305. if (ch != '\n') {
  1306. /* Somthing wrong in chunked encoding */
  1307. if (http_buf) {
  1308. efree(http_buf);
  1309. }
  1310. return FALSE;
  1311. }
  1312. }
  1313. } else {
  1314. /* Somthing wrong in chunked encoding */
  1315. if (http_buf) {
  1316. efree(http_buf);
  1317. }
  1318. return FALSE;
  1319. }
  1320. if (buf_size == 0) {
  1321. done = TRUE;
  1322. }
  1323. }
  1324. /* Ignore trailer headers */
  1325. while (1) {
  1326. if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
  1327. break;
  1328. }
  1329. if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
  1330. (headerbuf[0] == '\n')) {
  1331. /* empty line marks end of headers */
  1332. break;
  1333. }
  1334. }
  1335. if (http_buf == NULL) {
  1336. http_buf = emalloc(1);
  1337. }
  1338. } else if (header_length) {
  1339. if (header_length < 0 || header_length >= INT_MAX) {
  1340. return FALSE;
  1341. }
  1342. http_buf = safe_emalloc(1, header_length, 1);
  1343. while (http_buf_size < header_length) {
  1344. int len_read = php_stream_read(stream, http_buf + http_buf_size, header_length - http_buf_size);
  1345. if (len_read <= 0) {
  1346. break;
  1347. }
  1348. http_buf_size += len_read;
  1349. }
  1350. } else if (header_close) {
  1351. do {
  1352. int len_read;
  1353. http_buf = erealloc(http_buf, http_buf_size + 4096 + 1);
  1354. len_read = php_stream_read(stream, http_buf + http_buf_size, 4096);
  1355. if (len_read > 0) {
  1356. http_buf_size += len_read;
  1357. }
  1358. } while(!php_stream_eof(stream));
  1359. } else {
  1360. return FALSE;
  1361. }
  1362. http_buf[http_buf_size] = '\0';
  1363. (*response) = http_buf;
  1364. (*out_size) = http_buf_size;
  1365. return TRUE;
  1366. }
  1367. static int get_http_headers(php_stream *stream, char **response, int *out_size TSRMLS_DC)
  1368. {
  1369. int done = FALSE;
  1370. smart_str tmp_response = {0};
  1371. char headerbuf[8192];
  1372. while (!done) {
  1373. if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
  1374. break;
  1375. }
  1376. if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
  1377. (headerbuf[0] == '\n')) {
  1378. /* empty line marks end of headers */
  1379. done = TRUE;
  1380. break;
  1381. }
  1382. /* add header to collection */
  1383. smart_str_appends(&tmp_response, headerbuf);
  1384. }
  1385. smart_str_0(&tmp_response);
  1386. (*response) = tmp_response.c;
  1387. (*out_size) = tmp_response.len;
  1388. return done;
  1389. }
  1390. /*
  1391. * Local variables:
  1392. * tab-width: 4
  1393. * c-basic-offset: 4
  1394. * End:
  1395. * vim600: sw=4 ts=4 fdm=marker
  1396. * vim<600: sw=4 ts=4
  1397. */