Snoopy.class.php
上传用户:stephen_wu
上传日期:2008-07-05
资源大小:1757k
文件大小:39k
源码类别:

网络

开发平台:

Unix_Linux

  1. <?php
  2. /*************************************************
  3.  * $Id: Snoopy.class.php 361 2006-08-07 09:30:31Z beat $
  4. Snoopy - the PHP net client
  5. Author: Monte Ohrt <monte@ispi.net>
  6. Copyright (c): 1999-2000 ispi, all rights reserved
  7. Version: 1.2.3 + 2 fixes marked "//BB" (bugs 1482144 and 1192125 on sourceforge)
  8.  * This library is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  * This library is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Lesser General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with this library; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. You may contact the author of Snoopy by e-mail at:
  22. monte@ispi.net
  23. Or, write to:
  24. Monte Ohrt
  25. CTO, ispi
  26. 237 S. 70th suite 220
  27. Lincoln, NE 68510
  28. The latest version of Snoopy can be obtained from:
  29. http://snoopy.sourceforge.net/
  30. *************************************************/
  31. class Snoopy
  32. {
  33. /**** Public variables ****/
  34. /* user definable vars */
  35. var $host = "www.php.net"; // host name we are connecting to
  36. var $port = 80; // port we are connecting to
  37. var $proxy_host = ""; // proxy host to use
  38. var $proxy_port = ""; // proxy port to use
  39. var $proxy_user = ""; // proxy user to use
  40. var $proxy_pass = ""; // proxy password to use
  41. var $agent = "Snoopy v1.2.3"; // agent we masquerade as
  42. var $referer = ""; // referer info to pass
  43. var $cookies = array(); // array of cookies to pass
  44. // $cookies["username"]="joe";
  45. var $rawheaders = array(); // array of raw headers to send
  46. // $rawheaders["Content-type"]="text/html";
  47. var $maxredirs = 5; // http redirection depth maximum. 0 = disallow
  48. var $lastredirectaddr = ""; // contains address of last redirected address
  49. var $offsiteok = true; // allows redirection off-site
  50. var $maxframes = 0; // frame content depth maximum. 0 = disallow
  51. var $expandlinks = true; // expand links to fully qualified URLs.
  52. // this only applies to fetchlinks()
  53. // submitlinks(), and submittext()
  54. var $passcookies = true; // pass set cookies back through redirects
  55. // NOTE: this currently does not respect
  56. // dates, domains or paths.
  57. var $user = ""; // user for http authentication
  58. var $pass = ""; // password for http authentication
  59. // http accept types
  60. var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
  61. var $results = ""; // where the content is put
  62. var $error = ""; // error messages sent here
  63. var $response_code = ""; // response code returned from server
  64. var $headers = array(); // headers returned from server sent here
  65. var $maxlength = 500000; // max return data length (body)
  66. var $read_timeout = 0; // timeout on read operations, in seconds
  67. // supported only since PHP 4 Beta 4
  68. // set to 0 to disallow timeouts
  69. var $timed_out = false; // if a read operation timed out
  70. var $status = 0; // http request status
  71. var $temp_dir = "/tmp"; // temporary directory that the webserver
  72. // has permission to write to.
  73. // under Windows, this should be C:temp
  74. var $curl_path = "/usr/local/bin/curl";
  75. // Snoopy will use cURL for fetching
  76. // SSL content if a full system path to
  77. // the cURL binary is supplied here.
  78. // set to false if you do not have
  79. // cURL installed. See http://curl.haxx.se
  80. // for details on installing cURL.
  81. // Snoopy does *not* use the cURL
  82. // library functions built into php,
  83. // as these functions are not stable
  84. // as of this Snoopy release.
  85. /**** Private variables ****/
  86. var $_maxlinelen = 4096; // max line length (headers)
  87. var $_httpmethod = "GET"; // default http request method
  88. var $_httpversion = "HTTP/1.0"; // default http request version
  89. var $_submit_method = "POST"; // default submit method
  90. var $_submit_type = "application/x-www-form-urlencoded"; // default submit type
  91. var $_mime_boundary =   ""; // MIME boundary for multipart/form-data submit type
  92. var $_redirectaddr = false; // will be set if page fetched is a redirect
  93. var $_redirectdepth = 0; // increments on an http redirect
  94. var $_frameurls =  array(); // frame src urls
  95. var $_framedepth = 0; // increments on frame depth
  96. var $_isproxy = false; // set if using a proxy server
  97. var $_fp_timeout = 30; // timeout for socket connection
  98. /*======================================================================*
  99. Function: fetch
  100. Purpose: fetch the contents of a web page
  101. (and possibly other protocols in the
  102. future like ftp, nntp, gopher, etc.)
  103. Input: $URI the location of the page to fetch
  104. Output: $this->results the output text from the fetch
  105. *======================================================================*/
  106. function fetch($URI)
  107. {
  108. //preg_match("|^([^:]+)://([^:/]+)(:[d]+)*(.*)|",$URI,$URI_PARTS);
  109. $URI_PARTS = parse_url($URI);
  110. if (!empty($URI_PARTS["user"]))
  111. $this->user = $URI_PARTS["user"];
  112. if (!empty($URI_PARTS["pass"]))
  113. $this->pass = $URI_PARTS["pass"];
  114. if (empty($URI_PARTS["query"]))
  115. $URI_PARTS["query"] = '';
  116. if (empty($URI_PARTS["path"]))
  117. $URI_PARTS["path"] = '';
  118. switch(strtolower($URI_PARTS["scheme"]))
  119. {
  120. case "http":
  121. $this->host = $URI_PARTS["host"];
  122. if(!empty($URI_PARTS["port"]))
  123. $this->port = $URI_PARTS["port"];
  124. $fp = null;
  125. if($this->_connect($fp))
  126. {
  127. if($this->_isproxy)
  128. {
  129. // using proxy, send entire URI
  130. $this->_httprequest($URI,$fp,$URI,$this->_httpmethod);
  131. }
  132. else
  133. {
  134. $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
  135. // no proxy, send only the path
  136. $this->_httprequest($path, $fp, $URI, $this->_httpmethod);
  137. }
  138. $this->_disconnect($fp);
  139. if($this->_redirectaddr)
  140. {
  141. /* url was redirected, check if we've hit the max depth */
  142. if($this->maxredirs > $this->_redirectdepth)
  143. {
  144. // only follow redirect if it's on this site, or offsiteok is true
  145. if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
  146. {
  147. /* follow the redirect */
  148. ++$this->_redirectdepth;
  149. $this->lastredirectaddr=$this->_redirectaddr;
  150. $this->fetch($this->_redirectaddr);
  151. }
  152. }
  153. }
  154. if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
  155. {
  156. $frameurls = $this->_frameurls;
  157. $this->_frameurls = array();
  158. while(list(,$frameurl) = each($frameurls))
  159. {
  160. if($this->_framedepth < $this->maxframes)
  161. {
  162. $this->fetch($frameurl);
  163. ++$this->_framedepth;
  164. }
  165. else
  166. break;
  167. }
  168. }
  169. }
  170. else
  171. {
  172. return false;
  173. }
  174. return true;
  175. break;
  176. case "https":
  177. if(!$this->curl_path) {
  178. return false;
  179. }
  180. if(function_exists("is_executable")) {
  181.     if (!is_executable($this->curl_path)) {
  182.         return false;
  183.     }
  184. }
  185. $this->host = $URI_PARTS["host"];
  186. if(!empty($URI_PARTS["port"]))
  187. $this->port = $URI_PARTS["port"];
  188. if($this->_isproxy)
  189. {
  190. // using proxy, send entire URI
  191. $this->_httpsrequest($URI,$URI,$this->_httpmethod);
  192. }
  193. else
  194. {
  195. $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
  196. // no proxy, send only the path
  197. $this->_httpsrequest($path, $URI, $this->_httpmethod);
  198. }
  199. if($this->_redirectaddr)
  200. {
  201. /* url was redirected, check if we've hit the max depth */
  202. if($this->maxredirs > $this->_redirectdepth)
  203. {
  204. // only follow redirect if it's on this site, or offsiteok is true
  205. if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
  206. {
  207. /* follow the redirect */
  208. ++$this->_redirectdepth;
  209. $this->lastredirectaddr=$this->_redirectaddr;
  210. $this->fetch($this->_redirectaddr);
  211. }
  212. }
  213. }
  214. if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
  215. {
  216. $frameurls = $this->_frameurls;
  217. $this->_frameurls = array();
  218. while(list(,$frameurl) = each($frameurls))
  219. {
  220. if($this->_framedepth < $this->maxframes)
  221. {
  222. $this->fetch($frameurl);
  223. ++$this->_framedepth;
  224. }
  225. else
  226. break;
  227. }
  228. }
  229. return true;
  230. break;
  231. default:
  232. // not a valid protocol
  233. $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"n';
  234. return false;
  235. break;
  236. }
  237. }
  238. /*======================================================================*
  239. Function: submit
  240. Purpose: submit an http form
  241. Input: $URI the location to post the data
  242. $formvars the formvars to use.
  243. format: $formvars["var"] = "val";
  244. $formfiles  an array of files to submit
  245. format: $formfiles["var"] = "/dir/filename.ext";
  246. Output: $this->results the text output from the post
  247. *======================================================================*/
  248. function submit($URI, $formvars="", $formfiles="")
  249. {
  250. $postdata = $this->_prepare_post_body($formvars, $formfiles);
  251. $URI_PARTS = parse_url($URI);
  252. if (!empty($URI_PARTS["user"]))
  253. $this->user = $URI_PARTS["user"];
  254. if (!empty($URI_PARTS["pass"]))
  255. $this->pass = $URI_PARTS["pass"];
  256. if (empty($URI_PARTS["query"]))
  257. $URI_PARTS["query"] = '';
  258. if (empty($URI_PARTS["path"]))
  259. $URI_PARTS["path"] = '';
  260. switch(strtolower($URI_PARTS["scheme"]))
  261. {
  262. case "http":
  263. $this->host = $URI_PARTS["host"];
  264. if(!empty($URI_PARTS["port"]))
  265. $this->port = $URI_PARTS["port"];
  266. $fp = null;
  267. if($this->_connect($fp))
  268. {
  269. if($this->_isproxy)
  270. {
  271. // using proxy, send entire URI
  272. $this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata);
  273. }
  274. else
  275. {
  276. $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
  277. // no proxy, send only the path
  278. $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
  279. }
  280. $this->_disconnect($fp);
  281. if($this->_redirectaddr)
  282. {
  283. /* url was redirected, check if we've hit the max depth */
  284. if($this->maxredirs > $this->_redirectdepth)
  285. {
  286. if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr))
  287. $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]);
  288. // only follow redirect if it's on this site, or offsiteok is true
  289. if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
  290. {
  291. /* follow the redirect */
  292. ++$this->_redirectdepth;
  293. $this->lastredirectaddr=$this->_redirectaddr;
  294. if( strpos( $this->_redirectaddr, "?" ) > 0 )
  295. $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
  296. else
  297. $this->submit($this->_redirectaddr,$formvars, $formfiles);
  298. }
  299. }
  300. }
  301. if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
  302. {
  303. $frameurls = $this->_frameurls;
  304. $this->_frameurls = array();
  305. while(list(,$frameurl) = each($frameurls))
  306. {
  307. if($this->_framedepth < $this->maxframes)
  308. {
  309. $this->fetch($frameurl);
  310. ++$this->_framedepth;
  311. }
  312. else
  313. break;
  314. }
  315. }
  316. }
  317. else
  318. {
  319. return false;
  320. }
  321. return true;
  322. break;
  323. case "https":
  324. if(!$this->curl_path)
  325. return false;
  326. if(function_exists("is_executable")) {
  327.     if (!is_executable($this->curl_path)) {
  328.         return false;
  329.     }
  330. }
  331. $this->host = $URI_PARTS["host"];
  332. if(!empty($URI_PARTS["port"]))
  333. $this->port = $URI_PARTS["port"];
  334. if($this->_isproxy)
  335. {
  336. // using proxy, send entire URI
  337. $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata);
  338. }
  339. else
  340. {
  341. $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
  342. // no proxy, send only the path
  343. $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata);
  344. }
  345. if($this->_redirectaddr)
  346. {
  347. /* url was redirected, check if we've hit the max depth */
  348. if($this->maxredirs > $this->_redirectdepth)
  349. {
  350. if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr))
  351. $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]);
  352. // only follow redirect if it's on this site, or offsiteok is true
  353. if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
  354. {
  355. /* follow the redirect */
  356. ++$this->_redirectdepth;
  357. $this->lastredirectaddr=$this->_redirectaddr;
  358. if( strpos( $this->_redirectaddr, "?" ) > 0 )
  359. $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
  360. else
  361. $this->submit($this->_redirectaddr,$formvars, $formfiles);
  362. }
  363. }
  364. }
  365. if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
  366. {
  367. $frameurls = $this->_frameurls;
  368. $this->_frameurls = array();
  369. while(list(,$frameurl) = each($frameurls))
  370. {
  371. if($this->_framedepth < $this->maxframes)
  372. {
  373. $this->fetch($frameurl);
  374. ++$this->_framedepth;
  375. }
  376. else
  377. break;
  378. }
  379. }
  380. return true;
  381. break;
  382. default:
  383. // not a valid protocol
  384. $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"n';
  385. return false;
  386. break;
  387. }
  388. }
  389. /*======================================================================*
  390. Function: fetchlinks
  391. Purpose: fetch the links from a web page
  392. Input: $URI where you are fetching from
  393. Output: $this->results an array of the URLs
  394. *======================================================================*/
  395. function fetchlinks($URI)
  396. {
  397. if ($this->fetch($URI))
  398. {
  399. if($this->lastredirectaddr)
  400. $URI = $this->lastredirectaddr;
  401. if(is_array($this->results))
  402. {
  403. for($x=0;$x<count($this->results);$x++)
  404. $this->results[$x] = $this->_striplinks($this->results[$x]);
  405. }
  406. else
  407. $this->results = $this->_striplinks($this->results);
  408. if($this->expandlinks)
  409. $this->results = $this->_expandlinks($this->results, $URI);
  410. return true;
  411. }
  412. else
  413. return false;
  414. }
  415. /*======================================================================*
  416. Function: fetchform
  417. Purpose: fetch the form elements from a web page
  418. Input: $URI where you are fetching from
  419. Output: $this->results the resulting html form
  420. *======================================================================*/
  421. function fetchform($URI)
  422. {
  423. if ($this->fetch($URI))
  424. {
  425. if(is_array($this->results))
  426. {
  427. for($x=0;$x<count($this->results);$x++)
  428. $this->results[$x] = $this->_stripform($this->results[$x]);
  429. }
  430. else
  431. $this->results = $this->_stripform($this->results);
  432. return true;
  433. }
  434. else
  435. return false;
  436. }
  437. /*======================================================================*
  438. Function: fetchtext
  439. Purpose: fetch the text from a web page, stripping the links
  440. Input: $URI where you are fetching from
  441. Output: $this->results the text from the web page
  442. *======================================================================*/
  443. function fetchtext($URI)
  444. {
  445. if($this->fetch($URI))
  446. {
  447. if(is_array($this->results))
  448. {
  449. for($x=0;$x<count($this->results);$x++)
  450. $this->results[$x] = $this->_striptext($this->results[$x]);
  451. }
  452. else
  453. $this->results = $this->_striptext($this->results);
  454. return true;
  455. }
  456. else
  457. return false;
  458. }
  459. /*======================================================================*
  460. Function: submitlinks
  461. Purpose: grab links from a form submission
  462. Input: $URI where you are submitting from
  463. Output: $this->results an array of the links from the post
  464. *======================================================================*/
  465. function submitlinks($URI, $formvars="", $formfiles="")
  466. {
  467. if($this->submit($URI,$formvars, $formfiles))
  468. {
  469. if($this->lastredirectaddr)
  470. $URI = $this->lastredirectaddr;
  471. if(is_array($this->results))
  472. {
  473. for($x=0;$x<count($this->results);$x++)
  474. {
  475. $this->results[$x] = $this->_striplinks($this->results[$x]);
  476. if($this->expandlinks)
  477. $this->results[$x] = $this->_expandlinks($this->results[$x],$URI);
  478. }
  479. }
  480. else
  481. {
  482. $this->results = $this->_striplinks($this->results);
  483. if($this->expandlinks)
  484. $this->results = $this->_expandlinks($this->results,$URI);
  485. }
  486. return true;
  487. }
  488. else
  489. return false;
  490. }
  491. /*======================================================================*
  492. Function: submittext
  493. Purpose: grab text from a form submission
  494. Input: $URI where you are submitting from
  495. Output: $this->results the text from the web page
  496. *======================================================================*/
  497. function submittext($URI, $formvars = "", $formfiles = "")
  498. {
  499. if($this->submit($URI,$formvars, $formfiles))
  500. {
  501. if($this->lastredirectaddr)
  502. $URI = $this->lastredirectaddr;
  503. if(is_array($this->results))
  504. {
  505. for($x=0;$x<count($this->results);$x++)
  506. {
  507. $this->results[$x] = $this->_striptext($this->results[$x]);
  508. if($this->expandlinks)
  509. $this->results[$x] = $this->_expandlinks($this->results[$x],$URI);
  510. }
  511. }
  512. else
  513. {
  514. $this->results = $this->_striptext($this->results);
  515. if($this->expandlinks)
  516. $this->results = $this->_expandlinks($this->results,$URI);
  517. }
  518. return true;
  519. }
  520. else
  521. return false;
  522. }
  523. /*======================================================================*
  524. Function: set_submit_multipart
  525. Purpose: Set the form submission content type to
  526. multipart/form-data
  527. *======================================================================*/
  528. function set_submit_multipart()
  529. {
  530. $this->_submit_type = "multipart/form-data";
  531. }
  532. /*======================================================================*
  533. Function: set_submit_normal
  534. Purpose: Set the form submission content type to
  535. application/x-www-form-urlencoded
  536. *======================================================================*/
  537. function set_submit_normal()
  538. {
  539. $this->_submit_type = "application/x-www-form-urlencoded";
  540. }
  541. /*======================================================================*
  542. Function: set_submit_xml
  543. Purpose: Set the form submission content type to
  544. text/xml
  545. *======================================================================*/
  546. function set_submit_xml() //BB: function added.
  547. {
  548. $this->_submit_type = "text/xml";
  549. }
  550. /*======================================================================*
  551. Private functions
  552. *======================================================================*/
  553. /*======================================================================*
  554. Function: _striplinks
  555. Purpose: strip the hyperlinks from an html document
  556. Input: $document document to strip.
  557. Output: $match an array of the links
  558. *======================================================================*/
  559. function _striplinks($document)
  560. {
  561. $links = null;
  562. preg_match_all("'<\s*a\s.*?href\s*=\s* # find <a href=
  563. (["\'])? # find single or double quote
  564. (?(1) (.*?)\1 | ([^\s\>]+)) # if quote found, match up to next matching
  565. # quote, otherwise match up to next space
  566. 'isx",$document,$links);
  567. // catenate the non-empty matches from the conditional subpattern
  568. while(list($key,$val) = each($links[2]))
  569. {
  570. if(!empty($val))
  571. $match[] = $val;
  572. }
  573. while(list($key,$val) = each($links[3]))
  574. {
  575. if(!empty($val))
  576. $match[] = $val;
  577. }
  578. // return the links
  579. return $match;
  580. }
  581. /*======================================================================*
  582. Function: _stripform
  583. Purpose: strip the form elements from an html document
  584. Input: $document document to strip.
  585. Output: $match an array of the links
  586. *======================================================================*/
  587. function _stripform($document)
  588. {
  589. $elements = null;
  590. preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[rn]*)|(?=[rn]*))|(?=[rn]*))'Usi",$document,$elements);
  591. // catenate the matches
  592. $match = implode("rn",$elements[0]);
  593. // return the links
  594. return $match;
  595. }
  596. /*======================================================================*
  597. Function: _striptext
  598. Purpose: strip the text from an html document
  599. Input: $document document to strip.
  600. Output: $text the resulting text
  601. *======================================================================*/
  602. function _striptext($document)
  603. {
  604. // I didn't use preg eval (//e) since that is only available in PHP 4.0.
  605. // so, list your entities one by one here. I included some of the
  606. // more common ones.
  607. $search = array("'<script[^>]*?>.*?</script>'si", // strip out javascript
  608. "'<[\/\!]*?[^<>]*?>'si", // strip out html tags
  609. "'([rn])[\s]+'", // strip out white space
  610. "'&(quot|#34|#034|#x22);'i", // replace html entities
  611. "'&(amp|#38|#038|#x26);'i", // added hexadecimal values
  612. "'&(lt|#60|#060|#x3c);'i",
  613. "'&(gt|#62|#062|#x3e);'i",
  614. "'&(nbsp|#160|#xa0);'i",
  615. "'&(iexcl|#161);'i",
  616. "'&(cent|#162);'i",
  617. "'&(pound|#163);'i",
  618. "'&(copy|#169);'i",
  619. "'&(reg|#174);'i",
  620. "'&(deg|#176);'i",
  621. "'&(#39|#039|#x27);'",
  622. "'&(euro|#8364);'i", // europe
  623. "'&a(uml|UML);'", // german
  624. "'&o(uml|UML);'",
  625. "'&u(uml|UML);'",
  626. "'&A(uml|UML);'",
  627. "'&O(uml|UML);'",
  628. "'&U(uml|UML);'",
  629. "'&szlig;'i",
  630. );
  631. $replace = array( "",
  632. "",
  633. "\1",
  634. """,
  635. "&",
  636. "<",
  637. ">",
  638. " ",
  639. chr(161),
  640. chr(162),
  641. chr(163),
  642. chr(169),
  643. chr(174),
  644. chr(176),
  645. chr(39),
  646. chr(128),
  647. "