1 | <?php |
---|
2 | /**************************************************************************\ |
---|
3 | * eGroupWare API - HTTP protocol class * |
---|
4 | * http://www.egroupware.org/api * |
---|
5 | * ------------------------------------------------------------------------ * |
---|
6 | * This is not part of eGroupWare, but is used by eGroupWare. * |
---|
7 | * ------------------------------------------------------------------------ * |
---|
8 | * This program is free software; you can redistribute it and/or modify it * |
---|
9 | * under the terms of the GNU General Public License as published by the * |
---|
10 | * Free Software Foundation; either version 2 of the License, or (at your * |
---|
11 | * option) any later version. * |
---|
12 | \**************************************************************************/ |
---|
13 | |
---|
14 | |
---|
15 | class http |
---|
16 | { |
---|
17 | var $host_name = ''; |
---|
18 | var $host_port = 80; |
---|
19 | var $proxy_host_name = ''; |
---|
20 | var $proxy_host_port = 80; |
---|
21 | |
---|
22 | var $request_method = 'GET'; |
---|
23 | var $user_agent = 'Manuel Lemos HTTP class test script'; |
---|
24 | var $request_uri = ''; |
---|
25 | var $protocol_version = '1.0'; |
---|
26 | var $debug = 0; |
---|
27 | var $support_cookies = 1; |
---|
28 | var $cookies = array(); |
---|
29 | |
---|
30 | /* private variables - DO NOT ACCESS */ |
---|
31 | |
---|
32 | var $state = 'Disconnected'; |
---|
33 | var $connection = 0; |
---|
34 | var $content_length = 0; |
---|
35 | var $read_length = 0; |
---|
36 | var $request_host = ''; |
---|
37 | var $months = array( |
---|
38 | 'Jan' => '01', |
---|
39 | 'Feb' => '02', |
---|
40 | 'Mar' => '03', |
---|
41 | 'Apr' => '04', |
---|
42 | 'May' => '05', |
---|
43 | 'Jun' => '06', |
---|
44 | 'Jul' => '07', |
---|
45 | 'Aug' => '08', |
---|
46 | 'Sep' => '09', |
---|
47 | 'Oct' => '10', |
---|
48 | 'Nov' => '11', |
---|
49 | 'Dec' => '12' |
---|
50 | ); |
---|
51 | |
---|
52 | /* Private methods - DO NOT CALL */ |
---|
53 | |
---|
54 | function OutputDebug($message) |
---|
55 | { |
---|
56 | echo $message,"\n"; |
---|
57 | } |
---|
58 | |
---|
59 | function GetLine() |
---|
60 | { |
---|
61 | for($line='';;) |
---|
62 | { |
---|
63 | if(feof($this->connection) || !($part=fgets($this->connection,100))) |
---|
64 | { |
---|
65 | return(0); |
---|
66 | } |
---|
67 | $line.=$part; |
---|
68 | $length=strlen($line); |
---|
69 | if($length>=2 && substr($line,$length-2,2)=="\r\n") |
---|
70 | { |
---|
71 | $line=substr($line,0,$length-2); |
---|
72 | if($this->debug) |
---|
73 | { |
---|
74 | $this->OutputDebug("< $line"); |
---|
75 | } |
---|
76 | return($line); |
---|
77 | } |
---|
78 | } |
---|
79 | } |
---|
80 | |
---|
81 | function PutLine($line) |
---|
82 | { |
---|
83 | if($this->debug) |
---|
84 | { |
---|
85 | $this->OutputDebug("> $line"); |
---|
86 | } |
---|
87 | return(fputs($this->connection,"$line\r\n")); |
---|
88 | } |
---|
89 | |
---|
90 | function PutData($data) |
---|
91 | { |
---|
92 | if($this->debug) |
---|
93 | { |
---|
94 | $this->OutputDebug("> $data"); |
---|
95 | } |
---|
96 | return(fputs($this->connection,$data)); |
---|
97 | } |
---|
98 | |
---|
99 | function Readbytes($length) |
---|
100 | { |
---|
101 | if($this->debug) |
---|
102 | { |
---|
103 | if(($bytes=fread($this->connection,$length))!="") |
---|
104 | { |
---|
105 | $this->OutputDebug("< $bytes"); |
---|
106 | } |
---|
107 | return($bytes); |
---|
108 | } |
---|
109 | else |
---|
110 | { |
---|
111 | return(fread($this->connection,$length)); |
---|
112 | } |
---|
113 | } |
---|
114 | |
---|
115 | function EndOfInput() |
---|
116 | { |
---|
117 | return(feof($this->connection)); |
---|
118 | } |
---|
119 | |
---|
120 | function Connect($host_name,$host_port) |
---|
121 | { |
---|
122 | if($this->debug) |
---|
123 | { |
---|
124 | $this->OutputDebug("Connecting to $host_name..."); |
---|
125 | } |
---|
126 | if(($this->connection=fsockopen($host_name,$host_port,&$error))==0) |
---|
127 | { |
---|
128 | switch($error) |
---|
129 | { |
---|
130 | case -3: |
---|
131 | return('-3 socket could not be created'); |
---|
132 | case -4: |
---|
133 | return('-4 dns lookup on hostname "'.$host_name.'" failed'); |
---|
134 | case -5: |
---|
135 | return('-5 connection refused or timed out'); |
---|
136 | case -6: |
---|
137 | return('-6 fdopen() call failed'); |
---|
138 | case -7: |
---|
139 | return('-7 setvbuf() call failed'); |
---|
140 | default: |
---|
141 | return($error.' could not connect to the host "'.$host_name.'"'); |
---|
142 | } |
---|
143 | } |
---|
144 | else |
---|
145 | { |
---|
146 | if($this->debug) |
---|
147 | { |
---|
148 | $this->OutputDebug("Connected to $host_name"); |
---|
149 | } |
---|
150 | $this->state='Connected'; |
---|
151 | return(""); |
---|
152 | } |
---|
153 | } |
---|
154 | |
---|
155 | function Disconnect() |
---|
156 | { |
---|
157 | if($this->debug) |
---|
158 | { |
---|
159 | $this->OutputDebug('Disconnected from '.$this->host_name); |
---|
160 | } |
---|
161 | fclose($this->connection); |
---|
162 | return(''); |
---|
163 | } |
---|
164 | |
---|
165 | /* Public methods */ |
---|
166 | |
---|
167 | function Open($arguments) |
---|
168 | { |
---|
169 | if($this->state!='Disconnected') |
---|
170 | { |
---|
171 | return('1 already connected'); |
---|
172 | } |
---|
173 | if(IsSet($arguments['HostName'])) |
---|
174 | { |
---|
175 | $this->host_name=$arguments['HostName']; |
---|
176 | } |
---|
177 | if(IsSet($arguments['HostPort'])) |
---|
178 | { |
---|
179 | $this->host_port=$arguments['HostPort']; |
---|
180 | } |
---|
181 | if(IsSet($arguments['ProxyHostName'])) |
---|
182 | { |
---|
183 | $this->proxy_host_name=$arguments['ProxyHostName']; |
---|
184 | } |
---|
185 | if(IsSet($arguments['ProxyHostPort'])) |
---|
186 | { |
---|
187 | $this->proxy_host_port=$arguments['ProxyHostPort']; |
---|
188 | } |
---|
189 | if(strlen($this->proxy_host_name)==0) |
---|
190 | { |
---|
191 | if(strlen($this->host_name)==0) |
---|
192 | { |
---|
193 | return('2 it was not specified a valid hostname'); |
---|
194 | } |
---|
195 | $host_name = $this->host_name; |
---|
196 | $host_port = $this->host_port; |
---|
197 | } |
---|
198 | else |
---|
199 | { |
---|
200 | $host_name = $this->proxy_host_name; |
---|
201 | $host_port = $this->proxy_host_port; |
---|
202 | } |
---|
203 | $error = $this->Connect($host_name,$host_port); |
---|
204 | if(strlen($error)==0) |
---|
205 | { |
---|
206 | $this->state = 'Connected'; |
---|
207 | } |
---|
208 | return($error); |
---|
209 | } |
---|
210 | |
---|
211 | function Close() |
---|
212 | { |
---|
213 | if($this->state == 'Disconnected') |
---|
214 | { |
---|
215 | return('1 already disconnected'); |
---|
216 | } |
---|
217 | $error = $this->Disconnect(); |
---|
218 | if(strlen($error) == 0) |
---|
219 | { |
---|
220 | $this->state = 'Disconnected'; |
---|
221 | } |
---|
222 | return($error); |
---|
223 | } |
---|
224 | |
---|
225 | function SendRequest($arguments) |
---|
226 | { |
---|
227 | switch($this->state) |
---|
228 | { |
---|
229 | case 'Disconnected': |
---|
230 | return('1 connection was not yet established'); |
---|
231 | case 'Connected': |
---|
232 | break; |
---|
233 | default: |
---|
234 | return('2 can not send request in the current connection state'); |
---|
235 | } |
---|
236 | if(IsSet($arguments['RequestMethod'])) |
---|
237 | { |
---|
238 | $this->request_method = $arguments['RequestMethod']; |
---|
239 | } |
---|
240 | if(IsSet($arguments['User-Agent'])) |
---|
241 | { |
---|
242 | $this->user_agent = $arguments['User-Agent']; |
---|
243 | } |
---|
244 | if(strlen($this->request_method) == 0) |
---|
245 | { |
---|
246 | return('3 it was not specified a valid request method'); |
---|
247 | } |
---|
248 | if(IsSet($arguments['RequestURI'])) |
---|
249 | { |
---|
250 | $this->request_uri = $arguments['RequestURI']; |
---|
251 | } |
---|
252 | if(strlen($this->request_uri) == 0 || substr($this->request_uri,0,1) != '/') |
---|
253 | { |
---|
254 | return('4 it was not specified a valid request URI'); |
---|
255 | } |
---|
256 | $request_body = ''; |
---|
257 | $headers=(IsSet($arguments['Headers']) ? $arguments['Headers'] : array()); |
---|
258 | if($this->request_method == 'POST') |
---|
259 | { |
---|
260 | if(IsSet($arguments['PostValues'])) |
---|
261 | { |
---|
262 | $values = $arguments['PostValues']; |
---|
263 | if(!@is_array($values)) |
---|
264 | { |
---|
265 | return('5 it was not specified a valid POST method values array'); |
---|
266 | } |
---|
267 | $values_count = count($values); |
---|
268 | for($request_body = '',Reset($values),$value=0;$value<$values_count;Next($values),$value++) |
---|
269 | { |
---|
270 | if($value>0) |
---|
271 | { |
---|
272 | $request_body .= '&'; |
---|
273 | } |
---|
274 | $request_body.=Key($values).'='.UrlEncode($values[Key($values)]); |
---|
275 | } |
---|
276 | $headers['Content-type'] = 'application/x-www-form-urlencoded'; |
---|
277 | } |
---|
278 | } |
---|
279 | if(strlen($this->proxy_host_name) == 0) |
---|
280 | { |
---|
281 | $request_uri = $this->request_uri; |
---|
282 | } |
---|
283 | else |
---|
284 | { |
---|
285 | $request_uri = 'http://'.$this->host_name.($this->host_port==80 ? '' : ':'.$this->host_port).$this->request_uri; |
---|
286 | } |
---|
287 | if(($success = $this->PutLine($this->request_method.' '.$request_uri.' HTTP/'.$this->protocol_version))) |
---|
288 | { |
---|
289 | if(($body_length = strlen($request_body))) |
---|
290 | { |
---|
291 | $headers['Content-length'] = $body_length; |
---|
292 | } |
---|
293 | $headers_count = count($headers); |
---|
294 | for($host_set=0,Reset($headers),$header=0;$header<$headers_count;Next($headers),$header++) |
---|
295 | { |
---|
296 | $header_name = Key($headers); |
---|
297 | $header_value = $headers[$header_name]; |
---|
298 | if(@is_array($header_value)) |
---|
299 | { |
---|
300 | $header_value_count = count($header_value); |
---|
301 | for(Reset($header_value),$value=0;$value<$header_value_count;Next($header_value),$value++) |
---|
302 | { |
---|
303 | if(!$success = $this->PutLine("$header_name: ".$header_value[Key($header_value)])) |
---|
304 | { |
---|
305 | break 2; |
---|
306 | } |
---|
307 | } |
---|
308 | } |
---|
309 | else |
---|
310 | { |
---|
311 | if(!$success = $this->PutLine("$header_name: $header_value")) |
---|
312 | { |
---|
313 | break; |
---|
314 | } |
---|
315 | } |
---|
316 | if(strtolower(Key($headers)) == 'host') |
---|
317 | { |
---|
318 | $this->request_host = strtolower($header_value); |
---|
319 | $host_set = 1; |
---|
320 | } |
---|
321 | } |
---|
322 | if($success) |
---|
323 | { |
---|
324 | if(!$host_set) |
---|
325 | { |
---|
326 | $success = $this->PutLine('Host: '.$this->host_name); |
---|
327 | $this->request_host = strtolower($this->host_name); |
---|
328 | } |
---|
329 | if(count($this->cookies) && IsSet($this->cookies[0])) |
---|
330 | { |
---|
331 | $now = gmdate('Y-m-d H-i-s'); |
---|
332 | $cookies_count = count($this->cookies[0]); |
---|
333 | for($cookies = array(),$domain=0,Reset($this->cookies[0]);$domain<$cookies_count;Next($this->cookies[0]),$domain++) |
---|
334 | { |
---|
335 | $domain_pattern = Key($this->cookies[0]); |
---|
336 | $match = strlen($this->request_host)-strlen($domain_pattern); |
---|
337 | if($match >= 0 && |
---|
338 | !strcmp($domain_pattern,substr($this->request_host,$match)) && |
---|
339 | ($match == 0 || $domain_pattern[0] == '.' || $this->request_host[$match-1] == '.')) |
---|
340 | { |
---|
341 | $cookies_count_domain_pattern = count($this->cookies[0][$domain_pattern]); |
---|
342 | for(Reset($this->cookies[0][$domain_pattern]),$path_part=0;$path_part<$cookies_count_domain_pattern;Next($this->cookies[0][$domain_pattern]),$path_part++) |
---|
343 | { |
---|
344 | $path = Key($this->cookies[0][$domain_pattern]); |
---|
345 | if(strlen($this->request_uri) >= strlen($path) && substr($this->request_uri,0,strlen($path)) == $path) |
---|
346 | { |
---|
347 | $cookies_count_path = count($this->cookies[0][$domain_pattern][$path]); |
---|
348 | for(Reset($this->cookies[0][$domain_pattern][$path]),$cookie = 0;$cookie<$cookies_count_path;Next($this->cookies[0][$domain_pattern][$path]),$cookie++) |
---|
349 | { |
---|
350 | $cookie_name = Key($this->cookies[0][$domain_pattern][$path]); |
---|
351 | $expires = $this->cookies[0][$domain_pattern][$path][$cookie_name]['expires']; |
---|
352 | if($expires == '' || strcmp($now,$expires)<0) |
---|
353 | { |
---|
354 | $cookies[$cookie_name] = $this->cookies[0][$domain_pattern][$path][$cookie_name]; |
---|
355 | } |
---|
356 | } |
---|
357 | } |
---|
358 | } |
---|
359 | } |
---|
360 | } |
---|
361 | $cookies_count = count($cookies); |
---|
362 | for(Reset($cookies),$cookie=0;$cookie<$cookies_count;Next($cookies),$cookie++) |
---|
363 | { |
---|
364 | $cookie_name = Key($cookies); |
---|
365 | if(!($success = $this->PutLine('Cookie: '.UrlEncode($cookie_name).'='.$cookies[$cookie_name]['value'].';'))) |
---|
366 | { |
---|
367 | break; |
---|
368 | } |
---|
369 | } |
---|
370 | } |
---|
371 | if($success) |
---|
372 | { |
---|
373 | if($success) |
---|
374 | { |
---|
375 | $success = $this->PutLine(''); |
---|
376 | if($body_length && $success) |
---|
377 | { |
---|
378 | $success = $this->PutData($request_body); |
---|
379 | } |
---|
380 | } |
---|
381 | } |
---|
382 | } |
---|
383 | } |
---|
384 | if(!$success) |
---|
385 | { |
---|
386 | return('5 could not send the HTTP request'); |
---|
387 | } |
---|
388 | $this->state = 'RequestSent'; |
---|
389 | return(''); |
---|
390 | } |
---|
391 | |
---|
392 | function ReadReplyHeaders(&$headers) |
---|
393 | { |
---|
394 | switch($this->state) |
---|
395 | { |
---|
396 | case 'Disconnected': |
---|
397 | return('1 connection was not yet established'); |
---|
398 | case 'Connected': |
---|
399 | return('2 request was not sent'); |
---|
400 | case 'RequestSent': |
---|
401 | break; |
---|
402 | default: |
---|
403 | return('3 can not get request headers in the current connection state'); |
---|
404 | } |
---|
405 | $headers = array(); |
---|
406 | $this->content_length = $this->read_length = 0; |
---|
407 | $this->content_length_set = 0; |
---|
408 | for(;;) |
---|
409 | { |
---|
410 | $line = $this->GetLine(); |
---|
411 | if(!is_string($line)) |
---|
412 | { |
---|
413 | return('4 could not read request reply'); |
---|
414 | } |
---|
415 | if($line == '') |
---|
416 | { |
---|
417 | $this->state = 'GotReplyHeaders'; |
---|
418 | return(''); |
---|
419 | } |
---|
420 | $header_name = strtolower(strtok($line,':')); |
---|
421 | $header_value = Trim(Chop(strtok("\r\n"))); |
---|
422 | if(IsSet($headers[$header_name])) |
---|
423 | { |
---|
424 | if(is_string($headers[$header_name])) |
---|
425 | { |
---|
426 | $headers[$header_name] = array($headers[$header_name]); |
---|
427 | } |
---|
428 | $headers[$header_name][] = $header_value; |
---|
429 | } |
---|
430 | else |
---|
431 | { |
---|
432 | $headers[$header_name] = $header_value; |
---|
433 | } |
---|
434 | switch($header_name) |
---|
435 | { |
---|
436 | case 'content-length': |
---|
437 | $this->content_length = (int)$headers[$header_name]; |
---|
438 | $this->content_length_set = 1; |
---|
439 | break; |
---|
440 | case 'set-cookie': |
---|
441 | if($this->support_cookies) |
---|
442 | { |
---|
443 | $cookie_name = trim(strtok($headers[$header_name],'=')); |
---|
444 | $cookie_value = strtok(';'); |
---|
445 | $domain = $this->request_host; |
---|
446 | $path = '/'; |
---|
447 | $expires = ''; |
---|
448 | $secure = 0; |
---|
449 | while(($name=strtolower(trim(strtok('=')))) != '') |
---|
450 | { |
---|
451 | $value=UrlDecode(strtok(';')); |
---|
452 | switch($name) |
---|
453 | { |
---|
454 | case 'domain': |
---|
455 | if($value == '' || !strpos($value,'.',$value[0] == '.')) |
---|
456 | { |
---|
457 | break; |
---|
458 | } |
---|
459 | $domain = strtolower($value); |
---|
460 | break; |
---|
461 | case 'path': |
---|
462 | if($value != '' && $value[0] == '/') |
---|
463 | { |
---|
464 | $path = $value; |
---|
465 | } |
---|
466 | break; |
---|
467 | case 'expires': |
---|
468 | if(preg_match('/^((Mon|Monday|Tue|Tuesday|Wed|Wednesday|Thu|Thursday|Fri|Friday|Sat|Saturday|Sun|Sunday), )?([0-9]{2})\\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\-([0-9]{2,4}) ([0-9]{2})\\:([0-9]{2})\\:([0-9]{2}) GMT$/',$value,$matches)) |
---|
469 | { |
---|
470 | $year = (int)$matches[5]; |
---|
471 | if($year<1900) |
---|
472 | { |
---|
473 | $year += ($year<70 ? 2000 : 1900); |
---|
474 | } |
---|
475 | $expires = "$year-".$this->months[$matches[4]].'-'.$matches[3].' '.$matches[6].':'.$matches[7].':'.$matches[8]; |
---|
476 | } |
---|
477 | break; |
---|
478 | case 'secure': |
---|
479 | $secure = 1; |
---|
480 | break; |
---|
481 | } |
---|
482 | } |
---|
483 | $this->cookies[$secure][$domain][$path][$cookie_name] = array( |
---|
484 | 'name' => $cookie_name, |
---|
485 | 'value' => $cookie_value, |
---|
486 | 'domain' => $domain, |
---|
487 | 'path' => $path, |
---|
488 | 'expires' => $expires, |
---|
489 | 'secure' => $secure |
---|
490 | ); |
---|
491 | } |
---|
492 | } |
---|
493 | } |
---|
494 | } |
---|
495 | |
---|
496 | function ReadReplyBody(&$body,$length) |
---|
497 | { |
---|
498 | switch($this->state) |
---|
499 | { |
---|
500 | case 'Disconnected': |
---|
501 | return('1 connection was not yet established'); |
---|
502 | case 'Connected': |
---|
503 | return('2 request was not sent'); |
---|
504 | case 'RequestSent': |
---|
505 | if(($error = $this->ReadReplyHeaders(&$headers)) != '') |
---|
506 | { |
---|
507 | return($error); |
---|
508 | } |
---|
509 | break; |
---|
510 | case 'GotReplyHeaders': |
---|
511 | break; |
---|
512 | default: |
---|
513 | return('3 can not get request headers in the current connection state'); |
---|
514 | } |
---|
515 | $body = ''; |
---|
516 | if($this->content_length_set) |
---|
517 | { |
---|
518 | $length = min($this->content_length-$this->read_length,$length); |
---|
519 | } |
---|
520 | if($length>0 && !$this->EndOfInput() && ($body = $this->ReadBytes($length)) == '') |
---|
521 | { |
---|
522 | return('4 could not get the request reply body'); |
---|
523 | } |
---|
524 | return(''); |
---|
525 | } |
---|
526 | |
---|
527 | function GetPersistentCookies(&$cookies) |
---|
528 | { |
---|
529 | $now = gmdate('Y-m-d H-i-s'); |
---|
530 | $cookies = array(); |
---|
531 | $cookies_count = count($this->cookies); |
---|
532 | for($secure_cookies = 0,Reset($this->cookies);$secure_cookies<$cookies_count;Next($this->cookies),$secure_cookies++) |
---|
533 | { |
---|
534 | $secure = Key($this->cookies); |
---|
535 | $cookies_count_secure = count($this->cookies[$secure]); |
---|
536 | for($domain = 0,Reset($this->cookies[$secure]);$domain<$cookies_count_secure;Next($this->cookies[$secure]),$domain++) |
---|
537 | { |
---|
538 | $domain_pattern = Key($this->cookies[$secure]); |
---|
539 | $cookies_count_domain_pattern = count($this->cookies[$secure][$domain_pattern]); |
---|
540 | for(Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_part<$cookies_count_domain_pattern;Next($this->cookies[$secure][$domain_pattern]),$path_part++) |
---|
541 | { |
---|
542 | $path=Key($this->cookies[$secure][$domain_pattern]); |
---|
543 | $cookies_count_path = count($this->cookies[$secure][$domain_pattern][$path]); |
---|
544 | for(Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<$cookies_count_path;Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++) |
---|
545 | { |
---|
546 | $cookie_name = Key($this->cookies[$secure][$domain_pattern][$path]); |
---|
547 | $expires = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]['expires']; |
---|
548 | if($expires != '' && strcmp($now,$expires)<0) |
---|
549 | { |
---|
550 | $cookies[$secure][$domain_pattern][$path][$cookie_name] = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]; |
---|
551 | } |
---|
552 | } |
---|
553 | } |
---|
554 | } |
---|
555 | } |
---|
556 | } |
---|
557 | } |
---|