示例#1
0
 def collect_tags(self, querystring):
     """returns tags of messages that match `querystring`"""
     db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
     tagset = notmuch2._tags.ImmutableTagSet(
         db.messages(querystring, exclude_tags=self.exclude_tags),
         '_iter_p', notmuch2.capi.lib.notmuch_messages_collect_tags)
     return [t for t in tagset]
示例#2
0
 def GET(self, terms):
     redir = False
     if web.input(terms=None).terms:
         redir = True
         terms = web.input().terms
     terms = unquote_plus(terms)
     if web.input(afters=None).afters:
         afters = web.input(afters=None).afters[:-3]
     else:
         afters = '0'
     if web.input(befores=None).befores:
         befores = web.input(befores=None).befores
     else:
         befores = '4294967296'  # 2^32
     try:
         if int(afters) > 0 or int(befores) < 4294967296:
             redir = True
             terms += ' date:@%s..@%s' % (int(afters), int(befores))
     except ValueError:
         pass
     if redir:
         raise web.seeother('/search/%s' % quote_plus(terms.encode('utf8')))
     web.header('Content-type', 'text/html')
     db = Database()
     ts = db.threads(query=terms, sort=Database.SORT.NEWEST_FIRST)
     template = env.get_template('search.html')
     return template.generate(terms=terms,
                              ts=ts,
                              title=terms,
                              prefix=prefix,
                              sprefix=webprefix)
示例#3
0
 def GET(self, mid):
     web.header('Content-type', 'text/html')
     db = Database()
     try:
         m = db.find(mid)
     except:
         raise web.notfound("No such message id.")
     template = env.get_template('show.html')
     # FIXME add reply-all link with email.urils.getaddresses
     # FIXME add forward link using mailto with body parameter?
     return template.render(m=m,
                            mid=mid,
                            title=m.header('Subject'),
                            prefix=prefix,
                            sprefix=webprefix)
示例#4
0
 def get_all_tags(self):
     """
     returns all tagsstrings used in the database
     :rtype: list of str
     """
     db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
     return [t for t in db.tags]
示例#5
0
 def _with_notmuch_thread(self, tid):
     """returns :class:`notmuch2.Thread` with given id"""
     with Database(path=self.path, mode=Database.MODE.READ_ONLY) as db:
         try:
             yield next(db.threads('thread:' + tid))
         except NotmuchError:
             errmsg = 'no thread with id %s exists!' % tid
             raise NonexistantObjectError(errmsg)
示例#6
0
 def get_named_queries(self):
     """
     returns the named queries stored in the database.
     :rtype: dict (str -> str) mapping alias to full query string
     """
     db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
     return {k[6:]: db.config[k] for k in db.config if
             k.startswith('query.')}
示例#7
0
 def _with_notmuch_message(self, mid):
     """returns :class:`notmuch2.Message` with given id"""
     mode = Database.MODE.READ_ONLY
     with Database(path=self.path, mode=mode) as db:
         try:
             yield db.find_message(mid)
         except:
             errmsg = 'no message with id %s exists!' % mid
             raise NonexistantObjectError(errmsg)
示例#8
0
 def GET(self):
     web.header('Content-type', 'text/html')
     base = env.get_template('base.html')
     template = env.get_template('index.html')
     db = Database()
     tags = db.tags
     return template.render(tags=tags,
                            title="Notmuch webmail",
                            prefix=prefix,
                            sprefix=webprefix)
示例#9
0
    def get_threads(self, querystring, sort='newest_first'):
        """
        asynchronously look up thread ids matching `querystring`.

        :param querystring: The query string to use for the lookup
        :type querystring: str.
        :param sort: Sort order. one of ['oldest_first', 'newest_first',
                     'message_id', 'unsorted']
        :type query: str
        :returns: a pipe together with the process that asynchronously
                  writes to it.
        :rtype: (:class:`multiprocessing.Pipe`,
                :class:`multiprocessing.Process`)
        """
        assert sort in self._sort_orders
        db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
        for t in db.threads(querystring,
                            sort=self._sort_orders[sort],
                            exclude_tags=self.exclude_tags):
            yield t.threadid
示例#10
0
 def _with_notmuch_thread(self, tid, querystring=None):
     """returns :class:`notmuch2.Thread` with given id"""
     if querystring:
         query = 'thread:' + tid + ' AND (' + querystring + ')'
     else:
         query = 'thread:' + tid
     with Database(path=self.path, mode=Database.MODE.READ_ONLY) as db:
         try:
             yield next(db.threads(query))
         except NotmuchError:
             errmsg = 'no thread with id %s exists!' % tid
             raise NonexistantObjectError(errmsg)
示例#11
0
def thread_nav(m):
    if not show_thread_nav: return
    db = Database()
    thread = next(db.threads('thread:' + m.threadid))
    prv = None
    found = False
    nxt = None
    for msg in thread:
        if m == msg:
            found = True
        elif not found:
            prv = msg
        else:  # found message, but not on this loop
            nxt = msg
            break
    yield "<hr><ul>"
    if prv: yield "<li>Previous message (by thread): %s</li>" % link_msg(prv)
    if nxt: yield "<li>Next message (by thread): %s</li>" % link_msg(nxt)
    yield "</ul><h3>Thread:</h3>"
    # FIXME show now takes three queries instead of 1;
    # can we yield the message body while computing the thread shape?
    thread = next(db.threads('thread:' + m.threadid))
    yield show_msgs(thread.toplevel())
    return
示例#12
0
    def setUpClass(cls):

        # create temporary notmuch config
        with tempfile.NamedTemporaryFile(mode='w+', delete=False) as f:
            f.write(textwrap.dedent("""\
                [maildir]
                synchronize_flags = true
                """))
            cls.notmuch_config_path = f.name
        cls.addClassCleanup(os.unlink, f.name)

        # define an empty notmuch database in a temporary directory
        cls.dbpath = tempfile.mkdtemp()
        cls.db = Database.create(path=cls.dbpath)
        cls.db.close()
        cls.manager = DBManager(cls.dbpath)

        # clean up temporary database
        cls.addClassCleanup(shutil.rmtree, cls.dbpath)

        # let global settings manager read our temporary notmuch config
        settings.read_notmuch_config(cls.notmuch_config_path)
示例#13
0
    def flush(self):
        """
        write out all queued write-commands in order, each one in a separate
        :meth:`atomic <notmuch2.Database.atomic>` transaction.

        If this fails the current action is rolled back, stays in the write
        queue and an exception is raised.
        You are responsible to retry flushing at a later time if you want to
        ensure that the cached changes are applied to the database.

        :exception: :exc:`~errors.DatabaseROError` if db is opened read-only
        :exception: :exc:`~errors.DatabaseLockedError` if db is locked
        """
        if self.ro:
            raise DatabaseROError()
        if self.writequeue:
            # read notmuch's config regarding imap flag synchronization
            sync = settings.get_notmuch_setting('maildir', 'synchronize_flags')

            # go through writequeue entries
            while self.writequeue:
                current_item = self.writequeue.popleft()
                logging.debug('write-out item: %s', str(current_item))

                # watch out for notmuch errors to re-insert current_item
                # to the queue on errors
                try:
                    # the first two coordinants are cnmdname and post-callback
                    cmd, afterwards = current_item[:2]
                    logging.debug('cmd created')

                    # acquire a writeable db handler
                    try:
                        mode = Database.MODE.READ_WRITE
                        db = Database(path=self.path, mode=mode)
                    except NotmuchError:
                        raise DatabaseLockedError()
                    logging.debug('got write lock')

                    # make this a transaction
                    with db.atomic():
                        logging.debug('got atomic')

                        if cmd == 'add':
                            logging.debug('add')
                            path, tags = current_item[2:]
                            msg, _ = db.add(path, sync_flags=sync)
                            logging.debug('added msg')
                            with msg.frozen():
                                logging.debug('freeze')
                                for tag in tags:
                                    msg.tags.add(tag)
                                if sync:
                                    msg.tags.to_maildir_flags()
                                logging.debug('added tags ')
                            logging.debug('thaw')

                        elif cmd == 'remove':
                            path = current_item[2]
                            db.remove(path)

                        elif cmd == 'setconfig':
                            key = current_item[2]
                            value = current_item[3]
                            db.config[key] = value

                        else:  # tag/set/untag
                            querystring, tags = current_item[2:]
                            if cmd == 'toggle':
                                presenttags = self.collect_tags(querystring)
                                to_remove = []
                                to_add = []
                                for tag in tags:
                                    if tag in presenttags:
                                        to_remove.append(tag)
                                    else:
                                        to_add.append(tag)

                            for msg in db.messages(querystring):
                                with msg.frozen():
                                    if cmd == 'toggle':
                                        for tag in to_remove:
                                            msg.tags.discard(tag)
                                        for tag in to_add:
                                            msg.tags.add(tag)
                                    else:
                                        if cmd == 'set':
                                            msg.tags.clear()

                                        for tag in tags:
                                            if cmd == 'tag' or cmd == 'set':
                                                msg.tags.add(tag)
                                            elif cmd == 'untag':
                                                msg.tags.discard(tag)
                                    if sync:
                                        msg.tags.to_maildir_flags()

                        logging.debug('ended atomic')

                    # close db
                    db.close()
                    logging.debug('closed db')

                    # call post-callback
                    if callable(afterwards):
                        logging.debug(str(afterwards))
                        afterwards()
                        logging.debug('called callback')

                # re-insert item to the queue upon Xapian/NotmuchErrors
                except (XapianError, NotmuchError) as e:
                    logging.exception(e)
                    self.writequeue.appendleft(current_item)
                    raise DatabaseError(str(e))
                except DatabaseLockedError as e:
                    logging.debug('index temporarily locked')
                    self.writequeue.appendleft(current_item)
                    raise e
                logging.debug('flush finished')
示例#14
0
 def count_threads(self, querystring):
     """returns number of threads that match `querystring`"""
     db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
     return db.count_threads(querystring, exclude_tags=self.exclude_tags)
示例#15
0
 def count_messages(self, querystring):
     """returns number of messages that match `querystring`"""
     db = Database(path=self.path, mode=Database.MODE.READ_ONLY)
     return db.count_messages(querystring,
                              exclude_tags=settings.get('exclude_tags'))