]>
jfr.im git - irc/rizon/acid.git/blob - pyva/src/main/python/ctcp/ctcp.py
1 #!/usr/bin/python pseudoserver.py
3 # module for pypseudoserver
5 # doesn't do much by itself
9 from operator
import itemgetter
#for sort
10 from collections
import defaultdict
17 from threading
import Timer
19 class ctcp(AcidPlugin
):
27 AcidPlugin
.__init
__(self
)
29 self
.log
= logging
.getLogger(__name__
)
33 self
.dbp
.execute("CREATE TABLE IF NOT EXISTS ctcp_vbl (item SMALLINT(5) NOT NULL AUTO_INCREMENT, reply VARCHAR(255), found INT(10), PRIMARY KEY (item), UNIQUE KEY (reply));")
34 self
.dbp
.execute("CREATE TABLE IF NOT EXISTS ctcp_vlist (reply VARCHAR(255), found INT(10), UNIQUE KEY (reply));")
35 self
.dbp
.execute("CREATE TABLE IF NOT EXISTS ctcp_website (reply VARCHAR(255), found INT(10), UNIQUE KEY (reply));")
36 self
.dbp
.execute("CREATE TABLE IF NOT EXISTS ctcp_website_replies (keyword VARCHAR(255), reply VARCHAR(255), UNIQUE KEY (keyword));")
37 except Exception, err
:
38 self
.log
.exception("Error creating tables for CTCP module (%s)" % err
)
42 self
.dbp
.execute("SELECT reply FROM ctcp_vbl;")
43 for row
in self
.dbp
.fetchall(): self
.blacklist
.append(row
[0])
44 except Exception, err
:
45 self
.log
.exception("Error loading blacklists from DB for CTCP module (%s)" % err
)
48 num
= self
.dbp
.execute("SELECT num FROM record WHERE name='ctcp-total';")
50 self
.CTCP_TOTAL
= self
.dbp
.fetchone()[0]
52 num
= self
.dbp
.execute("SELECT num FROM record WHERE name='ctcp-found';")
54 self
.CTCP_BANNED
= self
.dbp
.fetchone()[0]
56 except Exception, err
:
57 self
.log
.exception("Error loading in historical records for CTCP module (%s)" % err
)
59 self
.client
= istring(self
.config
.get('ctcp', 'nick'))
65 self
.dbp
.execute("INSERT INTO record (name, num) VALUES ('ctcp-total', %s) ON DUPLICATE KEY UPDATE num=%s;", (self
.CTCP_TOTAL
,self
.CTCP_TOTAL
))
66 self
.dbp
.execute("INSERT INTO record (name, num) VALUES ('ctcp-found', %s) ON DUPLICATE KEY UPDATE num=%s;", (self
.CTCP_BANNED
,self
.CTCP_BANNED
))
67 except Exception, err
:
68 self
.log
.exception("Error updating historical records for CTCP module (%s)" % err
)
71 return int(time
.time())
74 def onUserConnect(self
, user
):
75 """Sends a CTCP VERSION request to a user
76 We setup a list with the timestamp we sent the request
77 and if we havent received the reply in X seconds, akill them.
78 Also akill if they have a blacklisted reply.
79 If user is from mibbit, request a CTCP WEBSITE, so we can track it.
80 Rizon: Temporarily ignore nick `bRO-*` / eth32 / RAC until they fix their 'client'.
84 if nick
.startswith(u
'bRO-'): return #rizon
85 elif nick
.startswith(u
'RAC_'): return #rizon
86 elif u
'eth32' in nick
: return #rizon
88 #if mibbit req website
90 self
.inter
.privmsg(self
.client
, user
.getUID(), "\001WEBSITE\001")
92 self
.inter
.privmsg(self
.client
, user
.getUID(), "\001VERSION\001")
95 def onCtcpReply(self
, creator
, recipient
, message
):
96 """Receiver for CTCP VERSION replies.
97 Check against active blacklist.
98 Remove from timeout list too.
99 #checks ctcp website too
103 user
= self
.inter
.findUser(creator
)
108 reply
= istring(message
[1:-1])
110 return # Can this throw?
113 if reply
.lower().startswith(u
"website"):
116 self
.dbp
.execute(u
"INSERT INTO ctcp_website (reply, found) VALUES(%s, 1) ON DUPLICATE KEY UPDATE found=found+1;", (reply
,))
117 except Exception, err
:
118 self
.log
.exception(u
"Error updating ctcp_website: %s (%s)" % (reply
, err
))
119 user
['website'] = reply
123 if not reply
.lower().startswith(u
"version"): return
124 reply
= unicode(reply
[8:])
125 self
.log
.debug(u
"Got CTCP VERSION: %s" % reply
)
128 self
.dbp
.execute(u
"INSERT INTO ctcp_vlist (reply, found) VALUES(%s, 1) ON DUPLICATE KEY UPDATE found=found+1;", (reply
,))
129 except Exception, err
:
130 self
.log
.exception(u
"Error updating ctcp_vlist: %s (%s)" % (reply
, err
))
132 user
['version'] = reply
133 self
.handleWebsiteReply(user
)
135 if reply
in self
.blacklist
:
136 self
.CTCP_BANNED
+= 1
138 akillserv
= self
.inter
.findUser("GeoServ") or self
.inter
.findUser("OperServ")
140 self
.inter
.privmsg(self
.client
, akillserv
.getUID(), "AKILL ADD +3d *@%s Compromised Host - Blacklisted VERSION reply." %
143 self
.inter
.privmsg(self
.client
, self
.logchan
, "CTCP: Banned %s!%s@%s - Blacklisted VERSION: %s (#%d/%d)" %
144 (user
.getNick(), user
.getUser(), user
.getIP(), reply
, self
.CTCP_BANNED
, self
.CTCP_TOTAL
))
147 self
.dbp
.execute(u
"UPDATE ctcp_vbl SET found=found+1 WHERE reply=%s;", (reply
,))
148 except Exception, err
:
149 self
.log
.exception("Error updating ctcp_vbl found count: %s (%s)" % (reply
, err
))
151 #if "dnsbl_admin" in self.parent.modules:
152 # uid2, user2 = self.parent.get_user(self.uid)
153 # self.parent.modules['dnsbl_admin'].add(user['ip'], "5", "Compromised host found by %s (%s!%s@%s)" %
154 # (user2['nick'],user['nick'], user['user'], user['ip']))
156 ## Begin Command hooks
157 def cmd_ctcpVersionAdd(self
, source
, target
, pieces
):
158 if not pieces
: return False
160 self
.dbp
.execute("INSERT INTO ctcp_vbl (reply,found) VALUES(%s,0);", (u
' '.join(pieces
),))
161 self
.inter
.privmsg(self
.client
, target
, "Added VERSION blacklist #%d" % self
.dbp
.lastrowid
)
162 except Exception, err
:
163 self
.log
.exception("Error adding version to CTCP module blacklist: (%s)" % err
)
166 self
.blacklist
.append(u
' '.join(pieces
))
169 def cmd_ctcpVersionDel(self
, source
, target
, pieces
):
170 if not pieces
: return False
172 num
= self
.dbp
.execute("SELECT reply FROM ctcp_vbl WHERE item=%s", (pieces
[0],))
173 if not num
: raise Exception("No such ID")
174 else: reply
= self
.dbp
.fetchone()[0]
176 self
.dbp
.execute("DELETE FROM ctcp_vbl WHERE item=%s;", (pieces
[0],))
177 self
.blacklist
.remove(reply
)
178 self
.inter
.privmsg(self
.client
, target
, "Removed blacklist for: %s" % reply
)
179 except Exception, err
:
180 self
.log
.exception("Error deleting version blacklist for CTCP module: (%s)" % err
)
181 self
.inter
.privmsg(self
.client
, target
, "No such ID")
185 def cmd_ctcpVersionBLList(self
, source
, target
, pieces
):
187 self
.dbp
.execute("SELECT item, reply FROM ctcp_vbl;")
188 for row
in self
.dbp
.fetchall():
189 self
.inter
.privmsg(self
.client
, target
, "#%d: %s" % (row
[0], row
[1]))
190 self
.log
.debug("CVMEMORY: %s" % self
.blacklist
)
191 except Exception, err
:
192 self
.log
.exception("Error selecting version blacklists for CTCP module: (%s)" % err
)
194 self
.inter
.privmsg(self
.client
, target
, "END OF CVLIST")
196 def cmd_ctcpWebsiteList(self
, source
, target
, pieces
):
198 w
= defaultdict(lambda: 0)
201 for k
,v
in self
.parent
.user_t
.iteritems():
205 if v
['user'] == u
'cgiirc': cm
+= 1
206 if v
['user'] == u
'qwebirc': cq
+= 1
207 if v
['user'].endswith(u
'webchat'): cq
+= 1
208 except Exception, err
:
209 self
.log
.exception("Error comprehending website list creation: %s" % (err
,))
210 self
.inter
.privmsg(self
.client
, target
, "Error creating list response")
212 #sort by number of hits
213 w
= sorted(w
.items(), key
=itemgetter(1))
215 self
.inter
.notice(self
.client
, source
, "%dx: %s" % (v
,k
))
216 self
.inter
.notice(self
.client
, source
, "END OF CWLIST(m=%d,q=%d)" % (cm
,cq
))
219 ctcpReplyCache
= None
221 def handleWebsiteReply(self
, user
):
222 if self
.ctcpReplyCache
== None:
223 self
.ctcpReplyCache
= []
224 self
.dbp
.execute("SELECT * FROM ctcp_website_replies")
225 for row
in self
.dbp
.fetchall():
226 self
.ctcpReplyCache
.append((row
[0], row
[1]))
228 for reply
in self
.ctcpReplyCache
:
229 if 'version' in user
and user
['version'].lower().find(reply
[0].lower()) != -1:
230 self
.inter
.notice(self
.client
, user
.getNick(), reply
[1])
231 elif 'website' in user
and user
['website'].lower().find(reply
[0].lower()) != -1:
232 self
.inter
.notice(self
.client
, user
.getNick(), reply
[1])
235 def cmd_ctcpReplyAdd(self
, source
, target
, pieces
):
239 self
.dbp
.execute("INSERT INTO ctcp_website_replies (keyword, reply) VALUES(%s, %s)", (pieces
[0], ' '.join(pieces
[1:])))
240 self
.inter
.privmsg(self
.client
, target
, "Added reply for keyword %s" % pieces
[0])
241 self
.ctcpReplyCache
= None
242 except Exception, err
:
243 self
.log
.exception("Error adding website reply for %s: %s" % (pieces
[0], err
))
244 self
.inter
.privmsg(self
.client
, target
, "Error creating reply keyword")
247 def cmd_ctcpReplyDel(self
, source
, target
, pieces
):
251 self
.dbp
.execute("DELETE FROM ctcp_website_replies WHERE keyword=%s", pieces
[0])
252 self
.inter
.privmsg(self
.client
, target
, "Removed reply for keyword %s" % pieces
[0])
253 self
.ctcpReplyCache
= None
254 except Exception, err
:
255 self
.log
.exception("Error removing website reply for %s: %s" % (pieces
[0], err
))
256 self
.inter
.privmsg(self
.client
, target
, "Error removing reply keyword")
259 def cmd_ctcpReplyList(self
, source
, target
, pieces
):
261 self
.dbp
.execute("SELECT * FROM ctcp_website_replies")
262 for row
in self
.dbp
.fetchall():
263 self
.inter
.privmsg(self
.client
, target
, "%s: %s" % (row
[0], row
[1]))
264 except Exception, err
:
265 self
.log
.exception("Error listing website replies: %s" % err
)
266 self
.inter
.privmsg(self
.client
, target
, "Error listing website replies.")
269 def cmd_ctcpVersionList(self
, source
, target
, pieces
):
271 subcmd
= pieces
[0].lower()
289 # pymysql apparently converts ints to strings, the %s here is intentional!
290 self
.dbp
.execute("SELECT reply, found FROM ctcp_vlist ORDER BY found DESC LIMIT %s", (maxrow
,))
293 for row
in self
.dbp
.fetchall():
294 self
.inter
.privmsg(self
.client
, target
, u
"%s [%s times]" % (row
[0], row
[1]))
296 except Exception, err
:
297 self
.log
.exception("Error listing version replies [top]: %s" % err
)
298 self
.inter
.privmsg(self
.client
, target
, "Error listing version replies.")
300 elif subcmd
== "search":
302 self
.inter
.privmsg(self
.client
, target
, 'Please give a string to search for.')
306 maxrow
= int(pieces
[2])
310 self
.dbp
.execute(u
"SELECT reply, found FROM ctcp_vlist WHERE reply LIKE '%%%s%%' LIMIT %s" % (arg
, maxrow
))
312 for row
in self
.dbp
.fetchall():
313 self
.inter
.privmsg(self
.client
, target
, u
"%s [%s times]" % (row
[0], row
[1]))
315 except Exception, err
:
316 self
.log
.exception("Error listing version replies [search]: %s" % err
)
317 self
.inter
.privmsg(self
.client
, target
, "Error listing version replies.")
319 self
.inter
.privmsg(self
.client
, target
, 'Please use one of "top" or "search."')
322 self
.inter
.privmsg(self
.client
, target
, "END OF VLIST (%d results)" % count
)
327 def getCommands(self
):
330 'callback':self
.cmd_ctcpVersionAdd
,
331 'usage':"<version string> - blacklists a VERSION string"}),
334 'callback':self
.cmd_ctcpVersionDel
,
335 'usage':"<version string> - removes a blacklisted VERSION string"}),
338 'callback':self
.cmd_ctcpVersionBLList
,
339 'usage':"- shows all active blacklisted VERSION strings"}),
342 'callback':self
.cmd_ctcpWebsiteList
,
343 'usage':"- shows source of all online mibbit users"}),
346 'callback' : self
.cmd_ctcpReplyAdd
,
347 'usage' : "<match> <reply> - sends a custom notice to specific clients"}),
350 'callback' : self
.cmd_ctcpReplyDel
,
351 'usage' : "<match> - removes the welcome notice for match"}),
354 'callback' : self
.cmd_ctcpReplyList
,
355 'usage' : "- shows the welcome notices for version matches"}),
358 'callback': self
.cmd_ctcpVersionList
,
359 'usage': "{top|search} [num|searchstring] - shows the top client version replies or searches them. Defaults to showing top"}),