~tsimpson/ubuntu-bots/tweak

1 by Dennis Kaarsemaker
Initial checkin
1
###
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
2
# Copyright (c) 2005-2007 Dennis Kaarsemaker
1 by Dennis Kaarsemaker
Initial checkin
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of version 2 of the GNU General Public License as
6
# published by the Free Software Foundation.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
###
14
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
15
from supybot.commands import *
1 by Dennis Kaarsemaker
Initial checkin
16
import supybot.utils as utils
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
17
import supybot.ircmsgs as ircmsgs
1 by Dennis Kaarsemaker
Initial checkin
18
import supybot.ircutils as ircutils
19
import supybot.callbacks as callbacks
20
import supybot.conf as conf
21
import supybot.registry as registry
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
22
import supybot.schedule as schedule
23
35 by Dennis Kaarsemaker
adkfasdyfb
24
import re, os, time, imaplib, commands
1 by Dennis Kaarsemaker
Initial checkin
25
import xml.dom.minidom as minidom
26
from htmlentitydefs import entitydefs as entities
27
import email.FeedParser
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
28
import SOAPpy
1 by Dennis Kaarsemaker
Initial checkin
29
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
30
bad_words = ["fuck","fuk","fucking","fuking","fukin","fuckin","fucked","fuked","fucker","shit","cunt","bastard","nazi","nigger","nigga","cock","bitches","bitch"]
31
32
def makeClean(s):
33
    words = s.split()
34
    for word in words:
35
        if word.lower() in bad_words:
36
            words[words.index(word)] = "<censored>"
37
    return " ".join(words)
38
1 by Dennis Kaarsemaker
Initial checkin
39
def registerBugtracker(name, url='', description='', trackertype=''):
40
    conf.supybot.plugins.Bugtracker.bugtrackers().add(name)
41
    group       = conf.registerGroup(conf.supybot.plugins.Bugtracker.bugtrackers, name)
42
    URL         = conf.registerGlobalValue(group, 'url', registry.String(url, ''))
43
    DESC        = conf.registerGlobalValue(group, 'description', registry.String(description, ''))
44
    TRACKERTYPE = conf.registerGlobalValue(group, 'trackertype', registry.String(trackertype, ''))
45
    if url:
46
        URL.setValue(url)
47
    if description:
48
        DESC.setValue(description)
49
    if trackertype:
50
        if defined_bugtrackers.has_key(trackertype.lower()):
51
            TRACKERTYPE.setValue(trackertype.lower())
52
        else:
53
            raise BugtrackerError("Unknown trackertype: %s" % trackertype)
54
            
55
entre = re.compile('&(\S*?);')
56
def _getnodetxt(node):
57
    L = []
58
    for childnode in node.childNodes:
59
        if childnode.nodeType == childnode.TEXT_NODE:
60
            L.append(childnode.data)
61
    val = ''.join(L)
62
    if node.hasAttribute('encoding'):
63
        encoding = node.getAttribute('encoding')
64
        if encoding == 'base64':
65
            try:
66
                val = val.decode('base64')
67
            except:
68
                val = 'Cannot convert bug data from base64.'
69
    while entre.search(val):
70
        entity = entre.search(val).group(1)
71
        if entity in entities:
72
            val = entre.sub(entities[entity], val)
73
        else:
74
            val = entre.sub('?', val)
75
    return val
76
77
class BugtrackerError(Exception):
78
    """A bugtracker error"""
79
    pass
80
81
class BugNotFoundError(Exception):
82
    """Pity, bug isn't there"""
83
    pass
84
61 by Dennis Kaarsemaker
Fix CVE snarfer for new layout of CVE website
85
cvere = re.compile(r'<th.*?Description.*?<td.*?>(.*?)\s*</td>', re.I | re.DOTALL)
1 by Dennis Kaarsemaker
Initial checkin
86
class Bugtracker(callbacks.PluginRegexp):
87
    """Show a link to a bug report with a brief description"""
88
    threaded = True
89
    callBefore = ['URL']
51 by Dennis Kaarsemaker
Misc fixes
90
    regexps = ['turlSnarfer', 'bugSnarfer', 'oopsSnarfer', 'cveSnarfer']
1 by Dennis Kaarsemaker
Initial checkin
91
92
    def __init__(self, irc):
93
        callbacks.PluginRegexp.__init__(self, irc)
94
        self.db = ircutils.IrcDict()
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
95
        events = []
1 by Dennis Kaarsemaker
Initial checkin
96
        for name in self.registryValue('bugtrackers'):
97
            registerBugtracker(name)
98
            group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
99
            if group.trackertype() in defined_bugtrackers.keys():
100
                self.db[name] = defined_bugtrackers[group.trackertype()](name, group.url(), group.description())
101
            else:
136 by Terence Simpson
* Fix a couple more typos/strange supybot errors.
102
#                raise BugtrackerError("Unknown trackertype: %s (%s)" % (group.trackertype(), name))
103
                self.log.warning("Unknown trackertype: %s (%s)" % (group.trackertype(), name))
1 by Dennis Kaarsemaker
Initial checkin
104
        self.shorthand = utils.abbrev(self.db.keys())
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
105
106
        # Schedule bug reporting
134 by Terence Simpson
Fix a couple of typos
107
        self.shown = {}
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
108
        if self.registryValue('imap_server') and self.registryValue('reportercache'):
109
            try:
110
                schedule.removeEvent(self.name() + '.bugreporter')
111
            except:
112
                pass
113
            schedule.addPeriodicEvent(lambda: self.reportnewbugs(irc),  60, name=self.name() + '.bugreporter')
87 by Terence Simpson
Made the IMAP code as generic as possible
114
            self.events += [self.name() + '.bugreporter']
115
            self.log.info('Adding scheduled event "%s.bugreporter"' % self.name())
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
116
35 by Dennis Kaarsemaker
adkfasdyfb
117
    def die(self):
118
        try:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
119
           for event in self.events:
87 by Terence Simpson
Made the IMAP code as generic as possible
120
                self.log.info('Removing scheduled event "%s"' % event)
121
                schedule.removeEvent(event)
135 by Terence Simpson
More typos
122
                schedule.removeEvent(self.name())
35 by Dennis Kaarsemaker
adkfasdyfb
123
        except:
124
            pass
125
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
126
    def is_ok(self, channel, tracker, bug):
127
        now = time.time()
128
        for k in self.shown.keys():
35 by Dennis Kaarsemaker
adkfasdyfb
129
            if self.shown[k] < now - self.registryValue('repeatdelay', channel):
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
130
                self.shown.pop(k)
131
        if (channel, tracker, bug) not in self.shown:
132
            self.shown[(channel, tracker, bug)] = now
133
            return True
134
        return False
135
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
136
    def is_new(self, tracker, tag, id):
137
        bugreporter_base = self.registryValue('reportercache')
138
        if not os.path.exists(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id))):
139
            try:
140
                os.makedirs(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000))))
141
            except:
142
                pass
143
            fd = open(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id)),'w')
144
            fd.close()
145
            return True
146
        return False
147
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
148
    def reportnewbugs(self,irc):
149
        # Compile list of bugs
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
150
        self.log.info("Checking for new bugs")
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
151
        bugs = {}
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
152
        if self.registryValue('imap_ssl'):
153
            sc = imaplib.IMAP4_SSL(self.registryValue('imap_server'))
154
        else:
155
            sc = imaplib.IMAP4(self.registryValue('imap_server'))
156
        sc.login(self.registryValue('imap_user'), self.registryValue('imap_password'))
35 by Dennis Kaarsemaker
adkfasdyfb
157
        sc.select('INBOX')
158
        new_mail = sc.search(None, '(UNSEEN)')[1][0].split()[:20]
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
159
160
        # Read all new mail
35 by Dennis Kaarsemaker
adkfasdyfb
161
        for m in new_mail:
162
            msg = sc.fetch(m, 'RFC822')[1][0][1]
163
            fp = email.FeedParser.FeedParser()
87 by Terence Simpson
Made the IMAP code as generic as possible
164
            sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again
35 by Dennis Kaarsemaker
adkfasdyfb
165
            fp.feed(msg)
166
            bug = fp.close()
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
167
            tag = None
87 by Terence Simpson
Made the IMAP code as generic as possible
168
169
            if 'X-Launchpad-Bug' not in bug.keys():
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
170
                self.log.info('Ignoring e-mail with no detectable bug (Not from Launchpad)')            
35 by Dennis Kaarsemaker
adkfasdyfb
171
                continue
87 by Terence Simpson
Made the IMAP code as generic as possible
172
            else:
173
                tag = bug['X-Launchpad-Bug']
174
                if 'distribution=' not in tag and 'product=' not in tag:
175
                    self.log.info('Ignoring e-mail with no detectable bug (no distro/product)')
176
                    continue
177
                else:
178
                    tag = tag.split(';')[0].strip().replace("product=",'').replace("distribution=","")
179
180
            if not tag:
181
                self.log.info('Ignoring e-mail with no detectible bug (bad tag)')
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
182
                
183
            tag = tag[tag.find('+')+1:tag.find('@')]
35 by Dennis Kaarsemaker
adkfasdyfb
184
            if tag not in bugs:
185
                bugs[tag] = {}
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
186
51 by Dennis Kaarsemaker
Misc fixes
187
            # Determine bugtracker type (currently only Launchpad is supported anyway)
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
188
            if bug['X-Launchpad-Bug']:
51 by Dennis Kaarsemaker
Misc fixes
189
                tracker = self.db['launchpad']
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
190
                id = int(bug['Reply-To'].split()[1])
87 by Terence Simpson
Made the IMAP code as generic as possible
191
                subj = bug['Subject'];
192
                if '[NEW]' not in subj: #Not a new bug
193
                    continue
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
194
                if self.is_new(tracker, tag, id):
195
                    component = bug['X-Launchpad-Bug']
196
                    if 'component' in component:
197
                        component = component[component.find('component=')+10:]
198
                        component = component[:component.find(';')].replace('None','')
199
                    else:
200
                        component = ''
35 by Dennis Kaarsemaker
adkfasdyfb
201
                    try:
202
                        if component:
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
203
                            bugs[tag][id] = self.get_bug('',tracker, id, False)[0].replace('"','(%s) "' % component, 1)
35 by Dennis Kaarsemaker
adkfasdyfb
204
                        else:
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
205
                            bugs[tag][id] = self.get_bug('',tracker, id, False)[0]
51 by Dennis Kaarsemaker
Misc fixes
206
                        if '[apport]' in bugs[tag][id]:
207
                            bugs[tag].pop(id)
35 by Dennis Kaarsemaker
adkfasdyfb
208
                    except:
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
209
                        self.log.info("Unable to get new bug %d" % id)
39 by Dennis Kaarsemaker
Made bantracker releaseable
210
                        pass
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
211
            else:
212
                self.log.info('Ignoring e-mail with no detectable bug')
87 by Terence Simpson
Made the IMAP code as generic as possible
213
214
        reported_bugs = 0
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
215
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
216
        for c in irc.state.channels:
87 by Terence Simpson
Made the IMAP code as generic as possible
217
            tags = self.registryValue('bugReporter', channel=c)
218
            if not tags:
219
                continue
220
            for tag in tags.split(','):
221
                if not tag or tag not in bugs.keys():
222
                    continue
223
                for b in sorted(bugs[tag].keys()):
224
                    irc.queueMsg(ircmsgs.privmsg(c,'New bug: #%s' % bugs[tag][b][bugs[tag][b].find('bug ')+4:]))
225
                    reported_bugs = reported_bugs+1
1 by Dennis Kaarsemaker
Initial checkin
226
227
    def add(self, irc, msg, args, name, trackertype, url, description):
228
        """<name> <type> <url> [<description>]
229
230
        Add a bugtracker <url> to the list of defined bugtrackers. <type> is the
51 by Dennis Kaarsemaker
Misc fixes
231
        type of the tracker (currently only Launchpad, Debbugs, Bugzilla,
95 by Terence Simpson
Big-Old-Update Edition
232
        Issuezilla, Mantis and Trac are known). <name> is the name that will be used to
1 by Dennis Kaarsemaker
Initial checkin
233
        reference the bugzilla in all commands. Unambiguous abbreviations of
234
        <name> will be accepted also.  <description> is the common name for the
235
        bugzilla and will be listed with the bugzilla query; if not given, it
236
        defaults to <name>.
237
        """
238
        name = name.lower()
239
        if not description:
240
            description = name
241
        if url[-1] == '/':
242
            url = url[:-1]
243
        trackertype = trackertype.lower()
244
        if trackertype in defined_bugtrackers:
245
            self.db[name] = defined_bugtrackers[trackertype](name,url,description)
246
        else:
247
            irc.error("Bugtrackers of type '%s' are not understood" % trackertype)
248
            return
249
        registerBugtracker(name, url, description, trackertype)
250
        self.shorthand = utils.abbrev(self.db.keys())
251
        irc.replySuccess()
126 by Terence Simpson
Update code from working branch and fix a couple of bugs
252
    add = wrap(add, [('checkCapability', 'admin'), 'something', 'something', 'url', additional('text')])
1 by Dennis Kaarsemaker
Initial checkin
253
254
    def remove(self, irc, msg, args, name):
255
        """<abbreviation>
256
257
        Remove the bugtracker associated with <abbreviation> from the list of
258
        defined bugtrackers.
259
        """
260
        try:
261
            name = self.shorthand[name.lower()]
262
            del self.db[name]
263
            self.registryValue('bugtrackers').remove(name)
264
            self.shorthand = utils.abbrev(self.db.keys())
265
            irc.replySuccess()
266
        except KeyError:
267
            s = self.registryValue('replyNoBugtracker', msg.args[0])
268
            irc.error(s % name)
126 by Terence Simpson
Update code from working branch and fix a couple of bugs
269
    remove = wrap(remove, [('checkCapability', 'admin'), 'text'])
1 by Dennis Kaarsemaker
Initial checkin
270
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
271
    def rename(self, irc, msg, args, oldname, newname, newdesc):
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
272
        """<oldname> <newname>
273
72 by Terence Simpson
Added @help values
274
        Rename the bugtracker associated with <oldname> to <newname>.
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
275
        """
276
        try:
277
            name = self.shorthand[oldname.lower()]
278
            group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
279
            d = group.description()
280
            if newdesc:
281
                d = newdesc
282
            self.db[newname] = defined_bugtrackers[group.trackertype()](name,group.url(),d)
283
            registerBugtracker(newname, group.url(), d, group.trackertype())
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
284
            del self.db[name]
285
            self.registryValue('bugtrackers').remove(name)
286
            self.shorthand = utils.abbrev(self.db.keys())
287
            irc.replySuccess()
288
        except KeyError:
289
            s = self.registryValue('replyNoBugtracker', msg.args[0])
290
            irc.error(s % name)
126 by Terence Simpson
Update code from working branch and fix a couple of bugs
291
    rename = wrap(rename, [('checkCapability', 'admin'), 'something','something', additional('text')])
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
292
1 by Dennis Kaarsemaker
Initial checkin
293
    def list(self, irc,  msg, args, name):
294
        """[abbreviation]
295
296
        List defined bugtrackers. If [abbreviation] is specified, list the
297
        information for that bugtracker.
298
        """
299
        if name:
300
            name = name.lower()
301
            try:
302
                name = self.shorthand[name]
303
                (url, description, type) = (self.db[name].url, self.db[name].description,
304
                                            self.db[name].__class__.__name__)
305
                irc.reply('%s: %s, %s [%s]' % (name, description, url, type))
306
            except KeyError:
307
                s = self.registryValue('replyNoBugtracker', msg.args[0])
308
                irc.error(s % name)
309
        else:
310
            if self.db:
311
                L = self.db.keys()
312
                L.sort()
313
                irc.reply(utils.str.commaAndify(L))
314
            else:
315
                irc.reply('I have no defined bugtrackers.')
316
    list = wrap(list, [additional('text')])
317
318
    def bugSnarfer(self, irc, msg, match):
144 by Terence Simpson
Tweak regex for bugSnarfer in Bugtracker (LP: #148777)
319
        r"""\b(?P<bt>(([a-z0-9]+)?\s+bugs?|[a-z0-9]+))\s+#?(?P<bug>\d+(?!\d*[\-\.]\d+)((,|\s*(and|en|et|und|ir))\s*#?\d+(?!\d*[\-\.]\d+))*)"""
28 by Dennis Kaarsemaker
Just keep fixing
320
        if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
1 by Dennis Kaarsemaker
Initial checkin
321
            return
43 by Dennis Kaarsemaker
More fixes everywhere
322
        nbugs = msg.tagged('nbugs')
323
        if not nbugs: nbugs = 0
324
        if nbugs >= 5:
325
            return
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
326
1 by Dennis Kaarsemaker
Initial checkin
327
        # Don't double on commands
328
        s = str(msg).split(':')[2]
92 by Terence Simpson
Bugtracker: Small fix
329
        if s and s[0] in str(conf.supybot.reply.whenAddressedBy.chars):
1 by Dennis Kaarsemaker
Initial checkin
330
            return
331
        sure_bug = match.group('bt').endswith('bug') or match.group('bt').endswith('bug')
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
332
        
1 by Dennis Kaarsemaker
Initial checkin
333
        # Get tracker name
334
        bugids = match.group('bug')
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
335
        reps = ((' ',''),('#',''),('and',','),('en',','),('et',','),('und',','),('ir',','))
1 by Dennis Kaarsemaker
Initial checkin
336
        for r in reps:
337
            bugids = bugids.replace(r[0],r[1])
43 by Dennis Kaarsemaker
More fixes everywhere
338
        bugids = bugids.split(',')[:5-nbugs]
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
339
        if not sure_bug:
340
            bugids = [x for x in bugids if int(x) > 100]
43 by Dennis Kaarsemaker
More fixes everywhere
341
        msg.tag('nbugs', nbugs + len(bugids))
1 by Dennis Kaarsemaker
Initial checkin
342
        bt = map(lambda x: x.lower(), match.group('bt').split())
343
        name = ''
344
        if len(bt) == 1 and not (bt[0] in ['bug','bugs']):
345
            try:
346
                name = bt[0].lower()
347
                tracker = self.db[name]
348
            except:
349
                return
350
        elif len(bt) == 2:
351
            try:
352
                name = bt[0].lower()
353
                tracker = self.db[name]
354
            except:
355
                name = ''
356
                pass
357
        if not name:
35 by Dennis Kaarsemaker
adkfasdyfb
358
            snarfTarget = self.registryValue('snarfTarget', msg.args[0]).lower()
1 by Dennis Kaarsemaker
Initial checkin
359
            if not snarfTarget:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
360
                self.log.warning("no snarfTarget for Bugtracker")
1 by Dennis Kaarsemaker
Initial checkin
361
                return
362
            try:
363
                name = self.shorthand[snarfTarget.lower()]
364
            except:
365
               s = self.registryValue('replyNoBugtracker', name)
366
               irc.error(s % name)
367
        try:
368
            tracker = self.db[name]
369
        except KeyError:
370
            s = self.registryValue('replyNoBugtracker', name)
371
            irc.error(s % name)
372
        else:
373
            for bugid in bugids:
374
                bugid = int(bugid)
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
375
                if bugid == 1 and tracker == self.db["lp"]:
376
                    irc.reply("https://bugs.launchpad.net/ubuntu/+bug/1 (Not reporting large bug)")
377
                    continue
1 by Dennis Kaarsemaker
Initial checkin
378
                try:
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
379
                    report = self.get_bug(msg.args[0],tracker,bugid,self.registryValue('showassignee', msg.args[0]))
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
380
                except BugNotFoundError:
43 by Dennis Kaarsemaker
More fixes everywhere
381
                    if self.registryValue('replyWhenNotFound'):
382
                        irc.error("%s bug %d could not be found" % (tracker.description, bugid))
1 by Dennis Kaarsemaker
Initial checkin
383
                except BugtrackerError, e:
27 by Dennis Kaarsemaker
Yes, more features!
384
                    if 'private' in str(e):
385
                        irc.reply("Bug %d on http://launchpad.net/bugs/%d is private" % (bugid, bugid))
386
                        return
1 by Dennis Kaarsemaker
Initial checkin
387
                    if not sure_bug and bugid < 30:
388
                        return
389
                    irc.error(str(e))
390
                else:
35 by Dennis Kaarsemaker
adkfasdyfb
391
                    for r in report:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
392
                        irc.reply(makeClean(r), prefixNick=False)
1 by Dennis Kaarsemaker
Initial checkin
393
394
    def turlSnarfer(self, irc, msg, match):
43 by Dennis Kaarsemaker
More fixes everywhere
395
        r"(?P<tracker>https?://\S*?)/(Bugs/0*|str.php\?L|show_bug.cgi\?id=|bugreport.cgi\?bug=|(bugs|\+bug)/|ticket/|tracker/|\S*aid=)(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?"
28 by Dennis Kaarsemaker
Just keep fixing
396
        if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
1 by Dennis Kaarsemaker
Initial checkin
397
            return
43 by Dennis Kaarsemaker
More fixes everywhere
398
        nbugs = msg.tagged('nbugs')
399
        if not nbugs: nbugs = 0
400
        if nbugs >= 5:
401
            return
402
        msg.tag('nbugs', nbugs+1)
1 by Dennis Kaarsemaker
Initial checkin
403
        try:
404
            tracker = self.get_tracker(match.group(0),match.group('sfurl'))
405
            if not tracker:
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
406
                return
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
407
            report = self.get_bug(msg.args[0],tracker,int(match.group('bug')),self.registryValue('showassignee', msg.args[0]), do_url = False)
1 by Dennis Kaarsemaker
Initial checkin
408
        except BugtrackerError, e:
409
            irc.error(str(e))
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
410
        except BugNotFoundError, e:
411
            irc.error("%s bug %s not found" % (tracker, match.group('bug')))
1 by Dennis Kaarsemaker
Initial checkin
412
        else:
35 by Dennis Kaarsemaker
adkfasdyfb
413
            for r in report:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
414
                irc.reply(makeClean(r), prefixNick=False)
1 by Dennis Kaarsemaker
Initial checkin
415
    turlSnarfer = urlSnarfer(turlSnarfer)
416
417
    # Only useful for launchpad developers
418
    def oopsSnarfer(self, irc, msg, match):
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
419
        r"OOPS-(?P<oopsid>\d*[\dA-Z]+)"
51 by Dennis Kaarsemaker
Misc fixes
420
        if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
421
            return
1 by Dennis Kaarsemaker
Initial checkin
422
        oopsid = match.group(1)
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
423
        if oopsid.lower() == "tools":
424
            return
425
        irc.reply("https://lp-oops.canonical.com/oops.py/?oopsid=%s" % oopsid, prefixNick=False)
426
        #irc.reply("https://devpad.canonical.com/~jamesh/oops.cgi/%s" % oopsid, prefixNick=False)
51 by Dennis Kaarsemaker
Misc fixes
427
428
    def cveSnarfer(self, irc, msg, match):
429
        r"(cve[- ]\d{4}[- ]\d{4})"
430
        if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
431
            return
432
        cve = match.group(1).replace(' ','-').upper()
433
        url = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' % cve
434
        cvedata = utils.web.getUrl(url)
435
        m = cvere.search(cvedata)
436
        if m:
437
            cve = m.group(1).replace('\n', ' ')
438
            irc.reply("%s (%s)" % (cve,url))
439
440
    def cveUrlSnarfer(self, irc, msg, match):
441
        pass
1 by Dennis Kaarsemaker
Initial checkin
442
443
    def get_tracker(self,snarfurl,sfdata):
44 by Dennis Kaarsemaker
Fixy fixy!
444
        snarfurl = snarfurl.replace('sf.net','sourceforge.net')
27 by Dennis Kaarsemaker
Yes, more features!
445
        snarfhost = snarfurl.replace('http://','').replace('https://','')
446
        if '/' in snarfurl:
447
            snarfhost = snarfhost[:snarfhost.index('/')]
1 by Dennis Kaarsemaker
Initial checkin
448
        for t in self.db.keys():
449
            tracker = self.db[t]
450
            url = tracker.url.replace('http://','').replace('https://','')
451
            if 'sourceforge.net' in url:
452
                # Try to find the correct sf tracker
453
                if str(sfdata) in tracker.url:
454
                    return tracker
455
            if '/' in url:
456
                url = url[:url.index('/')]
27 by Dennis Kaarsemaker
Yes, more features!
457
            if url in snarfhost:
1 by Dennis Kaarsemaker
Initial checkin
458
                return tracker
459
        if 'sourceforge.net' in snarfurl:
460
            return self.db['sourceforge']
461
        # No tracker found, bummer. Let's try and add one
462
        if 'show_bug.cgi' in snarfurl:
463
            tracker = Bugzilla().get_tracker(snarfurl)
464
            if tracker:
465
                self.db[tracker.name] = tracker
466
                self.shorthand = utils.abbrev(self.db.keys())
467
                return tracker
468
        return None
469
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
470
    def get_bug(self, channel, tracker, id, do_assignee, do_url = True):
35 by Dennis Kaarsemaker
adkfasdyfb
471
        reports = []
472
        for r in tracker.get_bug(id):
48 by Dennis Kaarsemaker
Fixes in the bugtracker plugin, first attempt at not disturbing a meeting
473
            if not self.is_ok(channel, tracker, r[0]):
474
                continue
35 by Dennis Kaarsemaker
adkfasdyfb
475
            (bid, product, title, severity, status, assignee, url) = r
476
            severity = severity[0].upper() + severity[1:].lower()
477
            status = status[0].upper() + status[1:].lower()
478
            if not do_url:
479
                url = ''
480
            if product:
481
                reports.append("%s bug %s in %s \"%s\" [%s,%s] %s" % (tracker.description, bid, product, 
482
                                                                      title, severity, status, url))
483
            else:
484
                reports.append("%s bug %s \"%s\" [%s,%s] %s" % (tracker.description, bid, title, severity, status, url))
485
            if do_assignee and assignee:
486
                reports[-1] = reports[-1] + (" - Assigned to %s" % assignee)
487
        return reports
1 by Dennis Kaarsemaker
Initial checkin
488
489
# Define all bugtrackers
490
class IBugtracker:
491
    def __init__(self, name=None, url=None, description=None):
492
        self.name        = name
493
        self.url         = url
494
        self.description = description
495
496
    def get_bug(self, id):
497
        raise BugTrackerError("Bugtracker class does not implement get_bug")
498
499
    def get_tracker(self, url):
500
        raise BugTrackerError("Bugtracker class does not implement get_tracker")
501
502
class Bugzilla(IBugtracker):
503
    def get_tracker(self, url):
504
        url = url.replace('show_bug','xml')
505
        try:
506
            bugxml = utils.web.getUrl(url)
507
            tree = minidom.parseString(bugxml)
508
            url  = str(tree.getElementsByTagName('bugzilla')[0].attributes['urlbase'].childNodes[0].data)
509
            if url[-1] == '/':
510
                url = url[:-1]
511
            name = url[url.find('//') + 2:]
512
            if '/' in name:
513
                name = name[:name.find('/')]
514
            desc = name
515
            registerBugtracker(name, url, desc, 'bugzilla')
516
            tracker = Bugzilla(name, url, desc)
517
            return tracker
518
        except:
519
            return None
520
    def get_bug(self, id):
521
        url = "%s/xml.cgi?id=%d" % (self.url,id)
522
        try:
523
            bugxml = utils.web.getUrl(url)
524
            zilladom = minidom.parseString(bugxml)
525
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
526
            s = 'Could not parse XML returned by %s: %s (%s)' % (self.description, e, url)
1 by Dennis Kaarsemaker
Initial checkin
527
            raise BugtrackerError, s
528
        bug_n = zilladom.getElementsByTagName('bug')[0]
529
        if bug_n.hasAttribute('error'):
530
            errtxt = bug_n.getAttribute('error')
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
531
            if errtxt == 'NotFound':
532
                raise BugNotFoundError
1 by Dennis Kaarsemaker
Initial checkin
533
            s = 'Error getting %s bug #%s: %s' % (self.description, id, errtxt)
534
            raise BugtrackerError, s
535
        try:
536
            title = _getnodetxt(bug_n.getElementsByTagName('short_desc')[0])
537
            status = _getnodetxt(bug_n.getElementsByTagName('bug_status')[0])
538
            try:
539
                status += ": " + _getnodetxt(bug_n.getElementsByTagName('resolution')[0])
540
            except:
541
                pass
542
            component = _getnodetxt(bug_n.getElementsByTagName('component')[0])
543
            severity = _getnodetxt(bug_n.getElementsByTagName('bug_severity')[0])
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
544
            assignee = '(unavailable)'
545
            try:
546
                assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0])
547
            except:
548
                pass
1 by Dennis Kaarsemaker
Initial checkin
549
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
550
            s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url)
1 by Dennis Kaarsemaker
Initial checkin
551
            raise BugtrackerError, s
35 by Dennis Kaarsemaker
adkfasdyfb
552
        return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))]
1 by Dennis Kaarsemaker
Initial checkin
553
554
class Issuezilla(IBugtracker):
555
    def get_bug(self, id):
556
        url = "%s/xml.cgi?id=%d" % (self.url,id)
557
        try:
558
            bugxml = utils.web.getUrl(url)
559
            zilladom = minidom.parseString(bugxml)
560
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
561
            s = 'Could not parse XML returned by %s: %s (%s)' % (self.description, e, url)
1 by Dennis Kaarsemaker
Initial checkin
562
            raise BugtrackerError, s
563
        bug_n = zilladom.getElementsByTagName('issue')[0]
564
        if not (bug_n.getAttribute('status_code') == '200'):
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
565
            if bug_n.getAttribute('status_message') == 'NotFound':
566
                raise BugNotFoundError
1 by Dennis Kaarsemaker
Initial checkin
567
            s = 'Error getting %s bug #%s: %s' % (self.description, id, bug_n.getAttribute('status_message'))
568
            raise BugtrackerError, s
569
        try:
570
            title = _getnodetxt(bug_n.getElementsByTagName('short_desc')[0])
571
            status = _getnodetxt(bug_n.getElementsByTagName('issue_status')[0])
572
            try:
573
                status += ": " + _getnodetxt(bug_n.getElementsByTagName('resolution')[0])
574
            except:
575
                pass
576
            component = _getnodetxt(bug_n.getElementsByTagName('component')[0])
577
            severity = _getnodetxt(bug_n.getElementsByTagName('issue_type')[0])
35 by Dennis Kaarsemaker
adkfasdyfb
578
            assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0])
1 by Dennis Kaarsemaker
Initial checkin
579
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
580
            s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url)
1 by Dennis Kaarsemaker
Initial checkin
581
            raise BugtrackerError, s
35 by Dennis Kaarsemaker
adkfasdyfb
582
        return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))]
1 by Dennis Kaarsemaker
Initial checkin
583
51 by Dennis Kaarsemaker
Misc fixes
584
class Launchpad(IBugtracker):
1 by Dennis Kaarsemaker
Initial checkin
585
    def _parse(self, task):
586
        parser = email.FeedParser.FeedParser()
587
        parser.feed(task)
588
        return parser.close()
589
    def _sort(self, task1, task2):
590
        # Status sort: 
591
        try:
51 by Dennis Kaarsemaker
Misc fixes
592
            statuses   = ['Rejected', 'Fix Released', 'Fix Committed', 'Unconfirmed','Needs Info','Confirmed','In Progress']
27 by Dennis Kaarsemaker
Yes, more features!
593
            severities = ['Undecided', 'Wishlist', 'Minor', 'Low', 'Normal', 'Medium', 'Major', 'High', 'Critical']
44 by Dennis Kaarsemaker
Fixy fixy!
594
            if task1['status'] not in statuses and task2['status'] in statuses: return -1
595
            if task1['status'] in statuses and task2['status'] not in statuses: return 1
596
            if task1['importance'] not in severities and task2['importance'] in severities: return -1
597
            if task1['importance'] in severities and task2['importance'] not in severities: return 1
1 by Dennis Kaarsemaker
Initial checkin
598
            if not (task1['status'] == task2['status']):
599
                if statuses.index(task1['status']) < statuses.index(task2['status']):
600
                    return -1
601
                return 1
4 by Dennis Kaarsemaker
Remove spurious prints, fix importance handling for launchpad bugs
602
            if not (task1['importance'] == task2['importance']):
603
                if severities.index(task1['importance']) < severities.index(task2['importance']):
1 by Dennis Kaarsemaker
Initial checkin
604
                    return -1
605
                return 1
606
        except: # Launchpad changed again?
607
            return 0
608
        return 0
609
    def get_bug(self, id):
610
        try:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
611
#            print("%s/bugs/%d/+text" % (self.url,id))
612
            bugdata = utils.web.getUrl("%s/bugs/%d/+text" % (self.url,id))
1 by Dennis Kaarsemaker
Initial checkin
613
        except Exception, e:
614
            if '404' in str(e):
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
615
                raise BugNotFoundError
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
616
            s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id)
1 by Dennis Kaarsemaker
Initial checkin
617
            raise BugtrackerError, s
618
        summary = {}
619
        # Trap private bugs
620
        if "<!-- 4a. didn't try to log in last time: -->" in bugdata:
621
            raise BugtrackerError, "This bug is private"
622
        try:
623
            # Split bug data into separate pieces (bug data, task data)
624
            data     =  bugdata.split('\n\n')
625
            bugdata  = data[0]
626
            taskdata = data[1:]
627
            parser   = email.FeedParser.FeedParser()
628
            parser.feed(bugdata)
629
            bugdata = parser.close()
630
            taskdata = map(self._parse, taskdata)
631
            taskdata.sort(self._sort)
632
            taskdata = taskdata[-1]
633
                
634
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
635
            s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id)
1 by Dennis Kaarsemaker
Initial checkin
636
            raise BugtrackerError, s
35 by Dennis Kaarsemaker
adkfasdyfb
637
        # Try and find duplicates
1 by Dennis Kaarsemaker
Initial checkin
638
        t = taskdata['task']
639
        if '(' in t:
640
            t = t[:t.rfind('(') -1]
35 by Dennis Kaarsemaker
adkfasdyfb
641
        if bugdata['duplicate-of']:
642
            dupbug = self.get_bug(int(bugdata['duplicate-of']))
643
            return [(id, t, bugdata['title'] + (' (dup-of: %d)' % dupbug[0][0]), taskdata['importance'], 
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
644
                    taskdata['status'], taskdata['assignee'], "%s/bugs/%s" % (self.url, id))] + dupbug
35 by Dennis Kaarsemaker
adkfasdyfb
645
        return [(id, t, bugdata['title'], taskdata['importance'], 
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
646
                taskdata['status'], taskdata['assignee'], "%s/bugs/%s" % (self.url, id))]
1 by Dennis Kaarsemaker
Initial checkin
647
            
648
# <rant>
649
# Debbugs sucks donkeyballs
650
# * HTML pages are inconsistent
651
# * Parsing mboxes gets incorrect with cloning perversions (eg with bug 330000)
21 by Dennis Kaarsemaker
* Completely rewritten encyclopedia plugin
652
# * No sane way of accessing bug reports in a machine readable way (bts2ldap
653
#   has no search on bugid)
654
# * The damn thing allow incomplete bugs, eg bugs without severity set. WTF?!?
1 by Dennis Kaarsemaker
Initial checkin
655
#
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
656
# Fortunately bugs.donarmstrong.com has a SOAP interface which we can use.
1 by Dennis Kaarsemaker
Initial checkin
657
# </rant>
658
class Debbugs(IBugtracker):
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
659
    def __init__(self, *args, **kwargs):
660
        IBugtracker.__init__(self, *args, **kwargs)
66 by Dennis Kaarsemaker
Misc fixes
661
        self.soap_proxy = SOAPpy.SOAPProxy("bugs.debian.org/cgi-bin/soap.cgi", "Debbugs/SOAP/Status")
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
662
        self.soap_proxy.soapaction = "Debbugs/SOAP/Status#get_status"
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
663
1 by Dennis Kaarsemaker
Initial checkin
664
    def get_bug(self, id):
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
665
        bug_url = "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%d" % id
1 by Dennis Kaarsemaker
Initial checkin
666
        try:
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
667
            raw = self.soap_proxy.get_status(id)
1 by Dennis Kaarsemaker
Initial checkin
668
        except Exception, e:
669
            s = 'Could not parse data returned by %s: %s' % (self.description, e)
670
            raise BugtrackerError, s
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
671
        if not raw:
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
672
            raise BugNotFoundError
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
673
        raw = raw['item']['value']
1 by Dennis Kaarsemaker
Initial checkin
674
        try:
53 by Dennis Kaarsemaker
Use the SOAP interface at bugs.donarmstrong.com for debbugs
675
            if len(raw['fixed_versions']):
676
                status = 'Fixed'
677
            else:
678
                status = 'Open'
679
            return [(id, raw['package'], raw['subject'], raw['severity'], status, '', "%s/%s" % (self.url, id))]
1 by Dennis Kaarsemaker
Initial checkin
680
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
681
            s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, bug_url)
1 by Dennis Kaarsemaker
Initial checkin
682
            raise BugtrackerError, s
683
95 by Terence Simpson
Big-Old-Update Edition
684
class Mantis(IBugtracker):
685
    def __init__(self, *args, **kwargs):
686
        IBugtracker.__init__(self, *args, **kwargs)
687
        self.soap_proxy = SOAPpy.SOAPProxy(self.url + "/api/soap/mantisconnect.php", "http://futureware.biz/mantisconnect")
688
        self.soap_proxy.soapaction = "http://futureware.biz/mantisconnect#mc_issue_get"
689
690
    def get_bug(self, id):
691
        url = self.url + "/view.php?id=%i" % id
692
        try:
693
            raw = self.soap_proxy.mc_issue_get('', "", id)
694
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
695
            s = 'Could not parse data returned by %s: %s (%s)' % (self.description, e, url)
95 by Terence Simpson
Big-Old-Update Edition
696
            raise BugtrackerError, s
697
        if not raw:
698
            raise BugNotFoundError
699
        try:
700
            return [(id, raw['project']['name'], raw['summary'], raw['priority']['name'], raw['resolution']['name'], '', url)]
701
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
702
            s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, url)
95 by Terence Simpson
Big-Old-Update Edition
703
            raise BugtrackerError, s
704
132 by Terence Simpson
Bugtracker/plugin.py: Update Trac class to use the "Tab-delimited Text" source rather than screen-scraping
705
# For trac based trackers we get the tab-separated-values format.
706
# The other option is a comma-separated-values format, but if the description
707
# has commas, things get tricky.
708
# This should be more robust than the screen-scraping done previously.
1 by Dennis Kaarsemaker
Initial checkin
709
class Trac(IBugtracker):
132 by Terence Simpson
Bugtracker/plugin.py: Update Trac class to use the "Tab-delimited Text" source rather than screen-scraping
710
    def get_bug(self, id): # This is still a little rough, but it works :)
711
        bug_url = "%s/%d" % (self.url, id)
1 by Dennis Kaarsemaker
Initial checkin
712
        try:
132 by Terence Simpson
Bugtracker/plugin.py: Update Trac class to use the "Tab-delimited Text" source rather than screen-scraping
713
            raw = utils.web.getUrl("%s?format=tab" % bug_url)
1 by Dennis Kaarsemaker
Initial checkin
714
        except Exception, e:
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
715
            if 'HTTP Error 500' in str(e):
716
                raise BugNotFoundError
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
717
            s = 'Could not parse data returned by %s: %s' % (self.description, e, bug_url)
1 by Dennis Kaarsemaker
Initial checkin
718
            raise BugtrackerError, s
132 by Terence Simpson
Bugtracker/plugin.py: Update Trac class to use the "Tab-delimited Text" source rather than screen-scraping
719
        raw = raw.replace("\r\n", '\n')
720
        (headers, rest) = raw.split('\n', 1)
721
        headers = headers.strip().split('\t')
722
        rest = rest.strip().split('\t')
131 by Terence Simpson
Bugtracker/plugin.py: Temporary fix for Trac
723
        title = status = package = severity = assignee = "Unknown"
132 by Terence Simpson
Bugtracker/plugin.py: Update Trac class to use the "Tab-delimited Text" source rather than screen-scraping
724
        if "summary" in headers:
725
            title = rest[headers.index("summary")]
726
        if "status" in headers:
727
            status = rest[headers.index("status")]
728
        if "component" in headers:
729
            package = rest[headers.index("component")]
730
        if "severity" in headers:
731
            severity = rest[headers.index("severity")]
732
        if "owner" in headers:
733
            assingee = rest[headers.index("owner")]
734
        if severity == "Unknown" and "priority" in headers:
735
            severity = rest[headers.index("priority")]
736
737
        return [(id, package, title, severity, status, assignee, bug_url)]
31 by Dennis Kaarsemaker
This project is now officially the one with the worst commitlog :)
738
        
739
class WikiForms(IBugtracker):
740
    def get_bug(self, id):
741
        def strip_tags(s):
742
            while '<' in s and '>' in s:
743
                s = str(s[:s.find('<')]) + str(s[s.find('>')+1:])
744
            return s
745
746
        url = "%s/%05d" % (self.url, id)
747
        try:
748
            bugdata = utils.web.getUrl(url)
749
        except Exception, e:
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
750
            if 'HTTP Error 404' in str(e):
751
                raise BugNotFoundError
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
752
            s = 'Could not parse data returned by %s: %s (%s)' % (self.description, e, url)
31 by Dennis Kaarsemaker
This project is now officially the one with the worst commitlog :)
753
            raise BugtrackerError, s
754
        for l in bugdata.split("\n"):
755
            l2 = l.lower()
756
            if '<dt>importance</dt>' in l2:
757
                severity = 'Importance ' + strip_tags(l[l.find('<dd>')+4:])
758
            if '<dt>summary</dt>' in l2:
759
                title = strip_tags(l[l.find('<dd>')+4:])
760
            if '<dt>status</dt>' in l2:
761
                status = strip_tags(l[l.find('<dd>')+4:])
762
            if '<dt>category</dt>' in l2:
763
                package = strip_tags(l[l.find('<dd>')+4:])
35 by Dennis Kaarsemaker
adkfasdyfb
764
        return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id))]
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
765
766
class Str(IBugtracker):
767
    def get_bug(self, id):
768
        def strip_tags(s):
769
            while '<' in s and '>' in s:
770
                s = str(s[:s.find('<')]) + str(s[s.find('>')+1:])
771
            return s
772
        url = "%s?L%d" % (self.url, id)
773
        try:
774
            bugdata = utils.web.getUrl(url)
775
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
776
            s = 'Could not parse data returned by %s: %s (%s)' % (self.description, e, url)
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
777
            raise BugtrackerError, s
778
        for l in bugdata.split("\n"):
779
            l2 = l.lower()
780
            if 'nowrap>priority:</th>' in l2:
781
                severity = 'Priority ' + l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))]
782
            if '>application:</th>' in l2:
783
                package = l[l.find('<td>')+4:l.find('</td>')]
784
            if 'nowrap>status:</th>' in l2:
785
                status = l[l.find(' - ')+3:l.find('</td>')]
786
            if 'nowrap>summary:</th>' in l2:
787
                title = l[l.find('<td>')+4:l.find('</td>')]
788
            if 'nowrap>assigned to:</th>' in l2:
789
                assignee = strip_tags(l[l.find('<td>')+4:l.find('</td>')])
790
                if assignee == 'Unassigned':
791
                    assignee = 'nobody'
42 by Dennis Kaarsemaker
Encyclopedia made neat.
792
        return [(id, package, title, severity, status, assignee, "%s?L%d" % (self.url, id))]
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
793
        
1 by Dennis Kaarsemaker
Initial checkin
794
795
sfre = re.compile(r"""
796
                  .*?
797
                  <h2>\[.*?\]\s*(?P<title>.*?)</h2>
35 by Dennis Kaarsemaker
adkfasdyfb
798
                  .*?
43 by Dennis Kaarsemaker
More fixes everywhere
799
                  assigned.*?<br>\s+(?P<assignee>\S+)
1 by Dennis Kaarsemaker
Initial checkin
800
                  .*?
43 by Dennis Kaarsemaker
More fixes everywhere
801
                  priority.*?(?P<priority>\d+)
1 by Dennis Kaarsemaker
Initial checkin
802
                  .*?
43 by Dennis Kaarsemaker
More fixes everywhere
803
                  status.*?<br>\s+(?P<status>\S+)
1 by Dennis Kaarsemaker
Initial checkin
804
                  .*?
43 by Dennis Kaarsemaker
More fixes everywhere
805
                  resolution.*?<br>\s+(?P<resolution>\S+)
1 by Dennis Kaarsemaker
Initial checkin
806
                  .*?
807
                  """, re.VERBOSE | re.DOTALL | re.I)
808
class Sourceforge(IBugtracker):
809
    _sf_url = 'http://sf.net/support/tracker.php?aid=%d'
810
    def get_bug(self, id):
811
        url = self._sf_url % id
812
        try:
813
            bugdata = utils.web.getUrl(url)
814
        except Exception, e:
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
815
            s = 'Could not parse data returned by %s: %s (%s)' % (self.description, e, url)
1 by Dennis Kaarsemaker
Initial checkin
816
            raise BugtrackerError, s
817
        try:
818
            reo = sfre.search(bugdata)
819
            status = reo.group('status')
820
            resolution = reo.group('resolution')
821
            if not (resolution.lower() == 'none'):
822
                status += ' ' + resolution
35 by Dennis Kaarsemaker
adkfasdyfb
823
            return [(id, None, reo.group('title'), "Pri: %s" % reo.group('priority'), status, reo.group('assignee'),self._sf_url % id)]
1 by Dennis Kaarsemaker
Initial checkin
824
        except:
40 by Dennis Kaarsemaker
Made bugtracker plugin more usable
825
            raise BugNotFoundError
1 by Dennis Kaarsemaker
Initial checkin
826
827
# Introspection is quite cool
828
defined_bugtrackers = {}
829
v = vars()
830
for k in v.keys():
831
    if type(v[k]) == type(IBugtracker) and issubclass(v[k], IBugtracker) and not (v[k] == IBugtracker):
832
        defined_bugtrackers[k.lower()] = v[k]
833
834
registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla')
133 by Terence Simpson
Resync all running ubottu code to bzr branch, they should now be in sync again
835
#registerBugtracker('ubuntu', 'http://bugzilla.ubuntu.com', 'Ubuntu', 'bugzilla')
93 by Terence Simpson
Make sure 'ubuntu ###' looks at launchpad for the bug, not bugzilla.ubuntu.com
836
registerBugtracker('ubuntu', 'https://launchpad.net', 'Ubuntu', 'launchpad')
1 by Dennis Kaarsemaker
Initial checkin
837
registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', 'bugzilla')
838
registerBugtracker('gnome2', 'http://bugs.gnome.org', 'Gnome', 'bugzilla')
839
registerBugtracker('kde', 'http://bugs.kde.org', 'KDE', 'bugzilla')
840
registerBugtracker('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla')
841
registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla')
842
registerBugtracker('freedesktop2', 'http://bugs.freedesktop.org', 'Freedesktop', 'bugzilla')
843
registerBugtracker('openoffice', 'http://openoffice.org/issues', 'OpenOffice.org', 'issuezilla')
51 by Dennis Kaarsemaker
Misc fixes
844
registerBugtracker('launchpad', 'https://launchpad.net', 'Launchpad', 'launchpad')
845
registerBugtracker('lp', 'https://launchpad.net', 'Launchpad', 'launchpad')
846
registerBugtracker('malone', 'https://launchpad.net', 'Launchpad', 'launchpad')
1 by Dennis Kaarsemaker
Initial checkin
847
registerBugtracker('debian', 'http://bugs.debian.org', 'Debian', 'debbugs')
43 by Dennis Kaarsemaker
More fixes everywhere
848
registerBugtracker('trac', 'http://trac.edgewall.org/ticket', 'Trac', 'trac')
1 by Dennis Kaarsemaker
Initial checkin
849
registerBugtracker('django', 'http://code.djangoproject.com/ticket', 'Django', 'trac')
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
850
registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str')
851
registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms')
1 by Dennis Kaarsemaker
Initial checkin
852
registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge')
95 by Terence Simpson
Big-Old-Update Edition
853
registerBugtracker('mantis', "http://www.mantisbt.org/bugs", "Mantis", 'mantis')
41 by Dennis Kaarsemaker
Bugtracker now is much more usable and contains documentation. Backwards
854
# Don't delete this one
1 by Dennis Kaarsemaker
Initial checkin
855
registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge')
856
Class = Bugtracker