Example #1
0
 def verifyInput(self, parms, pmap):
     '''Check that the given input is valid.'''
     errmsg = UserInterface.UserInterface.verifyInput(self, parms, pmap)
     if pmap != parm_ini_map:
         return errmsg
     slist = list(parms['pop3proxy_remote_servers'])
     plist = list(parms['pop3proxy_listen_ports'])
     if len(slist) != len(plist):
         errmsg += _('<li>The number of POP3 proxy ports specified ' \
                     'must match the number of servers specified</li>\n')
     plist.sort()
     for p in range(len(plist)-1):
         try:
             if plist[p] == plist[p+1]:
                 errmsg += _('<li>All POP3 port numbers must be unique</li>')
                 break
         except IndexError:
             pass
     slist = list(parms['smtpproxy_remote_servers'])
     plist = list(parms['smtpproxy_listen_ports'])
     if len(slist) != len(plist):
         errmsg += _('<li>The number of SMTP proxy ports specified ' \
                     'must match the number of servers specified</li>\n')
     plist.sort()
     for p in range(len(plist)-1):
         try:
             if plist[p] == plist[p+1]:
                 errmsg += _('<li>All SMTP port numbers must be unique</li>')
                 break
         except IndexError:
             pass
     return errmsg
Example #2
0
 def onShowclues(self, key, subject, tokens='0'):
     """Show clues for a message - linked from the Review page."""
     tokens = bool(int(tokens)) # needs the int, as bool('0') is True
     self._writePreamble(_("Message clues"),
                         parent=('review', _('Review')))
     sourceCorpus = None
     message = None
     if self.state.unknownCorpus.get(key) is not None:
         sourceCorpus = self.state.unknownCorpus
     elif self.state.hamCorpus.get(key) is not None:
         sourceCorpus = self.state.hamCorpus
     elif self.state.spamCorpus.get(key) is not None:
         sourceCorpus = self.state.spamCorpus
     if sourceCorpus is not None:
         message = sourceCorpus.get(key).as_string()
     if message is not None:
         # For Macs?
         message = message.replace('\r\n', '\n').replace('\r', '\n')
         results = self._buildCluesTable(message, subject, tokens)
         del results.classifyAnother
         self.write(results)
     else:
         self.write(_("<p>Can't find message %r. Maybe it expired.</p>") %
                    key)
     self._writePostamble()
Example #3
0
 def onHome(self):
     """Serve up the homepage."""
     stateDict = self.classifier.__dict__.copy()
     stateDict["warning"] = ""
     stateDict.update(self.classifier.__dict__)
     statusTable = self.html.statusTable.clone()
     del statusTable.proxyDetails
     statusTable.configurationLink += "<br />&nbsp;&nbsp;&nbsp;&nbsp;" \
         "&nbsp;" + _("You can also <a href='filterfolders'>configure" \
                      " folders to filter</a><br />and " \
                      "<a href='trainingfolders'>Configure folders to" \
                      " train</a>")
     findBox = self._buildBox(_('Word query'), 'query.gif',
                              self.html.wordQuery)
     if not options["html_ui", "display_adv_find"]:
         del findBox.advanced
     content = (self._buildBox(_('Status and Configuration'),
                               'status.gif', statusTable % stateDict)+
                self._buildTrainBox() +
                self._buildClassifyBox() +
                findBox
                )
     self._writePreamble(_("Home"))
     self.write(content)
     self._writePostamble()
Example #4
0
 def onFilterfolders(self):
     self._writePreamble(_("Select Filter Folders"))
     self._login_to_imap()
     available_folders = []
     for imap in self.imaps:
         if imap and imap.logged_in:
             available_folders.extend(imap.folder_list())
     if not available_folders:
         content = self._buildBox(_("Error"), None,
                                  _("No folders available"))
         self.write(content)
         self._writePostamble()
         return
     content = self.html.configForm.clone()
     content.configFormContent = ""
     content.introduction = _("This page allows you to change " \
                              "which folders are filtered, and " \
                              "where filtered mail ends up.")
     content.config_submit.value = _("Save Filter Folders")
     content.optionsPathname = optionsPathname
     for opt in ("unsure_folder", "spam_folder",
                 "filter_folders"):
         folderBox = self._buildFolderBox("imap", opt, available_folders)
         content.configFormContent += folderBox
     self.write(content)
     self._writePostamble()
Example #5
0
 def onTrainingfolders(self):
     self._writePreamble(_("Select Training Folders"))
     self._login_to_imap()
     available_folders = []
     for imap in self.imaps:
         if imap and imap.logged_in:
             available_folders.extend(imap.folder_list())
     if not available_folders:
         content = self._buildBox(_("Error"), None,
                                  _("No folders available"))
         self.write(content)
         self._writePostamble()
         return
     content = self.html.configForm.clone()
     content.configFormContent = ""
     content.introduction = _("This page allows you to change " \
                              "which folders contain mail to " \
                              "train Spambayes.")
     content.config_submit.value = _("Save Training Folders")
     content.optionsPathname = optionsPathname
     for opt in ("ham_train_folders",
                 "spam_train_folders"):
         folderBox = self._buildFolderBox("imap", opt, available_folders)
         content.configFormContent += folderBox
     self.write(content)
     self._writePostamble()
Example #6
0
 def onChangeopts(self, **parms):

        backup = self.parm_ini_map

        if parms["how"] == _("Save Training Folders") or \
           parms["how"] == _("Save Filter Folders"):

            del parms["how"]

            self.parm_ini_map = ()

            for opt, value in parms.items():

                del parms[opt]

                if opt[-len(value):] == value:

                    opt = opt[:-len(value)]

                self.parm_ini_map += ("imap", opt),

                key = "imap_" + opt

                if parms.has_key(key):

                    parms[key] += ',' + value

                else:

                    parms[key] = value

        UserInterface.UserInterface.onChangeopts(self, **parms)

        self.parm_ini_map = backup
Example #7
0
 def onHome(self):
     """Serve up the homepage."""
     stateDict = self.classifier.__dict__.copy()
     stateDict["warning"] = ""
     stateDict.update(self.classifier.__dict__)
     statusTable = self.html.statusTable.clone()
     del statusTable.proxyDetails
     # This could be a bit more modular
     statusTable.configurationLink += "<br />&nbsp;&nbsp;&nbsp;&nbsp;" \
         "&nbsp;" + _("You can also <a href='filterfolders'>configure" \
                      " folders to filter</a><br />and " \
                      "<a href='trainingfolders'>Configure folders to" \
                      " train</a>")
     findBox = self._buildBox(_('Word query'), 'query.gif',
                              self.html.wordQuery)
     if not options["html_ui", "display_adv_find"]:
         del findBox.advanced
     content = (self._buildBox(_('Status and Configuration'),
                               'status.gif', statusTable % stateDict)+
                self._buildTrainBox() +
                self._buildClassifyBox() +
                findBox
                )
     self._writePreamble(_("Home"))
     self.write(content)
     self._writePostamble()
Example #8
0
    def onTrainingfolders(self):
        self._writePreamble(_("Select Training Folders"))
        self._login_to_imap()
        available_folders = []
        for imap in self.imaps:
            if imap and imap.logged_in:
                available_folders.extend(imap.folder_list())

        if not available_folders:
            content = self._buildBox(_("Error"), None,
                                     _("No folders available"))
            self.write(content)
            self._writePostamble()
            return

        content = self.html.configForm.clone()
        content.configFormContent = ""
        content.introduction = _("This page allows you to change " \
                                 "which folders contain mail to " \
                                 "train Spambayes.")
        content.config_submit.value = _("Save Training Folders")
        content.optionsPathname = optionsPathname

        for opt in ("ham_train_folders",
                    "spam_train_folders"):
            folderBox = self._buildFolderBox("imap", opt, available_folders)
            content.configFormContent += folderBox

        self.write(content)
        self._writePostamble()
Example #9
0
    def onFilterfolders(self):
        self._writePreamble(_("Select Filter Folders"))
        self._login_to_imap()
        available_folders = []
        for imap in self.imaps:
            if imap and imap.logged_in:
                available_folders.extend(imap.folder_list())

        if not available_folders:
            content = self._buildBox(_("Error"), None,
                                     _("No folders available"))
            self.write(content)
            self._writePostamble()
            return

        content = self.html.configForm.clone()
        content.configFormContent = ""
        content.introduction = _("This page allows you to change " \
                                 "which folders are filtered, and " \
                                 "where filtered mail ends up.")
        content.config_submit.value = _("Save Filter Folders")
        content.optionsPathname = optionsPathname

        for opt in ("unsure_folder", "spam_folder",
                    "filter_folders"):
            folderBox = self._buildFolderBox("imap", opt, available_folders)
            content.configFormContent += folderBox

        self.write(content)
        self._writePostamble()
Example #10
0
 def _appendMessages(self, table, keyedMessageInfo, label, sort_order, reverse=False):
     """Appends the rows of a table of messages to 'table'."""
     stripe = 0
     keyedMessageInfo = self._sortMessages(keyedMessageInfo, sort_order, reverse)
     nrows = options["html_ui", "rows_per_section"]
     for key, messageInfo in keyedMessageInfo[:nrows]:
         unused, unused, messageInfo.received = self._getTimeRange(self._keyToTimestamp(key))
         row = self.html.reviewRow.clone()
         try:
             score = messageInfo.score
         except ValueError:
             score = None
         if label == _("Spam"):
             if score is not None and score > options["html_ui", "spam_discard_level"]:
                 r_att = getattr(row, "discard")
             else:
                 r_att = getattr(row, options["html_ui", "default_spam_action"])
         elif label == _("Ham"):
             if score is not None and score < options["html_ui", "ham_discard_level"]:
                 r_att = getattr(row, "discard")
             else:
                 r_att = getattr(row, options["html_ui", "default_ham_action"])
         else:
             r_att = getattr(row, options["html_ui", "default_unsure_action"])
         setattr(r_att, "checked", 1)
         row.optionalHeadersValues = ""  # make way for real list
         for header in options["html_ui", "display_headers"]:
             header = header.lower()
             text = getattr(messageInfo, "%sHeader" % (header,))
             if header == "subject":
                 h = self.html.reviewRow.linkedHeaderValue.clone()
                 h.text.title = messageInfo.bodySummary
                 h.text.href = "view?key=%s&corpus=%s" % (key, label)
             else:
                 h = self.html.reviewRow.headerValue.clone()
             h.text = text
             row.optionalHeadersValues += h
         if options["html_ui", "display_score"]:
             if isinstance(messageInfo.score, types.StringTypes):
                 row.score_ = messageInfo.score
             else:
                 row.score_ = "%.2f%%" % (messageInfo.score,)
         else:
             del row.score_
         if options["html_ui", "display_received_time"]:
             row.received_ = messageInfo.received
         else:
             del row.received_
         subj_list = []
         for c in messageInfo.subjectHeader:
             subj_list.append("%%%s" % (hex(ord(c))[2:],))
         subj = "".join(subj_list)
         row.classify.href = "showclues?key=%s&subject=%s" % (key, subj)
         row.tokens.href = "showclues?key=%s&subject=%s&tokens=1" % (key, subj)
         setattr(row, "class", ["stripe_on", "stripe_off"][stripe])  # Grr!
         setattr(row, "onMouseOut", ["this.className='stripe_on';", "this.className='stripe_off';"][stripe])
         row = str(row).replace("TYPE", label).replace("KEY", key)
         table += row
         stripe = stripe ^ 1
Example #11
0
    def onTrain(self, file, text, which):
        """Train on an uploaded or pasted message."""
        self._writePreamble(_("Train"))
        content = file or text
        isSpam = which == _("Train as Spam")
        if file:
            content = self._convertToMbox(content)
        content = content.replace("\r\n", "\n").replace("\r", "\n")
        messages = self._convertUploadToMessageList(content)
        if isSpam:
            desired_corpus = "spamCorpus"
        else:
            desired_corpus = "hamCorpus"
        if hasattr(self, desired_corpus):
            corpus = getattr(self, desired_corpus)
        else:
            if hasattr(self, "state"):
                corpus = getattr(self.state, desired_corpus)
                setattr(self, desired_corpus, corpus)
                self.msg_name_func = self.state.getNewMessageName
            else:
                if isSpam:
                    fn = storage.get_pathname_option("Storage", "spam_cache")
                else:
                    fn = storage.get_pathname_option("Storage", "ham_cache")
                storage.ensureDir(fn)
                if options["Storage", "cache_use_gzip"]:
                    factory = FileCorpus.GzipFileMessageFactory()
                else:
                    factory = FileCorpus.FileMessageFactory()
                age = options["Storage", "cache_expiry_days"] * 24 * 60 * 60
                corpus = FileCorpus.ExpiryFileCorpus(age, factory, fn, "[0123456789\-]*", cacheSize=20)
                setattr(self, desired_corpus, corpus)

                class UniqueNamer(object):
                    count = -1

                    def generate_name(self):
                        self.count += 1
                        return "%10.10d-%d" % (long(time.time()), self.count)

                Namer = UniqueNamer()
                self.msg_name_func = Namer.generate_name
        self.write("<b>" + _("Training") + "...</b>\n")
        self.flush()
        for message in messages:
            key = self.msg_name_func()
            msg = corpus.makeMessage(key, message)
            msg.setId(key)
            corpus.addMessage(msg)
            msg.RememberTrained(isSpam)
            self.stats.RecordTraining(not isSpam)
        self._doSave()
        self.write(_("%sOK. Return %sHome%s or train again:%s") % ("<p>", "<a href='home'>", "</a", "</p>"))
        self.write(self._buildTrainBox())
        self._writePostamble()
Example #12
0
 def onStats(self):
     """Provide statistics about previous SpamBayes activity."""
     self._writePreamble(_("Statistics"))
     if self.stats:
         stats = self.stats.GetStats(use_html=True)
         stats = self._buildBox(_("Statistics"), None, "<br/><br/>".join(stats))
     else:
         stats = self._buildBox(_("Statistics"), None, _("Statistics not available"))
     self.write(stats)
     self._writePostamble(help_topic="stats")
Example #13
0
 def _verifyEnteredDetails(self, from_addr, subject, message):
     """Ensure that the user didn't just send the form message, and
     at least changed the fields."""
     if from_addr.startswith(_("[YOUR EMAIL ADDRESS]")):
         return False
     if message.endswith(_("[DESCRIBE YOUR PROBLEM HERE]")):
         return False
     if subject.endswith(_("[PROBLEM SUMMARY]")):
         return False
     return True
Example #14
0
 def onAdvancedconfig(self):
     html = self._buildConfigPage(self.advanced_options_map)
     html.title = _("Home &gt; Advanced Configuration")
     html.pagename = _("&gt; Advanced Configuration")
     html.adv_button.name.value = _("Back to basic configuration")
     html.adv_button.action = "config"
     html.config_submit.value = _("Save advanced options")
     html.restore.value = _("Restore advanced options defaults")
     del html.exp_button
     self.writeOKHeaders("text/html")
     self.write(html)
Example #15
0
 def onExperimentalconfig(self):
     html = self._buildConfigPage(experimental_ini_map)
     html.title = _("Home &gt; Experimental Configuration")
     html.pagename = _("&gt; Experimental Configuration")
     html.adv_button.name.value = _("Back to basic configuration")
     html.adv_button.action = "config"
     html.config_submit.value = _("Save experimental options")
     html.restore.value = _("Restore experimental options defaults (all off)")
     del html.exp_button
     self.writeOKHeaders("text/html")
     self.write(html)
Example #16
0
    def _makeMessageInfo(self, message):
        """Given an email.Message, return an object with subjectHeader,
        bodySummary and other header (as needed) attributes.  These objects
        are passed into appendMessages by onReview - passing email.Message
        objects directly uses too much memory.
        """
        message.delNotations()
        subjectHeader = message["Subject"] or "(none)"
        headers = {"subject": subjectHeader}
        for header in options["html_ui", "display_headers"]:
            headers[header.lower()] = message[header] or "(none)"
        score = message[options["Headers", "score_header_name"]]
        if score:
            op = score.find("(")
            if op >= 0:
                score = score[:op]
            try:
                score = float(score) * 100
            except ValueError:
                score = "Err"  # Let the user know something is wrong.
        else:
            score = "?"
        try:
            part = typed_subpart_iterator(message, "text", "plain").next()
            text = part.get_payload()
        except StopIteration:
            try:
                part = typed_subpart_iterator(message, "text", "html").next()
                text = part.get_payload()
                text, unused = tokenizer.crack_html_style(text)
                text, unused = tokenizer.crack_html_comment(text)
                text = tokenizer.html_re.sub(" ", text)
                text = _("(this message only has an HTML body)\n") + text
            except StopIteration:
                text = _("(this message has no text body)")
        if type(text) == type([]):  # gotta be a 'right' way to do this
            text = _("(this message is a digest of %s messages)") % (len(text))
        elif text is None:
            text = _("(this message has no body)")
        else:
            text = text.replace("&nbsp;", " ")  # Else they'll be quoted
            text = re.sub(r"(\s)\s+", r"\1", text)  # Eg. multiple blank lines
            text = text.strip()

        class _MessageInfo:
            pass

        messageInfo = _MessageInfo()
        for headerName, headerValue in headers.items():
            headerValue = self._trimHeader(headerValue, 45, True)
            setattr(messageInfo, "%sHeader" % (headerName,), headerValue)
        messageInfo.score = score
        messageInfo.bodySummary = self._trimHeader(text, 200)
        return messageInfo
Example #17
0
 def onPluginconfig(self):
     html = self._buildConfigPage(self.plugin.plugin_map)
     html.title = _('Home &gt; Plugin Configuration')
     html.pagename = _('&gt; Plugin Configuration')
     html.plugin_button.name.value = _("Back to basic configuration")
     html.plugin_button.action = "config"
     html.config_submit.value = _("Save plugin options")
     html.restore.value = _("Restore plugin options defaults")
     del html.exp_button
     del html.adv_button
     self.writeOKHeaders('text/html')
     self.write(html)
Example #18
0
 def onPluginconfig(self):
     html = self._buildConfigPage(self.plugin.plugin_map)
     html.title = _('Home &gt; Plugin Configuration')
     html.pagename = _('&gt; Plugin Configuration')
     html.plugin_button.name.value = _("Back to basic configuration")
     html.plugin_button.action = "config"
     html.config_submit.value = _("Save plugin options")
     html.restore.value = _("Restore plugin options defaults")
     del html.exp_button
     del html.adv_button
     self.writeOKHeaders('text/html')
     self.write(html)
Example #19
0
 def verifyInput(self, parms, pmap):
     """Check that the given input is valid."""
     errmsg = ""
     for name, value in parms.items():
         if name[-2:-1] == "-":
             if parms.has_key(name[:-2]):
                 parms[name[:-2]] += (value,)
             else:
                 parms[name[:-2]] = (value,)
             del parms[name]
     for sect, opt in pmap:
         if opt is None:
             nice_section_name = sect
             continue
         if sect == "Headers" and opt in ("notate_to", "notate_subject"):
             valid_input = (
                 options["Headers", "header_ham_string"],
                 options["Headers", "header_spam_string"],
                 options["Headers", "header_unsure_string"],
             )
         else:
             valid_input = options.valid_input(sect, opt)
         html_key = sect + "_" + opt
         if not parms.has_key(html_key):
             value = ()
             entered_value = "None"
         else:
             value = parms[html_key]
             entered_value = value
             if options.is_boolean(sect, opt):
                 if value == _("No"):
                     value = False
                 elif value == _("Yes"):
                     value = True
             if options.multiple_values_allowed(sect, opt) and value == "":
                 value = ()
             value = options.convert(sect, opt, value)
         if not options.is_valid(sect, opt, value):
             errmsg += _("<li>'%s' is not a value valid for [%s] %s") % (
                 entered_value,
                 nice_section_name,
                 options.display_name(sect, opt),
             )
             if isinstance(valid_input, types.TupleType):
                 errmsg += _(". Valid values are: ")
                 for valid in valid_input:
                     errmsg += str(valid) + ","
                 errmsg = errmsg[:-1]  # cut last ','
             errmsg += "</li>"
         parms[html_key] = value
     return errmsg
Example #20
0
class XMLRPCUI(PluginUI):
    plugin_map = (
        (_('XML-RPC Options'), None),
        ('Plugin', 'xmlrpc_path'),
        ('Plugin', 'xmlrpc_host'),
        ('Plugin', 'xmlrpc_port'),
    )
Example #21
0
    def verifyInput(self, parms, pmap):
        '''Check that the given input is valid.'''
        # Most of the work here is done by the parent class, but
        # we have a few extra checks
        errmsg = UserInterface.UserInterface.verifyInput(self, parms, pmap)

        if pmap != parm_ini_map:
            return errmsg

        # check for equal number of pop3servers and ports
        slist = list(parms['pop3proxy_remote_servers'])
        plist = list(parms['pop3proxy_listen_ports'])
        if len(slist) != len(plist):
            errmsg += _('<li>The number of POP3 proxy ports specified ' \
                        'must match the number of servers specified</li>\n')

        # check for duplicate ports
        plist.sort()
        for p in range(len(plist) - 1):
            try:
                if plist[p] == plist[p + 1]:
                    errmsg += _(
                        '<li>All POP3 port numbers must be unique</li>')
                    break
            except IndexError:
                pass

        # check for equal number of smtpservers and ports
        slist = list(parms['smtpproxy_remote_servers'])
        plist = list(parms['smtpproxy_listen_ports'])
        if len(slist) != len(plist):
            errmsg += _('<li>The number of SMTP proxy ports specified ' \
                        'must match the number of servers specified</li>\n')

        # check for duplicate ports
        plist.sort()
        for p in range(len(plist) - 1):
            try:
                if plist[p] == plist[p + 1]:
                    errmsg += _(
                        '<li>All SMTP port numbers must be unique</li>')
                    break
            except IndexError:
                pass

        return errmsg
Example #22
0
 def onView(self, key, corpus):
     """View a message - linked from the Review page."""
     self._writePreamble(_("View message"), parent=('review', _('Review')))
     sourceCorpus = None
     message = None
     if state.unknownCorpus.get(key) is not None:
         sourceCorpus = state.unknownCorpus
     elif state.hamCorpus.get(key) is not None:
         sourceCorpus = state.hamCorpus
     elif state.spamCorpus.get(key) is not None:
         sourceCorpus = state.spamCorpus
     if sourceCorpus is not None:
         message = sourceCorpus.get(key)
     if message is not None:
         self.write("<pre>%s</pre>" % cgi.escape(message.as_string()))
     else:
         self.write(
             _("<p>Can't find message %r. Maybe it expired.</p>") % key)
     self._writePostamble()
Example #23
0
 def onHome(self):
     """Serve up the homepage."""
     self.state.buildStatusStrings()
     stateDict = self.state.__dict__.copy()
     stateDict.update(self.state.bayes.__dict__)
     statusTable = self.html.statusTable.clone()
     findBox = self._buildBox(_('Word query'), 'query.gif',
                              self.html.wordQuery)
     if not options["html_ui", "display_adv_find"]:
         del findBox.advanced
     content = (self._buildBox(_('Status and Configuration'), 'status.gif',
                               statusTable % stateDict) +
                self._buildBox(_('Train on proxied messages'), 'train.gif',
                               self.html.reviewText) +
                self._buildTrainBox() +
                self._buildClassifyBox() + findBox + self._buildBox(
                    _('Find message'), 'query.gif', self.html.findMessage))
     self._writePreamble(_("Home"))
     self.write(content)
     self._writePostamble(help_topic="home_proxy")
Example #24
0
 def onChangeopts(self, **parms):
     backup = self.parm_ini_map
     if parms["how"] == _("Save Training Folders") or \
        parms["how"] == _("Save Filter Folders"):
         del parms["how"]
         self.parm_ini_map = ()
         for opt, value in parms.items():
             del parms[opt]
             # Under strange circumstances this could break,
             # so if we can think of a better way to do this,
             # that would be nice.
             if opt[-len(value):] == value:
                 opt = opt[:-len(value)]
             self.parm_ini_map += ("imap", opt),
             key = "imap_" + opt
             if parms.has_key(key):
                 parms[key] += ',' + value
             else:
                 parms[key] = value
     UserInterface.UserInterface.onChangeopts(self, **parms)
     self.parm_ini_map = backup
Example #25
0
    def onUpload(self, file):
        """Save a message for later training - used by Skip's proxytee.py."""
        # Convert platform-specific line endings into unix-style.
        file = file.replace('\r\n', '\n').replace('\r', '\n')

        # Get a message list from the upload and write it into the cache.
        messages = self._convertUploadToMessageList(file)
        for m in messages:
            messageName = state.getNewMessageName()
            message = state.unknownCorpus.makeMessage(messageName, m)
            state.unknownCorpus.addMessage(message)

        # Return a link Home.
        self.write(_("<p>OK. Return <a href='home'>Home</a>.</p>"))
Example #26
0
 def onShowclues(self, key, subject, tokens='0'):
     """Show clues for a message - linked from the Review page."""
     tokens = bool(int(tokens))  # needs the int, as bool('0') is True
     self._writePreamble(_("Message clues"), parent=('review', _('Review')))
     sourceCorpus = None
     message = None
     if state.unknownCorpus.get(key) is not None:
         sourceCorpus = state.unknownCorpus
     elif state.hamCorpus.get(key) is not None:
         sourceCorpus = state.hamCorpus
     elif state.spamCorpus.get(key) is not None:
         sourceCorpus = state.spamCorpus
     if sourceCorpus is not None:
         message = sourceCorpus.get(key).as_string()
     if message is not None:
         message = message.replace('\r\n', '\n').replace('\r',
                                                         '\n')  # For Macs
         results = self._buildCluesTable(message, subject, tokens)
         del results.classifyAnother
         self.write(results)
     else:
         self.write(
             _("<p>Can't find message %r. Maybe it expired.</p>") % key)
     self._writePostamble()
Example #27
0
 def buildStatusStrings(self):
     """Build the status message(s) to display on the home page of the
     web interface."""
     nspam = self.bayes.nspam
     nham = self.bayes.nham
     if nspam > 10 and nham > 10:
         db_ratio = nham / float(nspam)
         if db_ratio > 5.0:
             self.warning = _("Warning: you have much more ham than " \
                              "spam - SpamBayes works best with " \
                              "approximately even numbers of ham and " \
                              "spam.")
         elif db_ratio < (1 / 5.0):
             self.warning = _("Warning: you have much more spam than " \
                              "ham - SpamBayes works best with " \
                              "approximately even numbers of ham and " \
                              "spam.")
         else:
             self.warning = ""
     elif nspam > 0 or nham > 0:
         self.warning = _("Database only has %d good and %d spam - " \
                          "you should consider performing additional " \
                          "training.") % (nham, nspam)
     else:
         self.warning = _("Database has no training information.  " \
                          "SpamBayes will classify all messages as " \
                          "'unsure', ready for you to train.")
     # Add an additional warning message if the user's thresholds are
     # truly odd.
     spam_cut = options["Categorization", "spam_cutoff"]
     ham_cut = options["Categorization", "ham_cutoff"]
     if spam_cut < 0.5:
         self.warning += _("<br/>Warning: we do not recommend " \
                           "setting the spam threshold less than 0.5.")
     if ham_cut > 0.5:
         self.warning += _("<br/>Warning: we do not recommend " \
                           "setting the ham threshold greater than 0.5.")
     if ham_cut > spam_cut:
         self.warning += _("<br/>Warning: your ham threshold is " \
                           "<b>higher</b> than your spam threshold. " \
                           "Results are unpredictable.")
Example #28
0
def stop():
    # Shutdown as though through the web UI.  This will save the DB, allow
    # any open proxy connections to complete, etc.
    from urllib import urlopen, urlencode
    urlopen('http://localhost:%d/save' % state.uiPort,
            urlencode({'how': _('Save & shutdown')})).read()
Example #29
0
    ('Headers', 'notate_subject'),
    ('Storage Options', None),
    ('Storage', 'persistent_storage_file'),
    ('Storage', 'messageinfo_storage_file'),
    ('Storage', 'cache_messages'),
    ('Storage', 'no_cache_bulk_ham'),
    ('Storage', 'no_cache_large_messages'),
    ('Statistics Options', None),
    ('Categorization', 'ham_cutoff'),
    ('Categorization', 'spam_cutoff'),
)

# Like the above, but these are the options that will be offered on the
# advanced configuration page.
adv_map = (
    (_('Statistics Options'), None),
    ('Classifier', 'max_discriminators'),
    ('Classifier', 'minimum_prob_strength'),
    ('Classifier', 'unknown_word_prob'),
    ('Classifier', 'unknown_word_strength'),
    ('Classifier', 'use_bigrams'),
    (_('Header Options'), None),
    ('Headers', 'include_score'),
    ('Headers', 'header_score_digits'),
    ('Headers', 'header_score_logarithm'),
    ('Headers', 'include_thermostat'),
    ('Headers', 'include_evidence'),
    ('Headers', 'clue_mailheader_cutoff'),
    (_('Storage Options'), None),
    ('Storage', 'persistent_use_database'),
    ('Storage', 'cache_expiry_days'),
Example #30
0
    def onReview(self, **params):
        """Present a list of message for (re)training."""
        # Train/discard sumbitted messages.
        self._writePreamble("Review")
        id = ''
        numTrained = 0
        numDeferred = 0
        if params.get('go') != _('Refresh'):
            for key, value in params.items():
                if key.startswith('classify:'):
                    old_class, id = key.split(':')[1:3]
                    if value == _('spam'):
                        targetCorpus = state.spamCorpus
                        stats_as_ham = False
                    elif value == _('ham'):
                        targetCorpus = state.hamCorpus
                        stats_as_ham = True
                    elif value == _('discard'):
                        targetCorpus = None
                        try:
                            state.unknownCorpus.removeMessage(\
                                state.unknownCorpus[id])
                        except KeyError:
                            pass  # Must be a reload.
                    else:  # defer
                        targetCorpus = None
                        numDeferred += 1
                    if targetCorpus:
                        sourceCorpus = None
                        if state.unknownCorpus.get(id) is not None:
                            sourceCorpus = state.unknownCorpus
                        elif state.hamCorpus.get(id) is not None:
                            sourceCorpus = state.hamCorpus
                        elif state.spamCorpus.get(id) is not None:
                            sourceCorpus = state.spamCorpus
                        if sourceCorpus is not None:
                            try:
                                # fromCache is a fix for sf #851785.
                                # See the comments in Corpus.py
                                targetCorpus.takeMessage(id,
                                                         sourceCorpus,
                                                         fromCache=True)
                                if numTrained == 0:
                                    self.write(_("<p><b>Training... "))
                                    self.flush()
                                numTrained += 1
                                self.stats.RecordTraining(\
                                  stats_as_ham, old_class=old_class)
                            except KeyError:
                                pass  # Must be a reload.

        # Report on any training, and save the database if there was any.
        if numTrained > 0:
            plural = ''
            if numTrained == 1:
                response = "Trained on one message. "
            else:
                response = "Trained on %d messages. " % (numTrained, )
            self._doSave()
            self.write(response)
            self.write("<br>&nbsp;")

        title = ""
        keys = []
        sourceCorpus = state.unknownCorpus
        # If any messages were deferred, show the same page again.
        if numDeferred > 0:
            start = self._keyToTimestamp(id)

        # Else after submitting a whole page, display the prior page or the
        # next one.  Derive the day of the submitted page from the ID of the
        # last processed message.
        elif id:
            start = self._keyToTimestamp(id)
            unused, unused, prior, unused, next = self._buildReviewKeys(start)
            if prior:
                start = prior
            else:
                start = next

        # Else if they've hit Previous or Next, display that page.
        elif params.get('go') == _('Next day'):
            start = self._keyToTimestamp(params['next'])
        elif params.get('go') == _('Previous day'):
            start = self._keyToTimestamp(params['prior'])

        # Else if an id has been specified, just show that message
        # Else if search criteria have been specified, show the messages
        # that match those criteria.
        elif params.get('find') is not None:
            prior = next = 0
            keys = set()  # so we don't end up with duplicates
            push = keys.add
            try:
                max_results = int(params['max_results'])
            except ValueError:
                max_results = 1
            key = params['find']
            if params.has_key('ignore_case'):
                ic = True
            else:
                ic = False
            error = False
            if key == "":
                error = True
                page = _("<p>You must enter a search string.</p>")
            else:
                if len(keys) < max_results and \
                   params.has_key('id'):
                    if state.unknownCorpus.get(key):
                        push((key, state.unknownCorpus))
                    elif state.hamCorpus.get(key):
                        push((key, state.hamCorpus))
                    elif state.spamCorpus.get(key):
                        push((key, state.spamCorpus))
                if params.has_key('subject') or params.has_key('body') or \
                   params.has_key('headers'):
                    # This is an expensive operation, so let the user know
                    # that something is happening.
                    self.write(_('<p>Searching...</p>'))
                    for corp in [
                            state.unknownCorpus, state.hamCorpus,
                            state.spamCorpus
                    ]:
                        for k in corp.keys():
                            if len(keys) >= max_results:
                                break
                            msg = corp[k]
                            msg.load()
                            if params.has_key('subject'):
                                subj = str(msg['Subject'])
                                if self._contains(subj, key, ic):
                                    push((k, corp))
                            if params.has_key('body'):
                                # For [ 906581 ] Assertion failed in search
                                # subject.  Can the headers be a non-string?
                                msg_body = msg.as_string()
                                msg_body = msg_body[msg_body.index('\r\n\r\n'
                                                                   ):]
                                if self._contains(msg_body, key, ic):
                                    push((k, corp))
                            if params.has_key('headers'):
                                for nm, val in msg.items():
                                    # For [ 906581 ] Assertion failed in
                                    # search subject.  Can the headers be
                                    # a non-string?
                                    nm = str(nm)
                                    val = str(val)
                                    if self._contains(nm, key, ic) or \
                                       self._contains(val, key, ic):
                                        push((k, corp))
                if len(keys):
                    if len(keys) == 1:
                        title = _("Found message")
                    else:
                        title = _("Found messages")
                    keys = list(keys)
                else:
                    page = _("<p>Could not find any matching messages. " \
                             "Maybe they expired?</p>")
                    title = _("Did not find message")
                    box = self._buildBox(title, 'status.gif', page)
                    self.write(box)
                    self.write(
                        self._buildBox(_('Find message'), 'query.gif',
                                       self.html.findMessage))
                    self._writePostamble()
                    return

        # Else show the most recent day's page, as decided by _buildReviewKeys.
        else:
            start = 0

        # Build the lists of messages: spams, hams and unsure.
        if len(keys) == 0:
            keys, date, prior, this, next = self._buildReviewKeys(start)
        keyedMessageInfo = {
            options["Headers", "header_unsure_string"]: [],
            options["Headers", "header_ham_string"]: [],
            options["Headers", "header_spam_string"]: [],
        }
        invalid_keys = []
        for key in keys:
            if isinstance(key, types.TupleType):
                key, sourceCorpus = key
            else:
                sourceCorpus = state.unknownCorpus
            # Parse the message, get the judgement header and build a message
            # info object for each message.
            message = sourceCorpus[key]
            try:
                message.load()
            except IOError:
                # Someone has taken this file away from us.  It was
                # probably a virus protection program, so that's ok.
                # Don't list it in the review, though.
                invalid_keys.append(key)
                continue
            judgement = message[options["Headers",
                                        "classification_header_name"]]
            if judgement is None:
                judgement = options["Headers", "header_unsure_string"]
            else:
                judgement = judgement.split(';')[0].strip()
            messageInfo = self._makeMessageInfo(message)
            keyedMessageInfo[judgement].append((key, messageInfo))
        for key in invalid_keys:
            keys.remove(key)

        # Present the list of messages in their groups in reverse order of
        # appearance, by default, or according to the specified sort order.
        if keys:
            page = self.html.reviewtable.clone()
            if prior:
                page.prior.value = prior
                del page.priorButton.disabled
            if next:
                page.next.value = next
                del page.nextButton.disabled
            templateRow = page.reviewRow.clone()

            # The decision about whether to reverse the sort
            # order has to go here, because _sortMessages gets called
            # thrice, and so the ham list would end up sorted backwards.
            sort_order = params.get('sort')
            if self.previous_sort == sort_order:
                reverse = True
                self.previous_sort = None
            else:
                reverse = False
                self.previous_sort = sort_order

            page.table = ""  # To make way for the real rows.
            for header, label in ((options["Headers",
                                           "header_unsure_string"], 'Unsure'),
                                  (options["Headers",
                                           "header_ham_string"], 'Ham'),
                                  (options["Headers",
                                           "header_spam_string"], 'Spam')):
                messages = keyedMessageInfo[header]
                if messages:
                    sh = self.html.reviewSubHeader.clone()
                    # Setup the header row
                    sh.optionalHeaders = ''
                    h = self.html.headerHeader.clone()
                    for disp_header in options["html_ui", "display_headers"]:
                        h.headerLink.href = 'review?sort=%sHeader' % \
                                            (disp_header.lower(),)
                        h.headerName = disp_header.title()
                        sh.optionalHeaders += h
                    if not options["html_ui", "display_score"]:
                        del sh.score_header
                    if not options["html_ui", "display_received_time"]:
                        del sh.received_header
                    subHeader = str(sh)
                    subHeader = subHeader.replace('TYPE', label)
                    page.table += self.html.blankRow
                    page.table += subHeader
                    self._appendMessages(page.table, messages, label,
                                         sort_order, reverse)

            page.table += self.html.trainRow
            if title == "":
                title = _("Untrained messages received on %s") % date
            box = self._buildBox(title, None, page)  # No icon, to save space.
        else:
            page = _("<p>There are no untrained messages to display. " \
                     "Return <a href='home'>Home</a>, or " \
                     "<a href='review'>check again</a>.</p>")
            title = _("No untrained messages")
            box = self._buildBox(title, 'status.gif', page)

        self.write(box)
        self._writePostamble(help_topic="review")
Example #31
0
__author__ = "Tony Meyer <*****@*****.**>, Tim Stone"
__credits__ = "All the Spambayes folk."

import cgi

from spambayes import UserInterface
from spambayes.Options import options, optionsPathname, _

# These are the options that will be offered on the configuration page.
# If the option is None, then the entry is a header and the following
# options will appear in a new box on the configuration page.
# These are also used to generate http request parameters and template
# fields/variables.
parm_map = (
    (_('IMAP Options'),       None),
    ('imap',                  'server'),
    ('imap',                  'username'),
    # to display, or not to display; that is the question!
    # if we show this here, it's in plain text for everyone to
    # see (and worse - if we don't restrict connections to
    # localhost, it's available for the world to see)
    # on the other hand, we have to be able to enter it somehow...
    ('imap',                  'password'),
    ('imap',                  'use_ssl'),
    (_('Header Options'),     None),
    ('Headers',               'notate_to'),
    ('Headers',               'notate_subject'),
    (_('Storage Options'),    None),
    ('Storage',               'persistent_storage_file'),
    ('Storage',               'messageinfo_storage_file'),
Example #32
0
 def _login_to_imap_server(self, imap, i):
     if imap and imap.logged_in:
         return imap
     if imap is None or not imap.connected:
         try:
             server = options["imap", "server"][i]
         except KeyError:
             content = self._buildBox(_("Error"), None,
                                      _("Please check server/port details."))
             self.write(content)
             return None
         if server.find(':') > -1:
             server, port = server.split(':', 1)
             port = int(port)
         else:
             if options["imap", "use_ssl"]:
                 port = 993
             else:
                 port = 143
         imap = self.imap_session_class(server, port)
         if not imap.connected:
             # Failed to connect.
             content = self._buildBox(_("Error"), None,
                                      _("Please check server/port details."))
             self.write(content)
             return None
     usernames = options["imap", "username"]
     if not usernames:
         content = self._buildBox(_("Error"), None,
                                  _("Must specify username first."))
         self.write(content)
         return None
     if not self.imap_pwds:
         self.imap_pwd = options["imap", "password"]
     if not self.imap_pwds:
         content = self._buildBox(_("Error"), None,
                                  _("Must specify password first."))
         self.write(content)
         return None
     try:
         imap.login(usernames[i], self.imap_pwds[i])
     except KeyError:
         content = self._buildBox(_("Error"), None,
                                  _("Please check username/password details."))
         self.write(content)
         return None
     except LoginFailure, e:
         content = self._buildBox(_("Error"), None, str(e))
         self.write(content)
         return None