mirror of
https://github.com/pulb/mailnag.git
synced 2026-05-07 22:47:21 +02:00
mass commit
fixed imap idle implementation (reuse open imap connections and don't poll unaffected accounts) reconnect if a connection has been lost (e.g. after standby) moved connection check from startup script to mailnag.py removed unused/unnecessary code refactoring, fixed exception handling
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# account.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,7 +22,6 @@
|
||||
# MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
import time
|
||||
import poplib
|
||||
import daemon.imaplib2 as imaplib
|
||||
from common.i18n import _
|
||||
@@ -42,7 +41,7 @@ account_defaults = {
|
||||
class Account:
|
||||
def __init__(self, enabled = False, name = _('Unnamed'), user = '', \
|
||||
password = '', server = '', port = '', ssl = True, imap = False, idle = False, folder = '' ):
|
||||
|
||||
|
||||
self.enabled = enabled # bool
|
||||
self.name = name
|
||||
self.user = user
|
||||
@@ -53,55 +52,72 @@ class Account:
|
||||
self.imap = imap # bool
|
||||
self.idle = idle # bool
|
||||
self.folder = folder
|
||||
self._conn = None
|
||||
|
||||
|
||||
def get_connection(self): # get email server connection
|
||||
def get_connection(self, use_existing = False): # get email server connection
|
||||
if self.imap:
|
||||
try:
|
||||
srv = self._get_IMAP_connection()
|
||||
conn = self._get_IMAP_connection(use_existing)
|
||||
except:
|
||||
print "Error: Cannot connect to IMAP account: %s. " % self.server
|
||||
srv = None
|
||||
conn = None
|
||||
else:
|
||||
try:
|
||||
srv = self._get_POP3_connection()
|
||||
conn = self._get_POP3_connection(use_existing)
|
||||
except:
|
||||
print "Error: Cannot connect to POP account: %s. " % self.server
|
||||
srv = None
|
||||
conn = None
|
||||
|
||||
return srv # server object
|
||||
|
||||
|
||||
def _get_IMAP_connection(self):
|
||||
if self.ssl:
|
||||
if self.port == '':
|
||||
srv = imaplib.IMAP4_SSL(self.server)
|
||||
else:
|
||||
srv = imaplib.IMAP4_SSL(self.server, self.port)
|
||||
else:
|
||||
if self.port == '':
|
||||
srv = imaplib.IMAP4(self.server)
|
||||
else:
|
||||
srv = imaplib.IMAP4(self.server, self.port)
|
||||
|
||||
srv.login(self.user, self.password)
|
||||
return srv
|
||||
return conn
|
||||
|
||||
|
||||
def _get_POP3_connection(self):
|
||||
def get_id(self):
|
||||
# TODO : this id is not really unique...
|
||||
return str(hash(self.user + self.server))
|
||||
|
||||
|
||||
def _get_IMAP_connection(self, use_existing):
|
||||
# try to reuse existing connection
|
||||
if use_existing and (self._conn != None) and \
|
||||
(self._conn.state != imaplib.LOGOUT) and (not self._conn.Terminate):
|
||||
return self._conn
|
||||
|
||||
if self.ssl:
|
||||
if self.port == '':
|
||||
srv = poplib.POP3_SSL(self.server)
|
||||
self._conn = imaplib.IMAP4_SSL(self.server)
|
||||
else:
|
||||
srv = poplib.POP3_SSL(self.server, self.port)
|
||||
self._conn = imaplib.IMAP4_SSL(self.server, self.port)
|
||||
else:
|
||||
if self.port == '':
|
||||
srv = poplib.POP3(self.server)
|
||||
self._conn = imaplib.IMAP4(self.server)
|
||||
else:
|
||||
srv = poplib.POP3(self.server, self.port)
|
||||
self._conn = imaplib.IMAP4(self.server, self.port)
|
||||
|
||||
srv.getwelcome()
|
||||
srv.user(self.user)
|
||||
srv.pass_(self.password)
|
||||
return srv
|
||||
self._conn.login(self.user, self.password)
|
||||
|
||||
return self._conn
|
||||
|
||||
|
||||
def _get_POP3_connection(self, use_existing):
|
||||
# try to reuse existing connection
|
||||
if use_existing and (self._conn != None) and ('sock' in self._conn.__dict__):
|
||||
return self._conn
|
||||
|
||||
if self.ssl:
|
||||
if self.port == '':
|
||||
self._conn = poplib.POP3_SSL(self.server)
|
||||
else:
|
||||
self._conn = poplib.POP3_SSL(self.server, self.port)
|
||||
else:
|
||||
if self.port == '':
|
||||
self._conn = poplib.POP3(self.server)
|
||||
else:
|
||||
self._conn = poplib.POP3(self.server, self.port)
|
||||
|
||||
self._conn.getwelcome()
|
||||
self._conn.user(self.user)
|
||||
self._conn.pass_(self.password)
|
||||
|
||||
return self._conn
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# mails.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2007 Marco Ferragina <marco.ferragina@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,6 +27,7 @@ from gi.repository import Gst, Gio
|
||||
import threading
|
||||
import os
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
PACKAGE_NAME = "mailnag"
|
||||
|
||||
@@ -68,6 +69,14 @@ def get_default_mail_reader():
|
||||
return mail_reader
|
||||
|
||||
|
||||
def is_online(): # check for internet connection
|
||||
try:
|
||||
urllib2.urlopen("http://www.google.com/")
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
class _GstPlayThread(threading.Thread):
|
||||
def __init__(self, ply):
|
||||
self.ply = ply
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#
|
||||
# idler.py
|
||||
#
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Leighton Earl <leighton.earl@gmx.com>
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
#
|
||||
# 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
|
||||
@@ -23,13 +23,22 @@
|
||||
#
|
||||
|
||||
import threading
|
||||
from daemon.imaplib2 import AUTH
|
||||
|
||||
class Idler(object):
|
||||
def __init__(self, conn, sync_callback):
|
||||
def __init__(self, account, sync_callback):
|
||||
self._thread = threading.Thread(target=self._idle)
|
||||
self._conn = conn
|
||||
self._event = threading.Event()
|
||||
self._sync_callback = sync_callback
|
||||
self._account = account
|
||||
self._conn = account.get_connection(use_existing = True) # connection has been opened in mailnag.py already (immediate check)
|
||||
|
||||
if self._conn == None:
|
||||
raise Exception("Failed to establish a connection for account '%s'" % account.name)
|
||||
|
||||
# Need to get out of AUTH mode of fresh connections.
|
||||
if self._conn.state == AUTH:
|
||||
self._select(self._conn, account.folder)
|
||||
|
||||
|
||||
def run(self):
|
||||
@@ -41,8 +50,11 @@ class Idler(object):
|
||||
self._event.set()
|
||||
self._thread.join()
|
||||
|
||||
self._conn.close()
|
||||
self._conn.logout()
|
||||
try:
|
||||
self._conn.close()
|
||||
self._conn.logout()
|
||||
except:
|
||||
pass
|
||||
|
||||
print "Idler closed"
|
||||
|
||||
@@ -55,11 +67,19 @@ class Idler(object):
|
||||
self._needsync = False
|
||||
|
||||
def callback(args):
|
||||
if (args[2] != None) and (args[2][0] is self._conn.abort):
|
||||
# connection has been reset by provider -> reopen
|
||||
print "Idler thread for account '%s' reconnected" % self._account.name
|
||||
self._conn = self._account.get_connection(use_existing = False)
|
||||
self._select(self._conn, self._account.folder)
|
||||
|
||||
if not self._event.isSet():
|
||||
self._needsync = True
|
||||
self._event.set()
|
||||
|
||||
self._conn.idle(callback=callback)
|
||||
# register idle callback that is called whenever an idle event arrives (new mail / mail deleted).
|
||||
# the callback is called after 10 minutes at the latest. gmail sends keepalive events every 5 minutes.
|
||||
self._conn.idle(callback = callback, timeout = 60 * 10)
|
||||
|
||||
# Waits for event to be set
|
||||
self._event.wait()
|
||||
@@ -67,6 +87,12 @@ class Idler(object):
|
||||
# If the event is set due to idle sync
|
||||
if self._needsync:
|
||||
self._event.clear()
|
||||
self._sync_callback()
|
||||
self._sync_callback(self._account)
|
||||
|
||||
def _select(self, conn, folder):
|
||||
folder = folder.strip()
|
||||
if len(folder) > 0:
|
||||
conn.select(folder)
|
||||
else:
|
||||
conn.select("INBOX")
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#
|
||||
# idlers.py
|
||||
#
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Leighton Earl <leighton.earl@gmx.com>
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
#
|
||||
# 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
|
||||
@@ -35,35 +35,14 @@ class Idlers:
|
||||
for acc in self._accounts:
|
||||
if acc.imap and acc.idle:
|
||||
try:
|
||||
self._new_idler(acc)
|
||||
except:
|
||||
pass
|
||||
idler = Idler(acc, self._sync_callback)
|
||||
idler.run()
|
||||
self._idlerlist.append(idler)
|
||||
except Exception as ex:
|
||||
print "Error: Failed to create an idler thread for account '%s'" % acc.name
|
||||
|
||||
|
||||
def dispose(self):
|
||||
for idler in self._idlerlist:
|
||||
idler.dispose()
|
||||
|
||||
|
||||
def _new_idler(self, account):
|
||||
server = account.get_connection()
|
||||
|
||||
if server == None:
|
||||
return
|
||||
|
||||
# Need to get out of AUTH mode.
|
||||
if len(account.folder) > 0:
|
||||
server.select(account.folder)
|
||||
else:
|
||||
server.select("INBOX")
|
||||
|
||||
try:
|
||||
tmp = server.search(None, 'UNSEEN') # ALL or UNSEEN
|
||||
except:
|
||||
server.select('INBOX', readonly=True) # If search fails select INBOX and try again
|
||||
tmp = server.search(None, 'UNSEEN') # ALL or UNSEEN
|
||||
|
||||
idler = Idler(server, self._sync_callback)
|
||||
idler.run()
|
||||
self._idlerlist.append(idler)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# mail.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,10 +23,10 @@
|
||||
#
|
||||
|
||||
class Mail:
|
||||
def __init__(self, seconds, subject, sender, datetime, id, provider):
|
||||
def __init__(self, seconds, subject, sender, datetime, id, account_id):
|
||||
self.seconds = seconds
|
||||
self.subject = subject
|
||||
self.sender = sender
|
||||
self.datetime = datetime
|
||||
self.id = id
|
||||
self.provider = provider
|
||||
self.account_id = account_id
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# mailchecker.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -29,18 +29,19 @@ import subprocess
|
||||
import os
|
||||
import time
|
||||
|
||||
from common.utils import get_data_file, gstplay, get_default_mail_reader
|
||||
from common.utils import get_data_file, gstplay, is_online, get_default_mail_reader
|
||||
from common.i18n import _
|
||||
from daemon.reminder import Reminder
|
||||
from daemon.mails import Mails
|
||||
from daemon.mailsyncer import MailSyncer
|
||||
from daemon.pid import Pid
|
||||
|
||||
class MailChecker:
|
||||
def __init__(self, cfg, accounts):
|
||||
def __init__(self, cfg):
|
||||
self.MAIL_LIST_LIMIT = 10 # prevent flooding of the messaging tray
|
||||
self.firstcheck = True; # first check after startup
|
||||
self.mailcheck_lock = threading.Lock()
|
||||
self.mails = Mails(cfg, accounts)
|
||||
self.mail_list = []
|
||||
self.mailsyncer = MailSyncer(cfg)
|
||||
self.reminder = Reminder()
|
||||
self.pid = Pid()
|
||||
self.cfg = cfg
|
||||
@@ -50,14 +51,18 @@ class MailChecker:
|
||||
self.reminder.load()
|
||||
Notify.init(cfg.get('general', 'messagetray_label')) # initialize Notification
|
||||
|
||||
|
||||
def check(self):
|
||||
|
||||
def check(self, accounts):
|
||||
with self.mailcheck_lock:
|
||||
print 'Checking email accounts at:', time.asctime()
|
||||
self.pid.kill() # kill all zombies
|
||||
|
||||
self.mail_list = self.mails.get_mail('desc') # get all mails from all inboxes
|
||||
|
||||
if not is_online():
|
||||
print 'Error: No internet connection'
|
||||
return
|
||||
|
||||
self.mail_list = self.mailsyncer.sync(accounts)
|
||||
|
||||
unseen_mails = []
|
||||
new_mails = []
|
||||
|
||||
@@ -80,7 +85,7 @@ class MailChecker:
|
||||
script_data = str(script_data_mailcount) + script_data
|
||||
|
||||
if len(self.mail_list) == 0:
|
||||
# no mails (e.g. email client has been launched) -> close notifications
|
||||
# no mails (e.g. email client has been launched) -> close notifications
|
||||
for n in self.notifications.itervalues():
|
||||
n.close()
|
||||
self.notifications = {}
|
||||
@@ -94,14 +99,13 @@ class MailChecker:
|
||||
gstplay(get_data_file(self.cfg.get('general', 'soundfile')))
|
||||
|
||||
self.reminder.save(self.mail_list)
|
||||
self.__run_user_scripts("on_mail_check", script_data) # process user scripts
|
||||
sys.stdout.flush() # write stdout to log file
|
||||
self.firstcheck = False
|
||||
|
||||
self.__run_user_scripts("on_mail_check", script_data) # process user scripts
|
||||
|
||||
sys.stdout.flush() # write stdout to log file
|
||||
return True
|
||||
|
||||
return
|
||||
|
||||
|
||||
def __notify_summary(self, unseen_mails):
|
||||
summary = ""
|
||||
body = ""
|
||||
@@ -162,22 +166,7 @@ class MailChecker:
|
||||
# so remove its reference
|
||||
del self.notifications[user_data[1]]
|
||||
|
||||
|
||||
def clear(self):
|
||||
with self.mailcheck_lock:
|
||||
# mark all mails to seen
|
||||
for mail in self.mail_list:
|
||||
self.reminder.set_to_seen(mail.id)
|
||||
self.reminder.save(self.mail_list)
|
||||
|
||||
# close all notifications
|
||||
for n in self.notifications.itervalues():
|
||||
n.close()
|
||||
self.notifications = {}
|
||||
|
||||
self.mail_list = []
|
||||
|
||||
|
||||
|
||||
def __run_user_scripts(self, event, data):
|
||||
if event == "on_mail_check":
|
||||
if self.cfg.get('script', 'script0_enabled') == '1':
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# mails.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Leighton Earl <leighton.earl@gmx.com>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
@@ -24,7 +24,6 @@
|
||||
#
|
||||
|
||||
import time
|
||||
import urllib2
|
||||
import sys
|
||||
import email
|
||||
|
||||
@@ -38,100 +37,93 @@ class Mails:
|
||||
self.accounts = accounts
|
||||
|
||||
|
||||
def get_mail(self, sort_order):
|
||||
def get_mail(self, sort_order = None):
|
||||
mail_list = [] # initialize list of mails
|
||||
mail_ids = [] # initialize list of mail ids
|
||||
|
||||
if not self.__is_online(): # check internet connection
|
||||
return mail_list
|
||||
|
||||
filter_enabled = bool(int(self.cfg.get('filter', 'filter_enabled'))) # get filter switch
|
||||
|
||||
for acc in self.accounts:
|
||||
srv = acc.get_connection() # get server connection for this account
|
||||
srv = acc.get_connection(use_existing = True) # get server connection for this account
|
||||
if srv == None:
|
||||
continue # continue with next account if server is empty
|
||||
elif acc.imap: # IMAP
|
||||
folder_list = acc.folder.split(',') # make a list of folders
|
||||
mail_count = 0 # reset email counter
|
||||
if folder_list[0] == '':
|
||||
folder_list = ['INBOX']
|
||||
for folder in folder_list:
|
||||
|
||||
tmp = folder.strip()
|
||||
srv.select(tmp, readonly=True) # select IMAP folder
|
||||
folder = acc.folder.strip()
|
||||
if len(folder) == 0:
|
||||
folder = "INBOX"
|
||||
|
||||
srv.select(folder, readonly=True) # select IMAP folder
|
||||
try:
|
||||
status, data = srv.search(None, 'UNSEEN') # ALL or UNSEEN
|
||||
except:
|
||||
print "The folder:", folder, "does not exist, using INBOX instead"
|
||||
try:
|
||||
srv.select('INBOX', readonly=True) # If search fails select INBOX and try again
|
||||
status, data = srv.search(None, 'UNSEEN') # ALL or UNSEEN
|
||||
except:
|
||||
print "The folder:", tmp, "does not exist, using INBOX instead"
|
||||
try:
|
||||
srv.select('INBOX', readonly=True) # If search fails select INBOX and try again
|
||||
status, data = srv.search(None, 'UNSEEN') # ALL or UNSEEN
|
||||
except:
|
||||
print "INBOX Could not be found", sys.exc_info()[0]
|
||||
print "INBOX Could not be found", sys.exc_info()[0]
|
||||
|
||||
if status != 'OK' or None in [d for d in data]:
|
||||
print "Folder", folder, "in status", status, "| Data:", data, "\n"
|
||||
continue # Bugfix LP-735071
|
||||
for num in data[0].split():
|
||||
typ, msg_data = srv.fetch(num, '(BODY.PEEK[HEADER])') # header only (without setting READ flag)
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
if status != 'OK' or None in [d for d in data]:
|
||||
print "Folder", folder, "in status", status, "| Data:", data, "\n"
|
||||
continue # Bugfix LP-735071
|
||||
for num in data[0].split():
|
||||
typ, msg_data = srv.fetch(num, '(BODY.PEEK[HEADER])') # header only (without setting READ flag)
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
try:
|
||||
msg = email.message_from_string(response_part[1])
|
||||
except:
|
||||
print "Could not get IMAP message." # debug
|
||||
continue
|
||||
try:
|
||||
try:
|
||||
msg = email.message_from_string(response_part[1])
|
||||
except:
|
||||
print "Could not get IMAP message." # debug
|
||||
continue
|
||||
sender = self.__format_header('sender', msg['From']) # get sender and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'From' in message." # debug
|
||||
sender = self.__format_header('sender', msg['from'])
|
||||
except:
|
||||
print "Could not get sender from IMAP message." # debug
|
||||
sender = "Error in sender"
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
sender = self.__format_header('sender', msg['From']) # get sender and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'From' in message." # debug
|
||||
sender = self.__format_header('sender', msg['from'])
|
||||
except:
|
||||
print "Could not get sender from IMAP message." # debug
|
||||
sender = "Error in sender"
|
||||
datetime, seconds = self.__format_header('date', msg['Date']) # get date and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'Date' in message." # debug
|
||||
datetime, seconds = self.__format_header('date', msg['date'])
|
||||
except:
|
||||
print "Could not get date from IMAP message." # debug
|
||||
datetime = time.strftime('%Y.%m.%d %X') # take current time as "2010.12.31 13:57:04"
|
||||
seconds = time.time() # take current time as seconds
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
datetime, seconds = self.__format_header('date', msg['Date']) # get date and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'Date' in message." # debug
|
||||
datetime, seconds = self.__format_header('date', msg['date'])
|
||||
except:
|
||||
print "Could not get date from IMAP message." # debug
|
||||
datetime = time.strftime('%Y.%m.%d %X') # take current time as "2010.12.31 13:57:04"
|
||||
seconds = time.time() # take current time as seconds
|
||||
try:
|
||||
try:
|
||||
subject = self.__format_header('subject', msg['Subject']) # get subject and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'Subject' in message." # debug
|
||||
subject = self.__format_header('subject', msg['subject'])
|
||||
except:
|
||||
print "Could not get subject from IMAP message." # debug
|
||||
subject = _('No subject')
|
||||
try:
|
||||
id = msg['Message-Id']
|
||||
except:
|
||||
print "Could not get id from IMAP message." # debug
|
||||
id = None # prepare emergency
|
||||
|
||||
if id == None or id == '':
|
||||
id = str(hash(subject)) # create emergency id
|
||||
|
||||
if id not in mail_ids: # prevent duplicates caused by Gmail labels
|
||||
if not (filter_enabled and self.__in_filter(sender + subject)): # check filter
|
||||
mail_list.append(Mail(seconds, subject, \
|
||||
sender, datetime, id, acc.name))
|
||||
mail_count += 1 # increment mail counter for this IMAP folder
|
||||
mail_ids.append(id) # add id to list
|
||||
subject = self.__format_header('subject', msg['Subject']) # get subject and format it
|
||||
except KeyError:
|
||||
print "KeyError exception for key 'Subject' in message." # debug
|
||||
subject = self.__format_header('subject', msg['subject'])
|
||||
except:
|
||||
print "Could not get subject from IMAP message." # debug
|
||||
subject = _('No subject')
|
||||
try:
|
||||
id = msg['Message-Id']
|
||||
except:
|
||||
print "Could not get id from IMAP message." # debug
|
||||
id = None
|
||||
|
||||
if id == None or id == '':
|
||||
id = str(hash(acc.server + acc.user + sender + subject)) # create fallback id
|
||||
|
||||
if id not in mail_ids: # prevent duplicates caused by Gmail labels
|
||||
if not (filter_enabled and self.__in_filter(sender + subject)): # check filter
|
||||
mail_list.append(Mail(seconds, subject, \
|
||||
sender, datetime, id, acc.get_id()))
|
||||
mail_ids.append(id) # add id to list
|
||||
|
||||
acc.mail_count = mail_count # store number of emails per account
|
||||
srv.close()
|
||||
srv.logout()
|
||||
# don't close IMAP idle connections
|
||||
if not acc.idle:
|
||||
srv.close()
|
||||
srv.logout()
|
||||
else: # POP
|
||||
mail_total = len(srv.list()[1]) # number of mails on the server
|
||||
mail_count = 0 # reset number of relevant mails
|
||||
for i in range(1, mail_total+1): # for each mail
|
||||
try:
|
||||
message = srv.top(i, 0)[1] # header plus first 0 lines from body
|
||||
@@ -176,31 +168,25 @@ class Mails:
|
||||
uidl = srv.uidl(i) # get id
|
||||
except:
|
||||
print "Could not get id from POP message." # debug
|
||||
uidl = None # prepare emergency
|
||||
uidl = None
|
||||
|
||||
if uidl == None or uidl == '':
|
||||
uidl = str(hash(subject)) # create emergency id
|
||||
id = str(hash(acc.server + acc.user + sender + subject)) # create fallback id
|
||||
else:
|
||||
id = acc.user + uidl.split(' ')[2] # create unique id
|
||||
|
||||
id = acc.user + uidl.split(' ')[2] # create unique id
|
||||
if not (filter_enabled and self.__in_filter(sender + subject)): # check filter
|
||||
mail_list.append(Mail(seconds, subject, sender, \
|
||||
datetime, id, acc.name))
|
||||
mail_count += 1 # increment mail counter for this IMAP folder
|
||||
acc.mail_count = mail_count # store number of emails per account
|
||||
datetime, id, acc.get_id()))
|
||||
|
||||
srv.quit() # disconnect from Email-Server
|
||||
mail_list = self.__sort_mails(mail_list, sort_order) # sort mails
|
||||
|
||||
if (sort_order != None):
|
||||
mail_list = self.sort_mails(mail_list, sort_order) # sort mails
|
||||
sys.stdout.flush() # write stdout to log file
|
||||
return mail_list
|
||||
|
||||
|
||||
def __is_online(self): # check for internet connection
|
||||
try:
|
||||
urllib2.urlopen("http://www.google.com/")
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def __in_filter(self, sendersubject): # check if filter appears in sendersubject
|
||||
status = False
|
||||
filter_text = self.cfg.get('filter', 'filter_text')
|
||||
@@ -217,7 +203,8 @@ class Mails:
|
||||
return status
|
||||
|
||||
|
||||
def __sort_mails(self, mail_list, sort_order): # sort mail list
|
||||
@staticmethod
|
||||
def sort_mails(mail_list, sort_order): # sort mail list
|
||||
sort_list = []
|
||||
for mail in mail_list:
|
||||
sort_list.append([mail.seconds, mail]) # extract seconds from mail instance
|
||||
|
||||
76
Mailnag/daemon/mailsyncer.py
Normal file
76
Mailnag/daemon/mailsyncer.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# mailsyncer.py
|
||||
#
|
||||
# Copyright 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
from daemon.mails import Mails
|
||||
|
||||
class MailSyncer:
|
||||
def __init__(self, cfg):
|
||||
self._cfg = cfg
|
||||
self._mails_by_account = {}
|
||||
self._mail_list = []
|
||||
|
||||
|
||||
def sync(self, accounts):
|
||||
needs_rebuild = False
|
||||
|
||||
# get mails from given accounts
|
||||
rcv_lst = Mails(self._cfg, accounts).get_mail()
|
||||
|
||||
# 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
|
||||
|
||||
# compare current mails against received mails
|
||||
# and remove those that are gone (probably opened in mail client).
|
||||
for acc_id in self._mails_by_account.iterkeys():
|
||||
if acc_id in tmp:
|
||||
del_ids = []
|
||||
for mail_id in self._mails_by_account[acc_id].iterkeys():
|
||||
if not (mail_id in tmp[acc_id]):
|
||||
del_ids.append(mail_id)
|
||||
needs_rebuild = True
|
||||
for mail_id in del_ids:
|
||||
del self._mails_by_account[acc_id][mail_id]
|
||||
|
||||
# compare received mails against current mails
|
||||
# and add new mails.
|
||||
for acc_id in tmp:
|
||||
if not (acc_id in self._mails_by_account):
|
||||
self._mails_by_account[acc_id] = {}
|
||||
for mail_id in tmp[acc_id]:
|
||||
if not (mail_id in self._mails_by_account[acc_id]):
|
||||
self._mails_by_account[acc_id][mail_id] = tmp[acc_id][mail_id]
|
||||
needs_rebuild = True
|
||||
|
||||
# rebuild and sort mail list
|
||||
if needs_rebuild:
|
||||
self._mail_list = []
|
||||
for acc_id in self._mails_by_account:
|
||||
for mail_id in self._mails_by_account[acc_id]:
|
||||
self._mail_list.append(self._mails_by_account[acc_id][mail_id])
|
||||
self._mail_list = Mails.sort_mails(self._mail_list, 'desc')
|
||||
|
||||
return self._mail_list
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# mailnag.py
|
||||
#
|
||||
# Copyright 2011 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011, 2012 Patrick Ulbrich <zulu99@gmx.net>
|
||||
# Copyright 2011 Leighton Earl <leighton.earl@gmx.com>
|
||||
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
|
||||
#
|
||||
@@ -25,10 +25,11 @@
|
||||
|
||||
import os
|
||||
from gi.repository import GObject, GLib
|
||||
import time
|
||||
import signal
|
||||
|
||||
from common.config import read_cfg, cfg_exists, cfg_folder
|
||||
from common.utils import set_procname
|
||||
from common.utils import set_procname, is_online
|
||||
from common.accountlist import AccountList
|
||||
from daemon.mailchecker import MailChecker
|
||||
from daemon.idlers import Idlers
|
||||
@@ -91,23 +92,38 @@ def main():
|
||||
print 'Error: Cannot find configuration file. Please run mailnag_config first.'
|
||||
exit(1)
|
||||
|
||||
if not is_online():
|
||||
print 'Waiting for internet connection...'
|
||||
while not is_online():
|
||||
time.sleep(5)
|
||||
|
||||
accounts = AccountList()
|
||||
accounts.load_from_cfg(cfg, enabled_only = True)
|
||||
|
||||
mailchecker = MailChecker(cfg, accounts)
|
||||
mailchecker = MailChecker(cfg)
|
||||
|
||||
# immediate check
|
||||
mailchecker.check()
|
||||
# immediate check, check *all* accounts
|
||||
mailchecker.check(accounts)
|
||||
|
||||
idle_accounts = filter(lambda acc: acc.imap and acc.idle, accounts)
|
||||
non_idle_accounts = filter(lambda acc: (not acc.imap) or (acc.imap and not acc.idle), accounts)
|
||||
|
||||
# start polling thread for POP3 accounts and
|
||||
# IMAP accounts without idle support
|
||||
if sum(1 for acc in accounts if ((not acc.imap ) or (acc.imap and not acc.idle))) > 0:
|
||||
if len(non_idle_accounts) > 0:
|
||||
def poll_func():
|
||||
mailchecker.check(non_idle_accounts)
|
||||
return True
|
||||
|
||||
check_interval = int(cfg.get('general', 'check_interval'))
|
||||
GObject.timeout_add_seconds(60 * check_interval, mailchecker.check)
|
||||
GObject.timeout_add_seconds(60 * check_interval, poll_func)
|
||||
|
||||
# start idler threads for IMAP accounts with idle support
|
||||
if sum(1 for acc in accounts if (acc.imap and acc.idle)) > 0:
|
||||
idlers = Idlers(accounts, mailchecker.check)
|
||||
if len(idle_accounts) > 0:
|
||||
def sync_func(account):
|
||||
mailchecker.check([account])
|
||||
|
||||
idlers = Idlers(idle_accounts, sync_func)
|
||||
idlers.run()
|
||||
|
||||
mainloop = GObject.MainLoop()
|
||||
|
||||
41
mailnag
41
mailnag
@@ -3,37 +3,14 @@ LIB_DIR=./Mailnag
|
||||
|
||||
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/mailnag"
|
||||
|
||||
main()
|
||||
{
|
||||
mkdir --parents "$config_dir"
|
||||
mkdir --parents "$config_dir"
|
||||
|
||||
if [ -f "$config_dir/mailnag.pid" ]; then
|
||||
kill $(cat "$config_dir/mailnag.pid") 2> /dev/null
|
||||
fi
|
||||
|
||||
rm --force "$config_dir/mailnag.log"
|
||||
|
||||
cd $(dirname $(readlink -f $0))
|
||||
python $LIB_DIR/mailnag.py >> "$config_dir/mailnag.log" 2>&1 &
|
||||
}
|
||||
|
||||
check_connection()
|
||||
{
|
||||
retries=50
|
||||
while ! ping -c1 www.google.com 2>/dev/null 1>&2 && [ $retries -gt 0 ] ; do
|
||||
sleep 5
|
||||
# ((retries--))
|
||||
done
|
||||
|
||||
if [ $retries -gt 0 ]; then
|
||||
return 0
|
||||
else
|
||||
return 1 # timed out
|
||||
fi
|
||||
}
|
||||
|
||||
if check_connection; then
|
||||
main
|
||||
else
|
||||
echo "Error: No internet connection."
|
||||
if [ -f "$config_dir/mailnag.pid" ]; then
|
||||
kill $(cat "$config_dir/mailnag.pid") 2> /dev/null
|
||||
fi
|
||||
|
||||
rm --force "$config_dir/mailnag.log"
|
||||
|
||||
cd $(dirname $(readlink -f $0))
|
||||
python $LIB_DIR/mailnag.py >> "$config_dir/mailnag.log" 2>&1 &
|
||||
|
||||
|
||||
Reference in New Issue
Block a user