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