Assembla home | Assembla project page
 

root/trunk/Phergie/Plugin/Sed.php

Revision 286, 4.8 kB (checked in by tobias382, 3 weeks ago)

Adds error suppression to the Sed plugin when invalid regular expressions are specified

Line 
1 <?php
2
3 /**
4  * Handles Sed search and replace statements in messages and outputs the last
5  * line the user said with the changes.
6  */
7 class Phergie_Plugin_Sed extends Phergie_Plugin_Abstract_Base
8 {
9     /**
10      * Prepared statement for selecting the second to last line said by the user
11      *
12      * @var PDOStatement
13      */
14     protected $select;
15
16     /**
17      * The time limit in seconds that can be checked with the sed search and
18      * replace statement, set to 0 or below to disable the check
19      *
20      * @var int
21      */
22     protected $timeLimit = 1800;
23
24     /**
25      * Initializes the database.
26      *
27      * @return void
28      */
29     public function onConnect()
30     {
31         try {
32             if (!Phergie_Plugin_Logging::databaseExists()) {
33                 return;
34             }
35
36             $this->select = Phergie_Plugin_Logging::prepare('
37                 SELECT message, tstamp
38                 FROM logs
39                 WHERE type IN (' . Phergie_Plugin_Logging::PRIVMSG . ', ' . Phergie_Plugin_Logging::ACTION . ')
40                 AND LOWER(nick) = LOWER(:name)
41                 AND LOWER(chan) = LOWER(:chan)
42                 ORDER BY tstamp DESC
43                 LIMIT :offset,1
44             ');
45         } catch (PDOException $e) { }
46     }
47
48     /**
49      * Returns whether or not the plugin's dependencies are met.
50      *
51      * @param Phergie_Driver_Abstract $client Client instance
52      * @param array $plugins List of short names for plugins that the
53      *                       bootstrap file intends to instantiate
54      * @see Phergie_Plugin_Abstract_Base::checkDependencies()
55      * @return bool TRUE if dependencies are met, FALSE otherwise
56      */
57     public static function checkDependencies(Phergie_Driver_Abstract $client, array $plugins)
58     {
59         if (!self::staticPluginLoaded('Logging', $client, $plugins)) {
60             return 'Logging plugin must be enabled';
61         }
62         return true;
63     }
64
65     public function onPrivmsg()
66     {
67         if (!Phergie_Plugin_Logging::databaseExists()) {
68             return;
69         }
70
71         $source = $this->event->getSource();
72         $message = $this->event->getArgument(1);
73         $target = $this->event->getNick();
74
75         if (empty($message)) {
76             return;
77         }
78
79         // The regex used to match the Sed search/replace syntax
80         $regex = '{^(?:([^\s:]+)\s*[:,>]?\s+)?%?s(\d*)/(.*?)(?<!\\\)/(.*?)(?:(?<!\\\)/([gimsx]*)(\d*)([gimsx]*))?$}ix';
81         if (preg_match($regex, $message, $matches)) {
82             list($match, $user, $line, $pattern, $replacement, $flags1, $limit, $flags2) = array_pad($matches, 8, NULL);
83             // Temp fix for very similar but invalid Sed statements
84             if (strpos($replacement, '/') !== false and preg_match('{(?<!\\\)/}', $replacement, $m)) {
85                 return;
86             }
87             $replacement = str_replace('\/', '/', $replacement);
88             $flags = str_replace('g', '', strtolower($flags1 . $flags2));
89             $limit = (isset($limit) && $limit > 0 ? $limit : (stripos($flags1 . $flags2, 'g') !== false ? -1 : 1));
90             $name = (!empty($user) ? $user : $target);
91             $offset = ((!isset($line) || $line <= 0 ? 1 : $line) -(!empty($user) && strtolower($user) !== strtolower($target) ? 1 : 0));
92
93             if (strtolower($name) == strtolower($this->getIni('nick'))) {
94                 $this->doPrivmsg($source, $target . ': Don\'t bother correcting me, I\'m always right!');
95                 return;
96             }
97
98             // Get the event for the specified line for the specified user
99             $params = array(
100                 ':chan' => $source,
101                 ':name' => $name,
102                 ':offset' => $offset
103             );
104
105             try {
106                 $this->select->execute($params);
107                 $log = $this->select->fetch();
108             } catch (PDOException $e) { }
109
110             if ($log) {
111                 $subject = trim($log['message']);
112                 $tstamp = strtotime($log['tstamp']);
113
114                 /**
115                  * Check to see if the last thing the the user said was said within the past
116                  * 30 minutes if so, check to see if the last thing said wasn't a blank line
117                  * and also not another Sed statement.
118                  */
119                 if (($this->timeLimit <= 0 || isset($tstamp) && ((time() - $this->timeLimit) < $tstamp)) &&
120                     !empty($subject) && !preg_match($regex, $subject, $m)) {
121                     $output = trim(@preg_replace('/' . $pattern . '/' . $flags, $replacement, $subject, $limit));
122                     if (!empty($output) && $subject != $output) {
123                         $this->doPrivmsg($source,
124                             $target . (!empty($user) && strtolower($user) != strtolower($target)? ' thinks ' . $user : '') . ' meant: ' . $output
125                         );
126                     }
127                 }
128             }
129         }
130     }
131 }
132
Note: See TracBrowser for help on using the browser.