3 * This class contains static helper functions for console applications
5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Core Developer Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.ship-simu.org
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 class ConsoleTools extends BaseFrameworkSystem {
26 const HTTP_EOL = "\r\n";
27 const HTTP_USER_AGENT = 'ConsoleTools/1.0';
30 * Protected constructor
34 protected function __construct () {
35 // Call parent constructor
36 parent::__construct(__CLASS__);
40 * Tries to resolve an IP address from given hostname. Currently only IPv
41 * addresses are resolved.
43 * @param $hostname Host name we shall resolve
44 * @return $ip IP address resolved from host name
45 * @todo We should connect this to a caching class to cache DNS requests
47 protected function resolveIpAddress ($hostname) {
49 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:] Host name to resolve is: %s',
54 // Default is an invalid one
58 // @TODO Here should the cacher be implemented
59 $ipResolved = gethostbyname($hostname);
62 if (($ipResolved !== false) && ($ipResolved != $hostname)) {
67 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:] Resolved IP address is: %s',
72 // Problem while resolving IP address
73 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:] Problem resolving IP address for host %s. Please check your /etc/hosts file.',
84 * Checks wether proxy configuration is used
86 * @return $isUsed Wether proxy is used
88 protected function isProxyUsed () {
90 if (!isset($GLOBALS[__METHOD__])) {
92 $GLOBALS[__METHOD__] = (($this->getConfigInstance()->getConfigEntry('proxy_host') != '') && ($this->getConfigInstance()->getConfigEntry('proxy_port') > 0));
96 return $GLOBALS[__METHOD__];
100 * Sets up a proxy tunnel for given hostname and through resource
102 * @param $host Host to connect to
103 * @param $port Port number to connect to
104 * @param $socketResource Resource of a socket
105 * @return $response Response array
107 protected function setupProxyTunnel ($host, $port, $socketResource) {
109 $response = array('', '', '');
112 // Generate CONNECT request header
113 $proxyTunnel .= 'CONNECT ' . $host . ':' . $port . ' HTTP/1.1' . self::HTTP_EOL;
114 $proxyTunnel .= 'Host: ' . $host . ':' . $port . self::HTTP_EOL;
115 $proxyTunnel .= 'Proxy-Connection: Keep-Alive' . self::HTTP_EOL;
117 // Use login data to proxy? (username at least!)
118 if ($this->getConfigInstance()->getConfigEntry('proxy_username') != '') {
120 $encodedAuth = base64_encode($this->getConfigInstance()->getConfigEntry('proxy_username') . ':' . $this->getConfigInstance()->getConfigEntry('proxy_password'));
121 $proxyTunnel .= 'Proxy-Authorization: Basic ' . $encodedAuth . self::HTTP_EOL;
125 $proxyTunnel .= self::HTTP_EOL;
126 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONSOLE-TOOLS: proxyTunnel=' . $proxyTunnel);
129 fwrite($socketResource, $proxyTunnel);
132 if (feof($socketResource)) {
133 // No response received
137 // Read the first line
138 $resp = trim(fgets($socketResource, 10240));
139 $respArray = explode(' ', $resp);
140 if (((strtolower($respArray[0]) !== 'http/1.0') && (strtolower($respArray[0]) !== 'http/1.1')) || ($respArray[1] != '200')) {
150 * Aquires the IP address of this host by reading the /etc/hostname file
151 * and solving it. It is now stored in configuration
153 * @return $ip Aquired IP address
155 public static function acquireSelfIPAddress () {
156 // Local IP by default
159 // Get a new instance
160 $helperInstance = new ConsoleTools();
163 // Get a file pointer
164 $io = FrameworkFileInputPointer::createFrameworkFileInputPointer($helperInstance->getConfigInstance()->getConfigEntry('hostname_file'));
167 $hostname = trim($io->readFromFile());
172 // Resolve the IP number
173 $ip = $helperInstance->resolveIpAddress($hostname);
174 } catch (FileIoException $e) {
175 // Fall-back to 'SESSION_SVR' which found on my Sun Station
176 if (isset($_SERVER['SESSION_SVR'])) {
178 $ip = $helperInstance->resolveIpAddress($_SERVER['SESSION_SVR']);
179 } elseif (isset($_SERVER['COMPUTERNAME'])) {
180 // May happen on some XP systems, so also try this
181 $ip = $helperInstance->resolveIpAddress($_SERVER['COMPUTERNAME']);
183 // Could not find our hostname
184 $helperInstance->debugOutput(sprintf('[%s:] WARNING: Cannot resolve my own IP address.',
185 $helperInstance->__toString()
188 } catch (FrameworkException $e) {
189 // Output debug message
190 $helperInstance->debugOutput(sprintf('[%s:] Problem while resolving own IP address: [%s|%s]:%s',
191 $helperInstance->__toString(),
198 // Set it in configuration
199 FrameworkConfiguration::getSelfInstance()->setServerAddress($ip);
206 * Determines own remote IP address (e.g. can be used to probe if we are
207 * reachable from outside by determining external IP and then connect to it.
208 * This is accomblished by connecting to the IP of www.ship-simu.org which
209 * should default to 188.138.90.169 and requesting /ip.php which does only
210 * return the content of $_SERVER['REMOTE_ADDR']. Of course, this method
211 * requires a working Internet connection.
213 * This method is taken from a user comment on php.net and heavily rewritten.
214 * Compare to following link:
215 * http://de.php.net/manual/en/function.socket-create.php#49368
217 * @return $externalAddress The determined external IP address
218 * @throws InvalidSocketException If socket initialization wents wrong or if an errors occurs
219 * @todo This should be moved out to an external class, e.g. HttpClient
220 * @todo Make IP, host name, port and script name configurable
222 public static function determineExternalIp () {
223 // Get helper instance
224 $helperInstance = new ConsoleTools();
226 // First get a socket
227 // @TODO Add some DNS caching here
230 if ($helperInstance->isProxyUsed() === true) {
231 // Resolve hostname into IP address
232 $ip = $helperInstance->resolveIpAddress($helperInstance->getConfigInstance()->getConfigEntry('proxy_host'));
234 // Connect to host through proxy connection
235 $socketResource = fsockopen($ip, $helperInstance->getConfigInstance()->getConfigEntry('proxy_port'), $errorNo, $errorStr, 30);
237 // Connect to host directly
238 $socketResource = fsockopen('188.138.90.169', 80, $errorNo, $errorStr, 30);
241 // Check if there was an error else
244 throw new InvalidSocketException(array($helperInstance, $socketResource, $errorNo, $errorStr), BaseListener::EXCEPTION_INVALID_SOCKET);
247 // Prepare the GET request
248 $request = 'GET ' . ($helperInstance->isProxyUsed() === true ? 'http://ship-simu.org' : '') . '/ip.php HTTP/1.0' . self::HTTP_EOL;
249 $request .= 'Host: ship-simu.org' . self::HTTP_EOL;
250 $request .= 'User-Agent: ' . self::HTTP_USER_AGENT . self::HTTP_EOL;
251 $request .= 'Connection: close' . self::HTTP_EOL;
254 if ($helperInstance->isProxyUsed() === true) {
256 if ($helperInstance->getConfigInstance()->getConfigEntry('proxy_connect_method') == 'Y') {
257 // Setup proxy tunnel
258 $response = $helperInstance->setupProxyTunnel('ship-simu.org', 80, $socketResource);
260 // If the response is invalid, abort
261 if ((count($response) == 3) && (empty($response[0])) && (empty($response[1])) && (empty($response[2]))) {
263 $helperInstance->debugBackTrace('Proxy tunnel not working: response=' . print_r($response, true));
266 // Add header for proxy
267 $request .= 'Proxy-Connection: Keep-Alive' . self::HTTP_EOL;
272 $request .= self::HTTP_EOL;
274 // Send it to the socket
275 fwrite($socketResource, $request);
277 // Init IP (this will always be the last line)
278 $externalAddress = 'invalid';
280 // And read the reply
281 while (!feof($socketResource)) {
283 $externalAddress = trim(fgets($socketResource, 128));
285 // Detect HTTP response
286 if ((substr($externalAddress, 0, 7) == 'HTTP/1.') && (substr($externalAddress, -6, 6) != '200 OK')) {
293 fclose($socketResource);
297 /* DEBUG: */ $helperInstance->debugOutput('CONSOLE-TOOLS: Resolved external address: ' . $externalAddress);
299 // Return determined external IP
300 return $externalAddress;
304 * Analyzes the 'environment', mostly $_SERVER, for presence of elements
305 * which indicates clearly that e.g. this script has been executed from
308 * @return $type The analyzed type, can be 'http' or 'console'
310 public static function analyzeEnvironmentForType () {
311 // Default is the console
314 // Now, do we have a request method, or query string set?
315 if ((isset($_SERVER['REQUEST_METHOD'])) || (isset($_SERVER['QUERY_STRING']))) {
316 // Possibly HTTP request
325 * Analyzes the 'environment', mostly $_SERVER, for presence of elements
326 * which indicates clearly that e.g. this script has been executed from
327 * console or web. This method should be used for class names, they
328 * currently are named differently. Here is a list to clarify this:
330 * Request type | Class type
331 * -----------------------------
335 * @return $type The analyzed type, can be 'http' or 'console'
337 public static function analyzeEnvironmentForClassType () {
338 // Default is the console
341 // Now, do we have a request method, or query string set?
342 if ((isset($_SERVER['REQUEST_METHOD'])) || (isset($_SERVER['QUERY_STRING']))) {
343 // Possibly HTTP request