Merge branch 'master' into ws
[brisk.git] / web / Obj / transports.phh
1 <?php
2 /*
3  *  sac-a-push - Obj/transports.phh
4  *
5  *  Copyright (C) 2012 Matteo Nastasi
6  *                          mailto: nastasi@alternativeoutput.it 
7  *                                  matteo.nastasi@milug.org
8  *                          web: http://www.alternativeoutput.it
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details. You should have received a
19  * copy of the GNU General Public License along with this program; if
20  * not, write to the Free Software Foundation, Inc, 59 Temple Place -
21  * Suite 330, Boston, MA 02111-1307, USA.
22  *
23  */
24
25 /*
26  *  test: SO x Browser
27  *  Values: Y: works, N: not works, @: continuous download,
28  *          D: continuous download after first reload
29  *
30  *  Stream IFRAME:
31  *
32  * Iframe| IW | FF | Ch | Op | Ko | IE
33  * ------+----+----+----+----+----+----
34  *   Lnx | D  |    | @  |    | @  | x
35  *   Win | x  | D  | @  | @  |    | D
36  *   Mac | x  |    |    |    |    |
37  *
38  *
39  *   XHR | IW | FF | Ch | Op | Ko | IE
40  * ------+----+----+----+----+----+----
41  *   Lnx | Y  |    | ^D |    | Y  | x
42  *   Win | x  | Y  | Y  |    |    | N
43  *   Mac | x  |    |    |    |    |
44  *
45  *
46  * HtmlFl| IW | FF | Ch | Op | Ko | IE
47  * ------+----+----+----+----+----+----
48  *   Lnx | N  |    |    |    | N  |
49  *   Win | x  | N  | N  |    |    | Y* (* seems delay between click and load of a new page)
50  *   Mac | x  |    |    |    |    |
51  *
52  *
53  */
54
55
56 class Transport_template {
57
58     function Transport_template() {
59     }
60
61     // return string value is appended to the content of the returned page
62     function init($enc, $header, &$header_out, $init_string, $base, $step)
63     {
64     }
65
66     static function fini($init_string, $base, $blockerr)
67     {
68     }
69
70     function chunk($step, $cont)
71     {
72     }
73
74     function is_chunked()
75     {
76     }
77 }
78
79 class Transport_websocket {
80     $magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
81
82     function Transport_websocket() {
83     }
84
85     protected function doHandshake($user, $buffer) {
86         $magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
87         $headers = array();
88         $lines = explode("\n",$buffer);
89         foreach ($lines as $line) {
90             if (strpos($line,":") !== false) {
91                 $header = explode(":",$line,2);
92                 $headers[strtolower(trim($header[0]))] = trim($header[1]);
93             } else if (stripos($line,"get ") !== false) {
94                 preg_match("/GET (.*) HTTP/i", $buffer, $reqResource);
95                 $headers['get'] = trim($reqResource[1]);
96             }
97         }
98         if (isset($headers['get'])) {
99             $user->requestedResource = $headers['get'];
100         } else {
101             // todo: fail the connection
102             $handshakeResponse = "HTTP/1.1 405 Method Not Allowed\r\n\r\n";                     
103         }
104         if (!isset($headers['host']) || !$this->checkHost($headers['host'])) {
105             $handshakeResponse = "HTTP/1.1 400 Bad Request";
106         }
107         if (!isset($headers['upgrade']) || strtolower($headers['upgrade']) != 'websocket') {
108             $handshakeResponse = "HTTP/1.1 400 Bad Request";
109         } 
110         if (!isset($headers['connection']) || strpos(strtolower($headers['connection']), 'upgrade') === FALSE) {
111             $handshakeResponse = "HTTP/1.1 400 Bad Request";
112         }
113         if (!isset($headers['sec-websocket-key'])) {
114             $handshakeResponse = "HTTP/1.1 400 Bad Request";
115         } else {
116             
117         }
118         if (!isset($headers['sec-websocket-version']) || strtolower($headers['sec-websocket-version']) != 13) {
119             $handshakeResponse = "HTTP/1.1 426 Upgrade Required\r\nSec-WebSocketVersion: 13";
120         }
121         if (($this->headerOriginRequired && !isset($headers['origin']) ) || ($this->headerOriginRequired && !$this->checkOrigin($headers['origin']))) {
122             $handshakeResponse = "HTTP/1.1 403 Forbidden";
123         }
124         if (($this->headerSecWebSocketProtocolRequired && !isset($headers['sec-websocket-protocol'])) || ($this->headerSecWebSocketProtocolRequired && !$this->checkWebsocProtocol($header['sec-websocket-protocol']))) {
125             $handshakeResponse = "HTTP/1.1 400 Bad Request";
126         }
127         if (($this->headerSecWebSocketExtensionsRequired && !isset($headers['sec-websocket-extensions'])) || ($this->headerSecWebSocketExtensionsRequired && !$this->checkWebsocExtensions($header['sec-websocket-extensions']))) {
128             $handshakeResponse = "HTTP/1.1 400 Bad Request";
129         }
130         
131         // Done verifying the _required_ headers and optionally required headers.
132         
133         if (isset($handshakeResponse)) {
134             socket_write($user->socket,$handshakeResponse,strlen($handshakeResponse));
135             $this->disconnect($user->socket);
136             return false;
137         }
138         
139         $user->headers = $headers;
140         $user->handshake = $buffer;
141         
142         $webSocketKeyHash = sha1($headers['sec-websocket-key'] . $magicGUID);
143         
144         $rawToken = "";
145         for ($i = 0; $i < 20; $i++) {
146             $rawToken .= chr(hexdec(substr($webSocketKeyHash,$i*2, 2)));
147         }
148         $handshakeToken = base64_encode($rawToken) . "\r\n";
149         
150         $subProtocol = (isset($headers['sec-websocket-protocol'])) ? $this->processProtocol($headers['sec-websocket-protocol']) : "";
151         $extensions = (isset($headers['sec-websocket-extensions'])) ? $this->processExtensions($headers['sec-websocket-extensions']) : "";
152         
153         $handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken$subProtocol$extensions\r\n";
154         socket_write($user->socket,$handshakeResponse,strlen($handshakeResponse));
155         $this->connected($user);
156     }
157     
158
159     function init($enc, $header, &$header_out, $init_string, $base, $step)
160     {
161         
162
163
164
165         $ret = sprintf("@BEGIN@ /* %s */ @END@", $init_string);
166         if ($enc != 'plain')
167             $header_out['Content-Encoding'] = $enc;
168         $header_out['Cache-Control'] = 'no-cache, must-revalidate';     // HTTP/1.1
169         $header_out['Expires']       = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Date in the past
170         $header_out['Content-type']  = 'application/xml; charset="utf-8"';
171
172         return ($ret);
173     }
174
175     static function fini($init_string, $base, $blockerr)
176     {
177         return (sprintf('@BEGIN@ %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); @END@',  ($blockerr ? 'xstm.stop(); ' : ''), $base));
178         return ("");
179     }
180
181     function chunk($step, $cont)
182     {
183         return ("@BEGIN@".$cont."@END@");
184     }
185 }
186
187 class Transport_xhr {
188
189     function Transport_xhr() {
190     }
191
192     function init($enc, $header, &$header_out, $init_string, $base, $step)
193     {
194         $ret = sprintf("@BEGIN@ /* %s */ @END@", $init_string);
195         if ($enc != 'plain')
196             $header_out['Content-Encoding'] = $enc;
197         $header_out['Cache-Control'] = 'no-cache, must-revalidate';     // HTTP/1.1
198         $header_out['Expires']       = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Date in the past
199         $header_out['Content-type']  = 'application/xml; charset="utf-8"';
200
201         return ($ret);
202     }
203
204     static function fini($init_string, $base, $blockerr)
205     {
206         return (sprintf('@BEGIN@ %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); @END@',  ($blockerr ? 'xstm.stop(); ' : ''), $base));
207         return ("");
208     }
209
210     function chunk($step, $cont)
211     {
212         return ("@BEGIN@".$cont."@END@");
213     }
214
215     function is_chunked()
216     {
217         return TRUE;
218     }
219 }
220
221 class Transport_iframe {
222
223     function Transport_iframe() {
224     }
225
226     function init($enc, $header, &$header_out, $init_string, $base, $step)
227     {
228         $ret = "";
229
230         if ($enc != 'plain')
231             $header_out['Content-Encoding'] = $enc;
232         $header_out['Cache-Control'] = 'no-cache, must-revalidate';     // HTTP/1.1
233         $header_out['Expires']       = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Date in the past
234         $header_out['Content-type']  = 'text/html; charset="utf-8"';
235         
236         $ret .= sprintf("<html>
237 <head>
238 <script type=\"text/javascript\" src=\"%scommons.js\"></script>
239 <script type=\"text/javascript\" src=\"%sxynt-streaming-ifra.js\"></script>
240 <script type=\"text/javascript\">
241 var xynt_streaming = \"ready\";", $base, $base);
242         if ($step > 0)
243             $ret .= sprintf("last_clean = %d;\n", ($step-1));
244         $ret .= sprintf("
245 window.onload = function () { try { if (xynt_streaming != \"ready\") { xynt_streaming.transp.stopped = true; } } catch(e) { /* console.log(\"catcha\"); */ } };
246 </script> 
247 </head>
248 <body>");
249         $ret .= sprintf("<!-- \n%s -->\n", $init_string);
250
251         return ($ret);
252     }
253
254     static function fini($init_string, $base, $blockerr)
255     {
256         $ret = "";
257         $ret .= sprintf("<html>
258 <head>
259 <script type=\"text/javascript\" src=\"%scommons.js\"></script>
260 <script type=\"text/javascript\" src=\"%sxynt-streaming-ifra.js\"></script>
261 <script type=\"text/javascript\">
262 var xynt_streaming = \"ready\";", $base, $base);
263         $ret .= sprintf("
264 window.onload = function () { try { if (xynt_streaming != \"ready\") { xynt_streaming.reload(); } } catch(e) { /* console.log(\"catcha\"); */ } };
265 </script>
266 </head>
267 <body>");
268         $ret .= sprintf("<!-- \n%s -->\n", $init_string);
269         $ret .= sprintf("<script id='hs%d' type='text/javascript'><!--
270 push(\"%s\");
271 // -->
272 </script>", 0, escpush($blockerr) );
273         return ($ret);
274     }
275
276     function chunk($step, $cont)
277     {
278         if ($cont == NULL) {
279             return sprintf("<script id='hs%d' type='text/javascript'><!--
280 push(null);\n// -->\n</script>", $step);
281         }
282         else {
283             return sprintf("<script id='hs%d' type='text/javascript'><!--
284 push(\"%s\");\n// -->\n</script>", $step, escpush($cont) );
285         }
286     }
287
288     function is_chunked()
289     {
290         return TRUE;
291     }
292 }
293
294 class Transport_htmlfile extends Transport_iframe {
295 }
296
297 class Transport {
298     function Transport()
299     {
300     }
301
302     static function create($transp)
303     {
304         if ($transp == 'xhr') {
305             return new Transport_xhr();
306         }
307         else if ($transp == 'htmlfile') {
308             return new Transport_htmlfile();
309         }
310         else  {
311             return new Transport_iframe();
312         }
313     }
314     static function gettype($transp)
315     {
316         if ($transp == 'xhr' || $transp == 'htmlfile') {
317             return "Transport_".$transp;
318         }
319         else {
320             return 'Transport_iframe';
321         }
322     }
323 }
324 ?>