Assembla home | Assembla project page
 

root/trunk/Phergie/Plugin/Abstract/Base.php

Revision 249, 23.3 kB (checked in by Slynderdale, 2 months ago)

Various fixes

Line 
1 <?php
2
3 /**
4  * Base class for handlers of events received from the IRC server to provide
5  * empty handler functions for cases where no action should be taken.
6  */
7 abstract class Phergie_Plugin_Abstract_Base
8 {
9     /**
10      * Flag indicating whether or not the plugin requires its own directory
11      * for local storage
12      *
13      * @see $dir
14      * @var bool
15      */
16     protected $needsDir = false;
17
18     /**
19      * Path to the directory for the plugin if $needsDir is enabled
20      *
21      * @see $needsDir
22      */
23     protected $dir;
24
25     /**
26      * Reference back to the client, used to initiate commands
27      *
28      * @var Phergie_Driver_Abstract
29      */
30     protected $client;
31
32     /**
33      * Short class name
34      *
35      * @var string
36      */
37     protected $name;
38
39     /**
40      * Last intercepted event
41      *
42      * @var Phergie_Event_Request|Phergie_Event_Response
43      */
44     protected $event;
45
46     /**
47      * List of administrator hostmasks
48      *
49      * @var array
50      */
51     protected $adminList = array();
52
53     /**
54      * List of channel ops
55      *
56      * @var array
57      */
58     protected $opList = array();
59
60     /**
61      * Flag indicating whether or not the plugin is enabled and receives events
62      *
63      * @var bool
64      */
65     public $enabled = true;
66
67     /**
68      * Flag indicating whether or not the plugin is muted and can output events
69      *
70      * @var array
71      */
72     public $muted = array();
73
74     /**
75      * Flag indicating whether or not the plugin is a passive plugin or not
76      *
77      * @var bool
78      */
79     public $passive = false;
80
81     /**
82      * Flag indicating whether or not the plugin is an admin plugin or not
83      *
84      * @var bool
85      */
86     public $needsAdmin = false;
87
88     /**
89      * Sets a reference to the client used to initiate commands.
90      *
91      * @param Phergie_Driver_Abstract $client
92      * @return void
93      */
94     final public function __construct(Phergie_Driver_Abstract $client)
95     {
96         // Temp fix till a better one is added
97         @set_error_handler(array(__CLASS__, 'onBaseError'));
98         $this->client = $client;
99
100         $name = get_class($this);
101         $this->name = substr($name, strrpos($name, '_') + 1);
102
103         if ($this->needsDir) {
104             $class = new ReflectionClass($name);
105             $dir = dirname($class->getFilename()) . DIRECTORY_SEPARATOR . $this->name . DIRECTORY_SEPARATOR;
106             if (!file_exists($dir)) {
107                 mkdir($dir);
108             }
109             $this->dir = $dir;
110         }
111         $this->onInit();
112     }
113
114     /**
115      * Base error handler for PHP errors. This functions called onPhpError for
116      * any clas extending it to give each plugin its own error handler.
117      *
118      * @return bool
119      */
120     public final function onBaseError($errno, $errstr, $errfile, $errline)
121     {
122         if (!isset($this)) {
123             return false;
124         }
125
126         return $this->onPhpError($errno, $errstr, $errfile, $errline);
127     }
128
129     /**
130      * Returns the short name of the current plugin.
131      *
132      * @return string
133      */
134     public function getName()
135     {
136         return $this->name;
137     }
138
139     /**
140      * Returns the value of a specified configuration setting.
141      *
142      * @param string $setting Full name of the setting including the plugin
143      *                        name prefix (ex: pluginname.settingname)
144      * @return mixed
145      */
146     public function getIni($setting)
147     {
148         return $this->client->getIni($setting);
149     }
150
151     /**
152      * Returns the value of a specified configuration setting for the current
153      * plugin.
154      *
155      * @param string $setting Name of the setting without the plugin name
156      *                        prefix
157      * @return mixed
158      */
159     public function getPluginIni($setting)
160     {
161         return $this->client->getIni($this->getName() . '.' . $setting);
162     }
163
164     /**
165      * Sets the value of a specified configuration setting.
166      *
167      * @param string $setting Full name of the setting including the plugin
168      *                        name prefix (ex: pluginname.settingname)
169      * @param mixed $value New value for the setting
170      * @return void
171      */
172     public function setIni($setting, $value)
173     {
174         $this->client->setIni($setting, $value);
175     }
176
177     /**
178      * Sets the value of a specified configuration setting for the current
179      * plugin.
180      *
181      * @param string $setting Name of the setting without the plugin name
182      *                        prefix
183      * @param mixed $value New value for the setting
184      * @return void
185      */
186     public function setPluginIni($setting, $value)
187     {
188         $this->client->setIni($this->getName() . '.' . $setting, $value);
189     }
190
191     /**
192      * Stores the last intercepted event. Should only be called by drivers.
193      *
194      * @param Phergie_Event_Request|Phergie_Event_Response $event
195      * @return void
196      */
197     public function setEvent($event)
198     {
199         $this->event = $event;
200     }
201
202     /**
203      * Shorthand for the underlying driver's debugging function.
204      *
205      * @param string $message Message to log
206      * @param bool $displayDebug Toggle whether to display the message in the
207      *                           console or not
208      * @return void
209      */
210     public function debug($message, $displayDebug = true)
211     {
212         $this->client->debug('<' . strtolower($this->name) . '> ' . $message, $displayDebug);
213     }
214
215     /**
216      * Returns a TinyURL version of a given URL.
217      *
218      * @param string $url URL to convert
219      * @return string
220      */
221     public function tinyUrl($url)
222     {
223         $tiny = $url;
224         if (strlen($url) > 30) {
225             $opts = array(
226                 'http' => array(
227                     'timeout' => 4,
228                     'method' => 'POST',
229                     'header' => 'Content-type: application/x-www-form-urlencoded',
230                     'content' => http_build_query(array(
231                         'url' => $url
232                     ))
233                 )
234             );
235             $context = stream_context_create($opts);
236             $tiny = @file_get_contents('http://tinyurl.com/api-create.php', false, $context);
237             if (empty($tiny)) {
238                 $tiny = $url;
239             }
240         }
241
242         return $tiny;
243     }
244
245     /**
246      * Decodes the entities of a given string and
247      * transliterates the UTF-8 string into corresponding ASCII characters.
248      *
249      * @param string $text The text to decode.
250      * @param string $charSetFrom The charset used as the base chrset to convert from
251      * @param string $charSetTo The charset used to convert to
252      * @return string
253      */
254     public function decodeTranslit($text, $charSetFrom = 'UTF-8', $charSetTo = 'ISO-8859-1')
255     {
256         $text = html_entity_decode($text, ENT_QUOTES, $charSetFrom);
257         if (strpos($text, '&#') !== false) {
258             $text = preg_replace('/&#0*([0-9]+);/me', '$this->codeToUtf(\\1)', $text);
259             $text = preg_replace('/&#x0*([a-f0-9]+);/mei', '$this->codeToUtf(hexdec(\\1))', $text);
260         }
261
262         // Use the translit extension if installed else fallback on to basic transliteration
263         if (extension_loaded('iconv')) {
264             $text = iconv($charSetFrom, $charSetTo . '//TRANSLIT', $text);
265         // Transliteration supprt via the translit extension is still experimental
266         } else if (false && extension_loaded('translit')) {
267             $text = transliterate($text, array('han_transliterate', 'diacritical_remove'), $charSetFrom, $charSetTo);
268         } else {
269             $text = strtr($text, 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ',
270                                  'AAAAAAACEEEEIIIIDNOOOOOOUUUUYPYaaaaaaaceeeeiiiidnoooooouuuuypy');
271             $text = preg_replace('{[^a-z0-9&|"#\'\{\}()§^!°\[\]$*¨µ£%´`~=+:/;.,?><\\ _-]}i', '', $text);
272             $text = utf8_decode($text);
273         }
274
275         return $text;
276     }
277
278     /**
279      * Converts a given unicode to its UTF-8 equivalent.
280      *
281      * @param int $code The code to decode to UTF-8.
282      * @return string
283      */
284     public function codeToUtf($code)
285     {
286         $code = intval($code);
287         switch ($code) {
288             // 1 byte, 7 bits
289             case 0:
290                 return chr(0);
291             case ($code&0x7F):
292                 return chr($code);
293
294             // 2 bytes, 11 bits
295             case ($code&0x7FF):
296                 return chr(0xC0|(($code>>6) &0x1F)) .
297                        chr(0x80|($code&0x3F));
298
299             // 3 bytes, 16 bits
300             case ($code&0xFFFF):
301                 return chr(0xE0|(($code>>12) &0x0F)) .
302                        chr(0x80|(($code>>6) &0x3F)) .
303                        chr(0x80|($code&0x3F));
304
305             // 4 bytes, 21 bits
306             case ($code&0x1FFFFF):
307                 return chr(0xF0|($code>>18)) .
308                        chr(0x80|(($code>>12) &0x3F)) .
309                        chr(0x80|(($code>>6) &0x3F)) .
310                        chr(0x80|($code&0x3F));
311         }
312     }
313
314     /**
315      * Parses a string of command line like arguments and returns them as an array
316      * Parses the following:
317      *    -Strings: "" | "foo" | "foo bar" | "foo \" bar" | foo
318      *    -Options: --opt | --opt foo | --opt= | --opt=foo | --opt="" | --opt="foo" | --opt="foo bar"
319      *      @Note: If no parameter is given, the value returned by the option defaults to
320      *            1 unless its an empty value such as --opt="" or --opt=
321      *    -Flags: -flag +flag
322      *      @Note: Flags are handled using bitwise. 1 = -flag, 2 = +flag, 3 = Both a +flag and -flag
323      *
324      * The results get stored in an array for the following structure:
325      *     Array -> strings []                   -Array containing all the string matches
326      *     Array -> commands [command] => value  -Array containing all the string matches
327      *     Array -> flags [flag] => bitwise      -Array containing all the string matches
328      *     Array -> all [strings|commands|flags] -Array containing a list of all the
329      *                                            strings, commands and flags
330      *
331      * @param string $args String of arguments that will be parsed
332      * @return array
333      */
334     public function parseArguments($args)
335     {
336         // Strip away multiple spaces
337         $args = ' ' . preg_replace('#\s+#', ' ', trim($args)) . ' ';
338
339         // Check the args string for agurments
340         preg_match_all('{' .
341         // String Regex: Matches "" | "foo" | "foo bar" | "foo \" bar" | foo
342         '(".*?(?<!\\\)" | [^-+\s]+) | ' .
343         // Option Regex: Matches --opt | --opt foo | --opt= | --opt=foo | --opt="" | --opt="foo" | --opt="foo bar"
344         '(--\w+ (?:=".*?(?<!\\\)" | [=\s][^-+"\s]*)?) | ' .
345         // Flag Regex: Matches -flag +flag
346         '([-+]\w+)}xi', $args, $matches);
347         // Shifts the first group matches element off of matches
348         $matches = array_shift($matches);
349
350         // Returned data structure
351         $data = array(
352             'strings'  => array(),
353             'commands' => array(),
354             'flags'    => array(),
355             'all'      => array(
356                 'strings'  => '',
357                 'commands' => array(),
358                 'flags'    => array()
359             )
360         );
361
362         // Loop through the results
363         foreach($matches as $match) {
364             $match = trim($match);
365             // Check to see if the current match an an --option
366             if (substr($match, 0, 2) === '--') {
367                 $value = preg_split('/[=\s]/', $match, 2);
368                 $com = substr(array_shift($value), 2);
369                 $value = trim(join($value));
370                 // Strip quotes from the option's value
371                 $realValue = (substr($value, 0, 1) === '"' && substr($value, -1) === '"' || substr($match, -1) === '=');
372                 if (substr($value, 0, 1) === '"' && substr($value, -1) === '"') {
373                     $value = trim(substr($value, 1, -1));
374                 }
375                 if (!in_array($com, $data['all']['commands'])) {
376                     $data['all']['commands'][] = $com;
377                 }
378                 $data['commands'][$com] = (!empty($value) || $realValue ? str_replace('\"', '"', $value) : true);
379                 continue;
380             }
381
382             // Check to see if the current match is a -flag
383             if (substr($match, 0, 1) === '-' || substr($match, 0, 1) === '+') {
384                 $flag = substr($match, 1);
385                 if (!in_array($flag, $data['all']['flags'])) {
386                     $data['all']['flags'][] = $flag;
387                 }
388                 // Handle the flags using bitwise operations. 1=-flag, 2=+flag, 3=Both a plus and minus
389                 if (isset($data['flags'][$flag])) {
390                     $data['flags'][$flag] |= (substr($match, 0, 1) === '-' ? 0x1 : 0x2);
391                 } else {
392                     $data['flags'][$flag] = (substr($match, 0, 1) === '-' ? 0x1 : 0x2);
393                 }
394                 continue;
395             }
396
397             // Strip the quotes away from match
398             if (substr($match, 0, 1) === '"' && substr($match, -1) === '"') {
399                 $match = trim(substr($match, 1, -1));
400             }
401             // The match value isn't a flag or an option so consider it a string
402             if (!empty($match)) {
403                 $data['strings'][] = str_replace('\"', '"', $match);
404             }
405         }
406
407         $data['all']['strings'] = trim(implode(' ', $data['strings']));
408         return $data;
409     }
410
411     /**
412      * Converts a given integer/timestamp into days, minutes and seconds
413      *
414      * @param int $time The time/integer to calulate the values from
415      * @return string
416      */
417     public function getCountdown($time)
418     {
419         $return = array();
420
421         $days = floor($time / 86400);
422         if ($days > 0) {
423             $return[] = $days . 'd';
424             $time %= 86400;
425         }
426
427         $hours = floor($time / 3600);
428         if ($hours > 0) {
429             $return[] = $hours . 'h';
430             $time %= 3600;
431         }
432
433         $minutes = floor($time / 60);
434         if ($minutes > 0) {
435             $return[] = $minutes . 'm';
436             $time %= 60;
437         }
438
439         if ($time > 0 || count($return) <= 0) {
440             $return[] = ($time > 0 ? $time : '0') . 's';
441         }
442
443         return implode(' ', $return);
444     }
445
446     /**
447      * Checks if a specified plugin is loaded
448      *
449      * @param string $plugin Plugin to check
450      * @param Phergie_Driver_Abstract $client Client instance
451      * @param array $plugins List of short names for plugins that the
452      *                       bootstrap file intends to instantiate
453      * @return bool
454      */
455     public function pluginLoaded($plugin, $plugins = null, $client = null)
456     {
457         $plugin = trim($plugin);
458         if (!empty($plugin)) {
459             if (!$client) {
460                 $client = $this->client;
461             }
462             if (!is_array($plugins) || count($plugins) <= 0) {
463                 $plugins = $this->getPluginList();
464             }
465             $plugins = array_map('strtolower', $plugins);
466             if (in_array(strtolower($plugin), $plugins) && class_exists('Phergie_Plugin_' . $plugin) &&
467                 call_user_func(array('Phergie_Plugin_' . $plugin, 'checkDependencies'), $client, $plugins)) {
468                 return true;
469             }
470         }
471         return false;
472     }
473
474     /**
475      * Static call to check if a specified plugin is loaded
476      *
477      * @param string $plugin Plugin to check
478      * @param Phergie_Driver_Abstract $client Client instance
479      * @param array $plugins List of short names for plugins that the
480      *                       bootstrap file intends to instantiate
481      * @return bool
482      */
483     public static function staticPluginLoaded($plugin, $plugins, $client)
484     {
485         $plugin = trim($plugin);
486         if (!empty($plugin) && is_array($plugins)) {
487             $plugins = array_map('strtolower', $plugins);
488             if (in_array(strtolower($plugin), $plugins) && class_exists('Phergie_Plugin_' . $plugin) &&
489                 call_user_func(array('Phergie_Plugin_' . $plugin, 'checkDependencies'), $client, $plugins)) {
490                 return true;
491             }
492         }
493         return false;
494     }
495
496     /**
497      * Returns whether or not a message originated from an authorized admin or
498      * op.
499      *
500      * @param bool $hostmaskAdminOnly Whether or not to allow just hostmask
501      *                                admins or not
502      * @return bool TRUE if the message originated from an authorized
503      *              individual, FALSE otherwise
504      */
505     public function fromAdmin($hostmaskAdminOnly = false)
506     {
507         $class = $this->getName();
508         // Check to see if the current class has any admin or ops settings specified
509         $ini = $this->getPluginIni('ops');
510         if (is_null($ini)) {
511             $ini = $this->getIni('admincommand.ops');
512         }
513         $this->opList[$class] = ((strtolower($ini) === 'true' || $ini === '1'));
514
515         // Handle the admin settings
516         $ini = trim($this->getPluginIni('admins') . ' ' . $this->getIni('admincommand.admins'));
517         $this->adminList[$class] = (!empty($ini) ? $this->hostmasksToRegex($ini) : null);
518         unset($ini);
519
520         // Try to match mask against admin masks
521         if (!empty($this->adminList[$class]) &&
522             preg_match($this->adminList[$class], $this->event->getHostmask())) {
523             return true;
524         }
525
526         // Check if is op and ops are admins
527         if (!$hostmaskAdminOnly && !empty($this->opList[$class]) && $this->pluginLoaded(