1<?php 2 3use dokuwiki\ErrorHandler; 4use dokuwiki\Extension\Plugin; 5use dokuwiki\HTTP\DokuHTTPClient; 6use dokuwiki\plugin\sqlite\SQLiteDB; 7use dokuwiki\plugin\statistics\DummyLogger; 8use dokuwiki\plugin\statistics\IgnoreException; 9use dokuwiki\plugin\statistics\IpResolverException; 10use dokuwiki\plugin\statistics\Logger; 11use dokuwiki\plugin\statistics\Query; 12use dokuwiki\plugin\statistics\StatisticsGraph; 13 14/** 15 * Statistics Plugin 16 * 17 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 18 * @author Andreas Gohr <andi@splitbrain.org> 19 */ 20class helper_plugin_statistics extends Plugin 21{ 22 protected ?Query $oQuery = null; 23 protected ?StatisticsGraph $oGraph = null; 24 protected ?SQLiteDB $db = null; 25 public ?DokuHTTPClient $httpClient = null; // public for testing purposes 26 27 /** 28 * Get SQLiteDB instance 29 * 30 * @return SQLiteDB|null 31 * @throws Exception when SQLite initialization failed 32 */ 33 public function getDB(): ?SQLiteDB 34 { 35 if (!$this->db instanceof SQLiteDB) { 36 if (!class_exists(SQLiteDB::class)) throw new Exception('SQLite Plugin missing'); 37 $this->db = new SQLiteDB('statistics', DOKU_PLUGIN . 'statistics/db/'); 38 } 39 return $this->db; 40 } 41 42 43 /** 44 * Return an instance of the query class 45 * 46 * @return Query 47 */ 48 public function getQuery(): Query 49 { 50 if (is_null($this->oQuery)) { 51 $this->oQuery = new Query($this); 52 } 53 return $this->oQuery; 54 } 55 56 /** 57 * Return an instance of the logger class 58 * 59 * When the logger cannot be created for any reason a DummyLogger is returned 60 * 61 * @return Logger|DummyLogger 62 */ 63 public function getLogger() 64 { 65 try { 66 return new Logger($this); 67 } catch (Exception $e) { 68 if (!$e instanceof IgnoreException) { 69 ErrorHandler::logException($e); 70 } 71 72 return new DummyLogger(); 73 } 74 } 75 76 /** 77 * Return an instance of the Graph class 78 * 79 * @return StatisticsGraph 80 */ 81 public function getGraph($from, $to, $width, $height) 82 { 83 if (is_null($this->oGraph)) { 84 $this->oGraph = new StatisticsGraph($this, $from, $to, $width, $height); 85 } 86 return $this->oGraph; 87 } 88 89 /** 90 * Just send a 1x1 pixel blank gif to the browser 91 * 92 * @called from dispatch.php 93 * 94 * @author Andreas Gohr <andi@splitbrain.org> 95 * @author Harry Fuecks <fuecks@gmail.com> 96 */ 97 public function sendGIF($transparent = true) 98 { 99 if ($transparent) { 100 $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7'); 101 } else { 102 $img = base64_decode('R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs='); 103 } 104 header('Content-Type: image/gif'); 105 header('Content-Length: ' . strlen($img)); 106 header('Connection: Close'); 107 echo $img; 108 flush(); 109 // Browser should drop connection after this 110 // Thinks it got the whole image 111 } 112 113 /** 114 * Return the location information for an IP address 115 * 116 * @throws IpResolverException 117 * @noinspection HttpUrlsUsage 118 */ 119 public function resolveIP($ip) 120 { 121 $http = $this->httpClient ?: new DokuHTTPClient(); 122 $http->timeout = 7; 123 $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only 124 125 if (!$json) { 126 throw new IpResolverException('Failed talk to ip-api.com.'); 127 } 128 try { 129 $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 130 } catch (\JsonException $e) { 131 throw new IpResolverException('Failed to decode JSON from ip-api.com.', $e->getTrace(), 0, $e); 132 } 133 if (!isset($data['status'])) { 134 throw new IpResolverException('Invalid ip-api.com result for' . $ip, $data); 135 } 136 // we do not check for 'success' status here. when the API can't resolve the IP we still log it 137 // without location data, so we won't re-query it in the next 30 days. 138 139 return $data; 140 } 141} 142