mirror of
https://github.com/pulb/mailnag.git
synced 2026-03-08 01:30:42 +01:00
Some fixes and refactoring of last commit
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -14,6 +14,7 @@ Popper was written by Ralf Hersel <ralf.hersel@gmx.net>.
|
||||
Code, docs and packaging contributors:
|
||||
======================================
|
||||
Amin Bandali <me@aminb.org>
|
||||
Andreas Angerer
|
||||
Balló György <ballogyor@gmail.com>
|
||||
Dan Christensen <jdc@uwo.ca>
|
||||
Edwin Smulders
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2016 Timo Kankare <timo.kankare@iki.fi>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -50,7 +51,7 @@ class MailboxBackend(object, metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
def list_messages(self):
|
||||
"""Lists unseen messages from the mailbox for this account.
|
||||
Yields tuples (folder, message) for every message.
|
||||
Yields tuples (folder, message, flags) for every message.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -61,6 +62,18 @@ class MailboxBackend(object, metaclass=ABCMeta):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def supports_mark_as_seen(self):
|
||||
"""Returns True if mailbox supports flagging mails as seen."""
|
||||
# Default implementation
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def mark_as_seen(self, mails):
|
||||
"""Asks mailbox to flag mails in the list as seen.
|
||||
This may raise an exception if mailbox does not support this action.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def supports_notifications(self):
|
||||
"""Returns True if mailbox supports notifications."""
|
||||
# Default implementation
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2011 - 2019 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 - 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2020 Andreas Angerer
|
||||
# Copyright 2016 Timo Kankare <timo.kankare@iki.fi>
|
||||
# Copyright 2016 Thomas Haider <t.haider@deprecate.de>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
@@ -82,7 +83,7 @@ class IMAPMailboxBackend(MailboxBackend):
|
||||
|
||||
for folder in folder_list:
|
||||
# select IMAP folder
|
||||
conn.select(f'"{folder}"')
|
||||
conn.select(f'"{folder}"', readonly = True)
|
||||
try:
|
||||
status, data = conn.uid('SEARCH', None, '(UNSEEN)') # ALL or UNSEEN
|
||||
except:
|
||||
@@ -101,7 +102,7 @@ class IMAPMailboxBackend(MailboxBackend):
|
||||
except:
|
||||
logging.debug("Couldn't get IMAP message.")
|
||||
continue
|
||||
yield (folder, msg, num.decode("utf-8"))
|
||||
yield (folder, msg, { 'uid' : num.decode("utf-8"), 'folder' : folder })
|
||||
|
||||
|
||||
def request_folders(self):
|
||||
@@ -128,11 +129,34 @@ class IMAPMailboxBackend(MailboxBackend):
|
||||
return lst
|
||||
|
||||
|
||||
def supports_mark_as_seen(self):
|
||||
return True
|
||||
|
||||
|
||||
def mark_as_seen(self, mails):
|
||||
# Always create a new connection as an existing one may
|
||||
# be used for IMAP IDLE.
|
||||
conn = self._connect()
|
||||
|
||||
try:
|
||||
last_folder = ''
|
||||
for m in mails:
|
||||
if ('uid' in m.flags) and ('folder' in m.flags):
|
||||
folder = m.flags['folder']
|
||||
if folder != last_folder:
|
||||
conn.select(f'"{folder}"', readonly = False)
|
||||
last_folder = folder
|
||||
status, data = conn.uid("STORE", m.flags['uid'], "+FLAGS", "(\Seen)")
|
||||
finally:
|
||||
self._disconnect(conn)
|
||||
|
||||
|
||||
def supports_notifications(self):
|
||||
"""Returns True if mailbox supports notifications.
|
||||
IMAP mailbox supports notifications if idle parameter is True"""
|
||||
return self.idle
|
||||
|
||||
|
||||
def notify_next_change(self, callback=None, timeout=None):
|
||||
self._ensure_open()
|
||||
|
||||
@@ -225,7 +249,7 @@ class IMAPMailboxBackend(MailboxBackend):
|
||||
folder = self.folders[0]
|
||||
else:
|
||||
folder = "INBOX"
|
||||
conn.select(f'"{folder}"')
|
||||
conn.select(f'"{folder}"', readonly = True)
|
||||
|
||||
def _ensure_open(self):
|
||||
if not self.is_open():
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2016 Timo Kankare <timo.kankare@iki.fi>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -61,7 +62,7 @@ class MBoxBackend(MailboxBackend):
|
||||
try:
|
||||
for msg in mbox:
|
||||
if 'R' not in msg.get_flags():
|
||||
yield folder, msg
|
||||
yield (folder, msg, {})
|
||||
finally:
|
||||
mbox.close()
|
||||
|
||||
@@ -71,6 +72,15 @@ class MBoxBackend(MailboxBackend):
|
||||
raise NotImplementedError("mbox does not support folders")
|
||||
|
||||
|
||||
def supports_mark_as_seen(self):
|
||||
return False
|
||||
|
||||
|
||||
def mark_as_seen(self, mails):
|
||||
# TODO: local mailboxes should support this
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def notify_next_change(self, callback=None, timeout=None):
|
||||
raise NotImplementedError("mbox does not support notifications")
|
||||
|
||||
|
||||
@@ -117,13 +117,21 @@ class POP3MailboxBackend(MailboxBackend):
|
||||
except:
|
||||
logging.debug("Couldn't get msg from POP message.")
|
||||
continue
|
||||
yield (folder, msg)
|
||||
yield (folder, msg, {})
|
||||
|
||||
|
||||
def request_folders(self):
|
||||
raise NotImplementedError("POP3 does not support folders")
|
||||
|
||||
|
||||
def supports_mark_as_seen(self):
|
||||
return False
|
||||
|
||||
|
||||
def mark_as_seen(self, mails):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def notify_next_change(self, callback=None, timeout=None):
|
||||
raise NotImplementedError("POP3 does not support notifications")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2011 - 2019 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 - 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2016 Thomas Haider <t.haider@deprecate.de>
|
||||
# Copyright 2016, 2018 Timo Kankare <timo.kankare@iki.fi>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
@@ -141,6 +141,14 @@ class Account:
|
||||
return self._get_backend().request_folders()
|
||||
|
||||
|
||||
def supports_mark_as_seen(self):
|
||||
return self._get_backend().supports_mark_as_seen()
|
||||
|
||||
|
||||
def mark_as_seen(self, mails):
|
||||
self._get_backend().mark_as_seen(mails)
|
||||
|
||||
|
||||
def get_id(self):
|
||||
"""Returns unique id for the account."""
|
||||
# Assumption: The name of the account is unique.
|
||||
|
||||
@@ -26,7 +26,6 @@ mailnag_defaults = {
|
||||
'poll_interval' : '10',
|
||||
'imap_idle_timeout' : '10',
|
||||
'autostart' : '1',
|
||||
'mark_imap_read' : '1',
|
||||
'connectivity_test' : 'auto',
|
||||
'enabled_plugins' : 'dbusplugin, soundplugin, libnotifyplugin'
|
||||
}
|
||||
|
||||
@@ -278,6 +278,7 @@ class ConfigWindow:
|
||||
aboutdialog.set_license_type(Gtk.License.GPL_2_0)
|
||||
aboutdialog.set_authors([
|
||||
"Patrick Ulbrich (maintainer)",
|
||||
"Andreas Angerer",
|
||||
"Balló György",
|
||||
"Dan Christensen",
|
||||
"Edwin Smulders",
|
||||
|
||||
@@ -108,9 +108,9 @@ class DBusService(dbus.service.Object):
|
||||
d['subject'] = m.subject # string (s)
|
||||
d['sender_name'] = name # string (s)
|
||||
d['sender_addr'] = addr # string (s)
|
||||
d['account_name'] = m.account_name # string (s)
|
||||
d['account_name'] = m.account.name # string (s)
|
||||
d['id'] = m.id # string (s)
|
||||
d['strID'] = m.strID # string (s)
|
||||
|
||||
converted_mails.append(d)
|
||||
|
||||
return converted_mails
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2011 - 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2020 Andreas Angerer
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,7 +37,6 @@ class MailChecker:
|
||||
self._conntest = conntest
|
||||
self._dbus_service = dbus_service
|
||||
self._count_on_last_check = 0
|
||||
self._all_mails = []
|
||||
|
||||
|
||||
def check(self, accounts):
|
||||
@@ -55,6 +55,7 @@ class MailChecker:
|
||||
all_mails = self._mailsyncer.sync(accounts)
|
||||
unseen_mails = []
|
||||
new_mails = []
|
||||
seen_mails_by_account = {}
|
||||
|
||||
for mail in all_mails:
|
||||
if self._memorizer.contains(mail.id): # mail was fetched before
|
||||
@@ -62,11 +63,24 @@ class MailChecker:
|
||||
unseen_mails.append(mail)
|
||||
if self._firstcheck:
|
||||
new_mails.append(mail)
|
||||
|
||||
else:
|
||||
# if the mail account supports tagging mails as seen (e.g. IMAP),
|
||||
# mark the mail as seen on the server as well.
|
||||
if mail.account.supports_mark_as_seen():
|
||||
if not mail.account in seen_mails_by_account:
|
||||
seen_mails_by_account[mail.account] = []
|
||||
seen_mails_by_account[mail.account].append(mail)
|
||||
else: # mail is fetched the first time
|
||||
unseen_mails.append(mail)
|
||||
new_mails.append(mail)
|
||||
self._all_mails = all_mails
|
||||
|
||||
# Flag mails to seen on server
|
||||
for acc, mails in seen_mails_by_account.items():
|
||||
try:
|
||||
acc.mark_as_seen(mails)
|
||||
except:
|
||||
logging.warning("Failed to set mails to seen on server (account: '%s').", acc.name)
|
||||
|
||||
self._memorizer.sync(all_mails)
|
||||
self._memorizer.save()
|
||||
self._firstcheck = False
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright 2016 Timo Kankare <timo.kankare@iki.fi>
|
||||
# Copyright 2014 - 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2020 Andreas Angerer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -126,23 +127,11 @@ class MailnagDaemon(MailnagController):
|
||||
|
||||
# Part of MailnagController interface
|
||||
def mark_mail_as_read(self, mail_id):
|
||||
mails = self._mailchecker._all_mails
|
||||
found = False
|
||||
for mail in mails:
|
||||
if mail_id == mail.id:
|
||||
found = True
|
||||
break
|
||||
if (not found) or (not bool(int(self._cfg.get('core', 'mark_imap_read')))):
|
||||
self._ensure_not_disposed()
|
||||
self._memorizer.set_to_seen(mail_id)
|
||||
self._memorizer.save()
|
||||
return
|
||||
backend = mail.account._get_backend()
|
||||
if type(backend).__name__ == 'IMAPMailboxBackend':
|
||||
mailid = mail.strID
|
||||
conn = backend._conn
|
||||
status, res = conn.uid("STORE", mailid, "+FLAGS", "(\Seen)")
|
||||
# Note: ensure_not_disposed() is not really necessary here
|
||||
# (the memorizer object is available in dispose()),
|
||||
# but better be consistent with other daemon methods.
|
||||
self._ensure_not_disposed()
|
||||
|
||||
self._memorizer.set_to_seen(mail_id)
|
||||
self._memorizer.save()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2011 - 2019 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 - 2020 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2020 Andreas Angerer
|
||||
# Copyright 2016, 2018 Timo Kankare <timo.kankare@iki.fi>
|
||||
# Copyright 2011 Leighton Earl <leighton.earl@gmx.com>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
@@ -35,15 +36,13 @@ from Mailnag.common.config import cfg_folder
|
||||
# Mail class
|
||||
#
|
||||
class Mail:
|
||||
def __init__(self, datetime, subject, sender, id, account, strID):
|
||||
def __init__(self, datetime, subject, sender, id, account, flags):
|
||||
self.datetime = datetime
|
||||
self.subject = subject
|
||||
self.sender = sender
|
||||
self.account = account
|
||||
self.account_name = account.name
|
||||
self.account_id = account.get_id()
|
||||
self.id = id
|
||||
self.strID = strID
|
||||
self.flags = flags
|
||||
|
||||
|
||||
#
|
||||
@@ -68,7 +67,7 @@ class MailCollector:
|
||||
logging.error("Failed to open mailbox for account '%s' (%s)." % (acc.name, ex))
|
||||
continue
|
||||
|
||||
for folder, msg, num in acc.list_messages():
|
||||
for folder, msg, flags in acc.list_messages():
|
||||
sender, subject, datetime, msgid = self._get_header(msg)
|
||||
id = self._get_id(msgid, acc, folder, sender, subject, datetime)
|
||||
|
||||
@@ -80,7 +79,7 @@ class MailCollector:
|
||||
# Also filter duplicates caused by Gmail labels.
|
||||
if id not in mail_ids:
|
||||
mail_list.append(Mail(datetime, subject, \
|
||||
sender, id, acc, num))
|
||||
sender, id, acc, flags))
|
||||
mail_ids[id] = None
|
||||
|
||||
# leave account with notifications open, so that it can
|
||||
@@ -195,12 +194,13 @@ class MailSyncer:
|
||||
|
||||
# collect mails from given accounts
|
||||
rcv_lst = MailCollector(self._cfg, accounts).collect_mail(sort = False)
|
||||
|
||||
# group received mails by account
|
||||
tmp = {}
|
||||
for acc in accounts:
|
||||
tmp[acc.get_id()] = {}
|
||||
for mail in rcv_lst:
|
||||
tmp[mail.account_id][mail.id] = mail
|
||||
tmp[mail.account.get_id()][mail.id] = mail
|
||||
|
||||
# compare current mails against received mails
|
||||
# and remove those that are gone (probably opened in mail client).
|
||||
|
||||
@@ -120,7 +120,7 @@ class UserscriptPlugin(Plugin):
|
||||
sender_name, sender_addr = m.sender
|
||||
if len(sender_addr) == 0: sender_addr = 'UNKNOWN_SENDER'
|
||||
|
||||
script_args.append(m.account_name)
|
||||
script_args.append(m.account.name)
|
||||
script_args.append(sender_addr)
|
||||
script_args.append(m.subject)
|
||||
start_subprocess(script_args)
|
||||
|
||||
Reference in New Issue
Block a user