Ejemplo n.º 1
0
    def find_message_by_filename(self, filename):
        """Find a message with the given filename

        .. warning::

            This call needs a writeable database in
            :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
            exit the program if this method is used on a read-only database!

        :returns: If the database contains a message with the given
            filename, then a class:`Message:` is returned.  This
            function returns None if no message is found with the given
            filename.

        :exception:
            :exc:`OutOfMemoryError`
                  If an Out-of-memory occured while constructing the message.
            :exc:`XapianError`
                  In case of a Xapian Exception. These exceptions
                  include "Database modified" situations, e.g. when the
                  notmuch database has been modified by another program
                  in the meantime. In this case, you should close and
                  reopen the database and retry.
            :exc:`NotInitializedError` if
                    the database was not intitialized.

        *Added in notmuch 0.9*"""
        self._assert_db_is_initialized()
        msg_p = NotmuchMessageP()
        status = Database._find_message_by_filename(self._db, _str(filename),
                                                    byref(msg_p))
        if status != STATUS.SUCCESS:
            raise NotmuchError(status)
        return msg_p and Message(msg_p, self) or None
Ejemplo n.º 2
0
    def find_message(self, msgid):
        """Returns a :class:`Message` as identified by its message ID

        Wraps the underlying *notmuch_database_find_message* function.

        :param msgid: The message ID
        :type msgid: unicode or str
        :returns: :class:`Message` or `None` if no message is found.
        :exception:
            :exc:`OutOfMemoryError`
                  If an Out-of-memory occured while constructing the message.
            :exc:`XapianError`
                  In case of a Xapian Exception. These exceptions
                  include "Database modified" situations, e.g. when the
                  notmuch database has been modified by another program
                  in the meantime. In this case, you should close and
                  reopen the database and retry.
            :exc:`NotInitializedError` if
                    the database was not intitialized.
        """
        self._assert_db_is_initialized()
        msg_p = NotmuchMessageP()
        status = Database._find_message(self._db, _str(msgid), byref(msg_p))
        if status != STATUS.SUCCESS:
            raise NotmuchError(status)
        return msg_p and Message(msg_p, self) or None
Ejemplo n.º 3
0
    def add_message(self, filename, sync_maildir_flags=False):
        """Adds a new message to the database

        :param filename: should be a path relative to the path of the
            open database (see :meth:`get_path`), or else should be an
            absolute filename with initial components that match the
            path of the database.

            The file should be a single mail message (not a
            multi-message mbox) that is expected to remain at its
            current location, since the notmuch database will reference
            the filename, and will not copy the entire contents of the
            file.

        :param sync_maildir_flags: If the message contains Maildir
            flags, we will -depending on the notmuch configuration- sync
            those tags to initial notmuch tags, if set to `True`. It is
            `False` by default to remain consistent with the libnotmuch
            API. You might want to look into the underlying method
            :meth:`Message.maildir_flags_to_tags`.

        :returns: On success, we return

           1) a :class:`Message` object that can be used for things
              such as adding tags to the just-added message.
           2) one of the following :attr:`STATUS` values:

              :attr:`STATUS`.SUCCESS
                  Message successfully added to database.
              :attr:`STATUS`.DUPLICATE_MESSAGE_ID
                  Message has the same message ID as another message already
                  in the database. The new filename was successfully added
                  to the list of the filenames for the existing message.

        :rtype:   2-tuple(:class:`Message`, :attr:`STATUS`)

        :exception: Raises a :exc:`NotmuchError` with the following meaning.
              If such an exception occurs, nothing was added to the database.

              :attr:`STATUS`.FILE_ERROR
                      An error occurred trying to open the file, (such as
                      permission denied, or file not found, etc.).
              :attr:`STATUS`.FILE_NOT_EMAIL
                      The contents of filename don't look like an email
                      message.
              :attr:`STATUS`.READ_ONLY_DATABASE
                      Database was opened in read-only mode so no message can
                      be added.
              :attr:`STATUS`.NOT_INITIALIZED
                      The database has not been initialized.
        """
        self._assert_db_is_initialized()
        msg_p = c_void_p()
        status = nmlib.notmuch_database_add_message(self._db,
                                                  _str(filename),
                                                  byref(msg_p))

        if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
            raise NotmuchError(status)

        #construct Message() and return
        msg = Message(msg_p, self)
        #automatic sync initial tags from Maildir flags
        if sync_maildir_flags:
            msg.maildir_flags_to_tags()
        return (msg, status)
Ejemplo n.º 4
0
    def add_message(self, filename, sync_maildir_flags=False):
        """Adds a new message to the database

        :param filename: should be a path relative to the path of the
            open database (see :meth:`get_path`), or else should be an
            absolute filename with initial components that match the
            path of the database.

            The file should be a single mail message (not a
            multi-message mbox) that is expected to remain at its
            current location, since the notmuch database will reference
            the filename, and will not copy the entire contents of the
            file.

        :param sync_maildir_flags: If the message contains Maildir
            flags, we will -depending on the notmuch configuration- sync
            those tags to initial notmuch tags, if set to `True`. It is
            `False` by default to remain consistent with the libnotmuch
            API. You might want to look into the underlying method
            :meth:`Message.maildir_flags_to_tags`.

        :returns: On success, we return

           1) a :class:`Message` object that can be used for things
              such as adding tags to the just-added message.
           2) one of the following :attr:`STATUS` values:

              :attr:`STATUS`.SUCCESS
                  Message successfully added to database.
              :attr:`STATUS`.DUPLICATE_MESSAGE_ID
                  Message has the same message ID as another message already
                  in the database. The new filename was successfully added
                  to the list of the filenames for the existing message.

        :rtype:   2-tuple(:class:`Message`, :attr:`STATUS`)

        :exception: Raises a :exc:`NotmuchError` with the following meaning.
              If such an exception occurs, nothing was added to the database.

              :attr:`STATUS`.FILE_ERROR
                      An error occurred trying to open the file, (such as
                      permission denied, or file not found, etc.).
              :attr:`STATUS`.FILE_NOT_EMAIL
                      The contents of filename don't look like an email
                      message.
              :attr:`STATUS`.READ_ONLY_DATABASE
                      Database was opened in read-only mode so no message can
                      be added.
        """
        self._assert_db_is_initialized()
        msg_p = NotmuchMessageP()
        status = self._add_message(self._db, _str(filename), byref(msg_p))

        if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
            raise NotmuchError(status)

        #construct Message() and return
        msg = Message(msg_p, self)
        #automatic sync initial tags from Maildir flags
        if sync_maildir_flags:
            msg.maildir_flags_to_tags()
        return (msg, status)
Ejemplo n.º 5
0
def manage (search) :
    _t0 = datetime.datetime.now ()
    result = { 'ok' : False }
    database_path = settings.NOTMUCH_DB
    exclude_tags = settings.EXCLUDE_TAGS

    if not "type" in search or search ['type'] == "message" :
        search_function = notmuch.Query.search_messages
        valid_global  = {'tags' : to_str_arr (Messages.collect_tags)}
        valid_details = {'id' : to_str (Message.get_message_id),
                         'thread_id' : to_str (Message.get_thread_id),
                         'tags' : to_str_arr (Message.get_tags),
                         'author': lambda msg : Message.get_header (msg, "From"),
                         'subject' : lambda msg : Message.get_header (msg, "Subject"),
                         'to': lambda msg : Message.get_header (msg, "To").split (","),
                         'cc': lambda msg : Message.get_header (msg, "Cc").split (","),
                         'bcc': lambda msg : Message.get_header (msg, "Bcc").split (","),
                         'date': lambda msg : time.strftime("%d %b, %X", time.localtime(msg.get_date())),
                         'parts' : lambda msg : get_parts_details (msg, search ['parts_details'] if 'parts_details' in search else {}),
                         'head' : get_head_msg,
        }
    elif search ['type'] == "thread" :
        search_function = notmuch.Query.search_threads
        valid_global  = {}
        valid_details = {'id' : to_str (Thread.get_thread_id),
                         'authors' : to_str_split (Thread.get_authors, ","),
                         'subject' : to_str (Thread.get_subject),
                         'dates' : lambda thread : [time.strftime("%d %b, %X", time.localtime (thread.get_oldest_date())), time.strftime("%d %b, %X", time.localtime (thread.get_newest_date()))],
                         'tags' : to_str_arr (Thread.get_tags),
                         'count' : Thread.get_total_messages,
                         'head' : get_head_thread,
                         'tree' : get_thread_messages_tree,
        }


    if 'options' in search and 'max_delay' in search ['options']:
        max_delay_present = True
        max_delay = search ['options']['max_delay']

    else :
        max_delay_present = False

    if 'global' in search :
        result ['global'] = {}
        def global_call () :
            database = notmuch.Database(database_path)
            query = notmuch.Query (database, search ['reference'] if "reference" in search else "")
            for tag in exclude_tags :
                query.exclude_tag (tag)
            elements = search_function (query)

            glob = {}
            glob ['ok'] = True
            for key in search ['global'] :
                if key in valid_global :
                    glob [key] = valid_global [key] (elements)

            del (elements)
            database.close()
            return glob

    if 'details' in search :
        result ['details'] = {}
        def details_call () :
            database = notmuch.Database(database_path)
            query = notmuch.Query (database, search ['reference'] if "reference" in search else "")
            for tag in exclude_tags :
                query.exclude_tag (tag)
            elements = search_function (query)

            details = {}
            details ['ok'] = True
            if 'options' in search and 'max_count' in search ['options'] :
                max_count_present = True
                max_count = search ['options']['max_count']
            else :
                max_count_present = False

            _count = 0
            for elt in elements :
                if max_count_present and max_count < _count :
                    details ['ok'] = False
                    details ['message'] = "incomplete"
                    break
                if max_delay_present and max_delay < (datetime.datetime.now () - _t0).total_seconds() :
                    # Ok, don’t run for ages, even if in a background thread
                    break
                details [_count] = {}
                for key in search ['details'] :
                    if key in valid_details :
                        details [_count][key] = valid_details [key] (elt)
                _count += 1

            del (elements)
            database.close()
            return details

    if 'global' in search :
        if max_delay_present :
            global_thread, global_queue = timeouted_call (global_call,
                                                          max_delay - (datetime.datetime.now() - _t0).total_seconds ()).async ()
        else :
            result ['global'] = global_call ()

    if 'details' in search :
        if max_delay_present :
            details_thread, details_queue = timeouted_call (details_call,
                                                            max_delay - (datetime.datetime.now() - _t0).total_seconds ()).async ()
        else :
            result ['details'] = details_call ()

    if max_delay_present :
        if 'global' in search :
            global_thread.join (max_delay - (datetime.datetime.now() - _t0).total_seconds ());
            if global_thread.is_alive () :
                global_result =  { 'ok' : False, 'message' : "Call took to much time"}
            else :
                global_result = global_queue.get ()
            result ['global'] = global_result
    if 'details' in search :
            details_thread.join (max_delay - (datetime.datetime.now() - _t0).total_seconds ());
            if details_thread.is_alive () :
                details_result =  { 'ok' : False, 'message' : "Call took to much time"}
            else :
                details_result = details_queue.get ()
            result ['details'] = details_result

    result ['ok'] = True

    return result