private function _parseBlockData(ProtocolReply $reply, $startDelimiter, $endDelimter) { $reply->next(); $line = $reply->current(); if ($line != $startDelimiter) { throw new ProtocolError('Expected line beginning with "' . $startDelimiter . '"'); } $key = $line . "\n"; do { $reply->next(); $line = $reply->current(); $key .= $line . "\n"; } while ($line && $line != $endDelimter); return $key; }
/** * Read a complete reply from the controller. * * This method will process asynchronous events, call the user callback for * async events (if any) and then continue to attempt to read a reply from * the controller if data is available. * * This method blocks if there is nothing to be read from the controller. * * @param $cmd The name of the previous command sent to the controller * @return \Dapphp\TorUtils\ProtocolReply ProtocolReply object containing the response from the controller */ public function readReply($cmd = null) { $reply = new ProtocolReply($cmd); $first = true; while (true) { $data = $this->_recvData(); if ($data === false) { break; } if ($this->_isEventReplyLine($data)) { // TODO: invoke a callback or process the event and store it somewhere if ($first) { $first = false; $evreply = new ProtocolReply(); } $evreply->appendReplyLine($data); } else { if (trim($data) == '.') { $end = $this->_recvData(); if (!$this->_isEndReplyLine($end)) { throw new ProtocolError('Last read "." line - expected EndReplyLine but got "' . trim($end) . '"'); } if (!isset($evreply)) { break; } else { $data = $end; // run code at the end of the loop to process evreply } } else { if (isset($evreply)) { $evreply->appendReplyLine($data); } else { $reply->appendReplyLine($data); } } } if ($this->_isEndReplyLine($data)) { if (isset($evreply)) { $this->_asyncEventHandler($evreply); unset($evreply); $first = true; } else { break; } } } return $reply; }
/** * Pick a random dir authority to query and perform the HTTP request for directory info * * @param string $uri Uri to request * @param string $directoryServer IP and port of the directory to query * @throws \Exception No authorities responded * @return \Dapphp\TorUtils\ProtocolReply The reply from the directory authority */ private function _request($uri, $directoryServer = null) { reset($this->_serverList); do { // pick a server from the list, it is randomized in __construct $server = $this->getNextServer(); if ($server === false) { throw new \Exception('No more directory servers available to query'); } list($host, $port) = @explode(':', $server); if (!$port) { $port = 80; } $fp = fsockopen($host, $port, $errno, $errstr, $this->_connectTimeout); if (!$fp) { continue; } $request = $this->_getHttpRequest('GET', $host, $uri); fwrite($fp, $request); $response = ''; while (!feof($fp)) { $response .= fgets($fp); } fclose($fp); list($headers, $body) = explode("\r\n\r\n", $response, 2); $headers = $this->_parseHttpResponseHeaders($headers); if ($headers['status_code'] !== '200') { throw new \Exception(sprintf('Directory returned a negative response code to request. %s %s', $headers['status_code'], $headers['message'])); } $encoding = isset($headers['headers']['content-encoding']) ? $headers['headers']['content-encoding'] : null; if ($encoding == 'deflate') { if (!function_exists('gzuncompress')) { throw new \Exception('Directory response was gzip compressed but PHP does not have zlib support enabled'); } $body = gzuncompress($body); if ($body === false) { throw new \Exception('Failed to inflate response data'); } } else { if ($encoding == 'identity') { // nothing to do } else { throw new \Exception('Directory sent response in an unknown encoding: ' . $encoding); } } break; } while (true); $reply = new ProtocolReply(); $reply->appendReplyLine(sprintf('%s %s', $headers['status_code'], $headers['message'])); $reply->appendReplyLines(explode("\n", $body)); return $reply; }