diff --git a/Mailnag/config.py b/Mailnag/config.py new file mode 100644 index 0000000..3ab680e --- /dev/null +++ b/Mailnag/config.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# config.py +# +# Copyright 2011 Patrick Ulbrich +# +# 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. +# +import os +import ConfigParser +import xdg.BaseDirectory as bd + +mailnag_defaults = { + 'general': + { + 'mail_client' : "evolution", + 'messagetray_label' : "mailnag", + 'check_interval' : 5, + 'notification_mode' : 0, + 'remember' : 1, + 'sender_format' : 1, + 'playsound' : 1, + 'soundfile' : 'mailnag.wav', + 'autostart' : 1 + }, + 'filter': + { + 'filter_on' : 0, + 'filter_text' : 'newsletter, viagra' + }, + 'script': + { + 'script0_on' : 0, + 'script1_on' : 0, + 'script0_file' : '', + 'script1_file' : '', + }, + 'account': + { + 'on' : '', + 'name' : '', + 'server' : '', + 'user' : '', + 'imap' : '', + 'folder' : '', + 'port' : '' + } +} + +cfg_folder = os.path.join(bd.xdg_config_home, "mailnag") +cfg_file = os.path.join(cfg_folder, "mailnag.cfg") + +def cfg_exists(): + return os.path.exists(cfg_file) + + +def read_cfg(): + cfg = ConfigParser.RawConfigParser() + cfg._sections = mailnag_defaults # HACK : use cfg.read_dict(mailnag_defaults) in python 3 + + if os.path.exists(cfg_file): + cfg.read(cfg_file) + + return cfg + + +def write_cfg(cfg): + if not os.path.exists(cfg_folder): + os.popen("mkdir -p " + cfg_folder) + + with open(cfg_file, 'wb') as configfile: cfg.write(configfile) diff --git a/Mailnag/config_window.py b/Mailnag/config_window.py index 7125223..1f5ee1a 100644 --- a/Mailnag/config_window.py +++ b/Mailnag/config_window.py @@ -25,13 +25,11 @@ PACKAGE_NAME = "mailnag" from gi.repository import GLib, GdkPixbuf, Gtk, GObject - import os -import ConfigParser import locale import gettext -import xdg.BaseDirectory as bd from utils import * +from config import read_cfg, write_cfg from keyring import Keyring from account_dialog import AccountDialog @@ -63,11 +61,13 @@ class ConfigWindow: self.window = builder.get_object("config_window") self.window.set_icon(GdkPixbuf.Pixbuf.new_from_file_at_size(get_data_file("mailnag.svg"), 48, 48)); + self.keyring = Keyring() + self.cfg = read_cfg() # # account tab # - self.accounts = Accounts() + self.accounts = Accounts(self.cfg, self.keyring) self.treeview_accounts = builder.get_object("treeview_accounts") self.liststore_accounts = builder.get_object("liststore_accounts") @@ -97,6 +97,10 @@ class ConfigWindow: self.entry_mail_client = builder.get_object("entry_mail_client") self.entry_label = builder.get_object("entry_label") self.spinbutton_interval = builder.get_object("spinbutton_interval") + self.cb_notification_mode = builder.get_object("cb_notification_mode") + cell = Gtk.CellRendererText() + self.cb_notification_mode.pack_start(cell, True) + self.cb_notification_mode.add_attribute(cell, "text", 0) self.chk_playsound = builder.get_object("chk_playsound") self.chk_autostart = builder.get_object("chk_autostart") @@ -127,32 +131,33 @@ class ConfigWindow: def load_config(self): - self.entry_mail_client.set_text(cfg.get('general', 'mail_client')) - self.entry_label.set_text(cfg.get('general', 'messagetray_label')) - self.spinbutton_interval.set_value(int(cfg.get('general', 'check_interval'))) - self.chk_playsound.set_active(int(cfg.get('general', 'playsound'))) - self.chk_autostart.set_active(int(cfg.get('general', 'autostart'))) + self.entry_mail_client.set_text(self.cfg.get('general', 'mail_client')) + self.entry_label.set_text(self.cfg.get('general', 'messagetray_label')) + self.spinbutton_interval.set_value(int(self.cfg.get('general', 'check_interval'))) + self.cb_notification_mode.set_active(int(self.cfg.get('general', 'notification_mode'))) + self.chk_playsound.set_active(int(self.cfg.get('general', 'playsound'))) + self.chk_autostart.set_active(int(self.cfg.get('general', 'autostart'))) - self.chk_enable_filter.set_active(int(cfg.get('filter', 'filter_on'))) - self.textbuffer_filter.set_text(cfg.get('filter', 'filter_text')) + self.chk_enable_filter.set_active(int(self.cfg.get('filter', 'filter_on'))) + self.textbuffer_filter.set_text(self.cfg.get('filter', 'filter_text')) - self.chk_script0.set_active(int(cfg.get('script', 'script0_on'))) + self.chk_script0.set_active(int(self.cfg.get('script', 'script0_on'))) - tmp = cfg.get('script', 'script0_file') + tmp = self.cfg.get('script', 'script0_file') if len(tmp) > 0: self.filechooser_script0.set_filename(tmp) - self.chk_script1.set_active(int(cfg.get('script', 'script1_on'))) + self.chk_script1.set_active(int(self.cfg.get('script', 'script1_on'))) - tmp = cfg.get('script', 'script1_file') + tmp = self.cfg.get('script', 'script1_file') if len(tmp) > 0: self.filechooser_script1.set_filename(tmp) self.accounts.load() if len(self.accounts.account) == 0: - imported_accounts = keyring.import_accounts() + imported_accounts = self.keyring.import_accounts() if len(imported_accounts) > 0 and \ self.show_yesno_dialog(_("Mailnag found %s mail accounts on this computer.\n\nDo you want to import them?") % len(imported_accounts)): for arr in imported_accounts: @@ -167,43 +172,43 @@ class ConfigWindow: def save_config(self): - cfg.set('general', 'mail_client', self.entry_mail_client.get_text()) - cfg.set('general', 'messagetray_label', self.entry_label.get_text()) - cfg.set('general', 'check_interval', int(self.spinbutton_interval.get_value())) - cfg.set('general', 'playsound',int(self.chk_playsound.get_active())) + self.cfg.set('general', 'mail_client', self.entry_mail_client.get_text()) + self.cfg.set('general', 'messagetray_label', self.entry_label.get_text()) + self.cfg.set('general', 'check_interval', int(self.spinbutton_interval.get_value())) + self.cfg.set('general', 'notification_mode', int(self.cb_notification_mode.get_active())) + self.cfg.set('general', 'playsound',int(self.chk_playsound.get_active())) autostart = self.chk_autostart.get_active() - cfg.set('general', 'autostart', int(autostart)) + self.cfg.set('general', 'autostart', int(autostart)) - cfg.set('filter', 'filter_on', int(self.chk_enable_filter.get_active())) + self.cfg.set('filter', 'filter_on', int(self.chk_enable_filter.get_active())) start, end = self.textbuffer_filter.get_bounds() - cfg.set('filter', 'filter_text', self.textbuffer_filter.get_text(start, end, True)) + self.cfg.set('filter', 'filter_text', self.textbuffer_filter.get_text(start, end, True)) - cfg.set('script', 'script0_on', int(self.chk_script0.get_active())) + self.cfg.set('script', 'script0_on', int(self.chk_script0.get_active())) tmp = self.filechooser_script0.get_filename() if tmp == None: tmp = "" - cfg.set('script', 'script0_file', tmp) + self.cfg.set('script', 'script0_file', tmp) - cfg.set('script', 'script1_on', int(self.chk_script1.get_active())) + self.cfg.set('script', 'script1_on', int(self.chk_script1.get_active())) tmp = self.filechooser_script1.get_filename() if tmp == None: tmp = "" - cfg.set('script', 'script1_file', tmp) + self.cfg.set('script', 'script1_file', tmp) on, name, server, user, password, imap, folder, port = self.accounts.get_cfg() - cfg.set('account', 'on', on) - cfg.set('account', 'name', name) - cfg.set('account', 'server', server) - cfg.set('account', 'user', user) - cfg.set('account', 'imap', imap) - cfg.set('account', 'folder', folder) - cfg.set('account', 'port', port) + self.cfg.set('account', 'on', on) + self.cfg.set('account', 'name', name) + self.cfg.set('account', 'server', server) + self.cfg.set('account', 'user', user) + self.cfg.set('account', 'imap', imap) + self.cfg.set('account', 'folder', folder) + self.cfg.set('account', 'port', port) for acc in self.accounts.account: if bool(acc.imap): protocol = 'imap' else: protocol = 'pop' - keyring.set(protocol, acc.user, acc.server, acc.password) + self.keyring.set(protocol, acc.user, acc.server, acc.password) - cfg_file = os.path.join(cfg_folder, "mailnag.cfg") - with open(cfg_file, 'wb') as configfile: cfg.write(configfile) + write_cfg(self.cfg) if autostart: create_autostart() else: delete_autostart() @@ -342,7 +347,7 @@ class ConfigWindow: def __save_and_quit(self): self.save_config() - keyring.remove(self.accounts.account) # delete obsolete entries from Keyring + self.keyring.remove(self.accounts.account) # delete obsolete entries from Keyring Gtk.main_quit() @@ -371,10 +376,11 @@ class Account: class Accounts: - def __init__(self): + def __init__(self, cfg, keyring): self.account = [] self.current = None # currently selected account_id - + self.cfg = cfg + self.keyring = keyring def add(self, on = 0, name = _('Unnamed'), server = '', user= '' , \ password = '', imap = 0, folder = '', port = ''): # add one new account @@ -395,13 +401,13 @@ class Accounts: def load(self): self.account = [] # empty account list - on = cfg.get('account', 'on') - name = cfg.get('account', 'name') - server = cfg.get('account', 'server') - user = cfg.get('account', 'user') - imap = cfg.get('account', 'imap') - folder = cfg.get('account', 'folder') - port = cfg.get('account', 'port') + on = self.cfg.get('account', 'on') + name = self.cfg.get('account', 'name') + server = self.cfg.get('account', 'server') + user = self.cfg.get('account', 'user') + imap = self.cfg.get('account', 'imap') + folder = self.cfg.get('account', 'folder') + port = self.cfg.get('account', 'port') separator = '|' on_list = on.split(separator) @@ -423,7 +429,7 @@ class Accounts: port = port_list[i] if imap: protocol = 'imap' else: protocol = 'pop' - password = keyring.get(protocol, user, server) + password = self.keyring.get(protocol, user, server) self.add(on, name, server, user, password, imap, folder, port) # fill Account list @@ -476,87 +482,9 @@ class Accounts: return cfg_on, cfg_name, cfg_server, cfg_user, password_list, cfg_imap, cfg_folder, cfg_port - def ok(self, window): # check if name, server, user, password are not empty - nok = [] # list of not-ok accounts - for acc in self.account: - if acc.name == '' or \ - acc.server == '' or \ - acc.user == '' or \ - acc.password == '': - nok.append(acc.name) - if len(nok) > 0: - message_text = _("Missing data in account(s):") + "\n\n" - for acc_name in nok: - message_text += acc_name + '\n' - message_text += "\n" + _("Please correct this first.") - window.show_message(message_text) - return False, nok - else: - return True, True - - # # Misc # -def read_config(): # read config file or create it - cfg = ConfigParser.RawConfigParser() - cfg_file = cfg_file = os.path.join(cfg_folder, "mailnag.cfg") - if not os.path.exists(cfg_file): # create a fresh config file - print _("Config file does not exist, creating new one") - cfg = set_default_config(cfg) # write default values to cfg - with open(cfg_file, 'wb') as configfile: cfg.write(configfile) - - cfg.read(cfg_file) - return cfg - - -def set_default_config(cfg): - try: cfg.add_section('general') - except ConfigParser.DuplicateSectionError: pass - cfg.set('general', 'mail_client', "evolution") - cfg.set('general', 'messagetray_label', "mailnag") - cfg.set('general', 'check_interval', 5) - cfg.set('general', 'show_only_new', 0) - cfg.set('general', 'remember', 1) - cfg.set('general', 'check_once', 0) - cfg.set('general', 'sender_format', 1) - cfg.set('general', 'playsound', 1) - cfg.set('general', 'soundfile', 'mailnag.wav') - cfg.set('general', 'autostart', 1) - - try: cfg.add_section('filter') - except ConfigParser.DuplicateSectionError: pass - cfg.set('filter', 'filter_on', 0) - cfg.set('filter', 'filter_text', 'newsletter, viagra') - - try: cfg.add_section('script') - except ConfigParser.DuplicateSectionError: pass - cfg.set('script', 'script0_on', 0) - cfg.set('script', 'script1_on', 0) - cfg.set('script', 'script2_on', 0) - cfg.set('script', 'script3_on', 0) - cfg.set('script', 'script4_on', 0) - cfg.set('script', 'script5_on', 0) - cfg.set('script', 'script0_file', '') - cfg.set('script', 'script1_file', '') - cfg.set('script', 'script2_file', '') - cfg.set('script', 'script3_file', '') - cfg.set('script', 'script4_file', '') - cfg.set('script', 'script5_file', '') - - try: cfg.add_section('account') - except ConfigParser.DuplicateSectionError: pass - cfg.set('account', 'on', '') - cfg.set('account', 'name', '') - cfg.set('account', 'server', '') - cfg.set('account', 'user', '') - cfg.set('account', 'imap', '') - cfg.set('account', 'folder', '') - cfg.set('account', 'port', '') - - return cfg - - def create_autostart(): curdir = os.getcwd() # get working directory exec_file = os.path.join(curdir, "mailnag") # path of the shell script to start mailnag.py @@ -593,20 +521,9 @@ def delete_autostart(): # Entry point # def main(): - global cfg, cfg_folder, keyring - set_procname("mailnag_config") - - cfg_folder = os.path.join(bd.xdg_config_home, "mailnag") - if not os.path.exists(cfg_folder): - os.popen("mkdir -p " + cfg_folder) - - cfg = read_config() # get configurations - keyring = Keyring() - confwin = ConfigWindow() - - Gtk.main() # start main loop + Gtk.main() if __name__ == "__main__": main() diff --git a/Mailnag/mailnag.py b/Mailnag/mailnag.py index 192796f..c6adfa5 100644 --- a/Mailnag/mailnag.py +++ b/Mailnag/mailnag.py @@ -22,27 +22,24 @@ # MA 02110-1301, USA. # +PACKAGE_NAME = "mailnag" + import poplib import imaplib import urllib2 -import ConfigParser import os import subprocess - -PACKAGE_NAME = "mailnag" - +import threading from gi.repository import GObject, GLib, GdkPixbuf, Gtk, Notify - import time import email from email.header import decode_header import sys import gettext +from config import read_cfg, cfg_exists, cfg_folder from keyring import Keyring from utils import * import signal -import xdg.BaseDirectory as bd - gettext.bindtextdomain(PACKAGE_NAME, './locale') gettext.textdomain(PACKAGE_NAME) @@ -347,7 +344,7 @@ class Mails: def in_filter(self, sendersubject): # check if filter appears in sendersubject status = False filter_text = cfg.get('filter', 'filter_text') - filter_list = filter_text.replace('\n', '').split(',') # convert text to list + filter_list = filter_text.replace('\n', '').split(',') # convert text to list for filter_item in filter_list: filter_stripped_item = filter_item.strip() # remove CR and white space @@ -429,27 +426,24 @@ class Mails: # Misc ================================================================= -def read_config(cfg_file): # read config file or create it - cfg = ConfigParser.RawConfigParser() - if not os.path.exists(cfg_file): - print 'Error: Cannot find configuration file: ', cfg_file - exit(1) +def read_config(): + if not cfg_exists(): + return None else: - cfg.read(cfg_file) - return cfg + return read_cfg() -def write_pid(): # write Mailnags's process id to file - pid_file = os.path.join(user_path, 'mailnag.pid') +def write_pid(): # write Mailnags's process id to file + pid_file = os.path.join(cfg_folder, 'mailnag.pid') f = open(pid_file, 'w') - f.write(str(os.getpid())) # get PID and write to file + f.write(str(os.getpid())) f.close() -def delete_pid(): # delete file mailnag.pid - pid_file = os.path.join(user_path, 'mailnag.pid') +def delete_pid(): # delete file mailnag.pid + pid_file = os.path.join(cfg_folder, 'mailnag.pid') if os.path.exists(pid_file): - os.popen("rm " + pid_file) # delete it + os.popen("rm " + pid_file) def user_scripts(event, data): @@ -488,99 +482,151 @@ def commandExecutor(command, context_menu_command=None): # MailChecker ============================================================ class MailChecker: def __init__(self): - self.MAIL_LIST_LIMIT = 10 # prevent flooding of the messaging tray - self.mails = Mails() # create Mails object - self.reminder = Reminder() # create Reminder object - - Notify.init(cfg.get('general', 'messagetray_label')) # initialize Notification - self.notification = Notify.Notification.new(" ", None, None) # empty string will emit a gtk warning - self.notification.set_hint("resident", GLib.Variant("b", True)) # don't close when the bubble or actions are clicked - self.notification.set_category("email") - self.notification.add_action("open", _("Open in mail reader"), self.__notification_action_handler, None, None) - self.notification.add_action("close", _("Close"), self.__notification_action_handler, None, None) + self.MAIL_LIST_LIMIT = 10 # prevent flooding of the messaging tray + self.mailcheck_lock = threading.Lock() + self.mails = Mails() + self.reminder = Reminder() + # dict that tracks all notifications that need to be closed + self.notifications = {} + + Notify.init(cfg.get('general', 'messagetray_label')) # initialize Notification def timeout(self, firstcheck = False): - print 'Checking email accounts at:', time.asctime() # debug - pid.kill() # kill all Zombies + with self.mailcheck_lock: + print 'Checking email accounts at:', time.asctime() + pid.kill() # kill all zombies - if firstcheck: # Manual firststart - self.reminder.load() + if firstcheck: # Manual firststart + self.reminder.load() - self.mail_list = self.mails.get_mail('desc') # get all mails from all inboxes + self.mail_list = self.mails.get_mail('desc') # get all mails from all inboxes - all_mails = len(self.mail_list) - new_mails = 0 - summary = "" - body = "" - script_data = "" + unseen_mails = [] + new_mails = [] - for mail in self.mail_list: - if not self.reminder.contains(mail.id): - new_mails += 1 - script_data += ' "<%s> %s"' % (mail.sender, mail.subject) - - script_data = str(new_mails) + script_data - - if all_mails == 0: - # no mails (e.g. email client has been launched) -> close notification - self.notification.close() - elif (firstcheck and all_mails > 0) or (new_mails > 0): - ubound = all_mails if all_mails <= self.MAIL_LIST_LIMIT else self.MAIL_LIST_LIMIT + script_data = "" + script_data_mailcount = 0 - for i in range(ubound): - body += self.mail_list[i].sender + ":\n" + self.mail_list[i].subject + "\n\n" + for mail in self.mail_list: + if self.reminder.contains(mail.id): # mail was fetched before + if self.reminder.unseen(mail.id): # mail was not marked as seen + unseen_mails.append(mail) + if firstcheck: # first check after startup + new_mails.append(mail) + + else: # mail is fetched the first time + unseen_mails.append(mail) + new_mails.append(mail) + script_data += ' "<%s> %s"' % (mail.sender, mail.subject) + script_data_mailcount += 1 - if all_mails > self.MAIL_LIST_LIMIT: - body += "" + _("(and {0} more)").format(str(all_mails - self.MAIL_LIST_LIMIT)) + "" + 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 + for n in self.notifications.itervalues(): + n.close() + self.notifications = {} + elif len(new_mails) > 0: + if cfg.get('general', 'notification_mode') == '1': + self.notify_summary(unseen_mails) + else: + self.notify_single(new_mails) - if all_mails > 1: # multiple new emails - summary = _("You have {0} new mails.").format(str(all_mails)) - else: - summary = _("You have a new mail.") + if cfg.get('general', 'playsound') == '1': # play sound? + soundcommand = ['aplay', '-q', get_data_file(cfg.get('general', 'soundfile'))] + pid.append(subprocess.Popen(soundcommand)) - if cfg.get('general', 'playsound') == '1': # play sound? - soundcommand = ['aplay', '-q', get_data_file(cfg.get('general', 'soundfile'))] - pid.append(subprocess.Popen(soundcommand)) + self.reminder.save(self.mail_list) - self.notification.update(summary, body, "mail-unread") - self.notification.show() - - user_scripts("on_mail_check", script_data) # process user scripts - - self.reminder.save(self.mail_list) - sys.stdout.flush() # write stdout to log file + user_scripts("on_mail_check", script_data) # process user scripts + + sys.stdout.flush() # write stdout to log file return True - def __notification_action_handler(self, n, action, user_data): - self.notification.close() - - if action == "open": - emailclient = cfg.get('general', 'mail_client').split(' ') # create list of command arguments - pid.append(subprocess.Popen(emailclient)) - elif action == "close": - pass + def notify_summary(self, unseen_mails): + summary = "" + body = "" - - def clear(self): # clear the messages list (not the menu entries) - show_only_new = bool(int(cfg.get('general', 'show_only_new'))) # get show_only_new flag + if len(self.notifications) == 0: + self.notifications['0'] = self.get_notification(" ", None, None) # empty string will emit a gtk warning - if show_only_new: + ubound = len(unseen_mails) if len(unseen_mails) <= self.MAIL_LIST_LIMIT else self.MAIL_LIST_LIMIT + + for i in range(ubound): + body += unseen_mails[i].sender + ":\n" + unseen_mails[i].subject + "\n\n" + + if len(unseen_mails) > self.MAIL_LIST_LIMIT: + body += "" + _("(and {0} more)").format(str(len(unseen_mails) - self.MAIL_LIST_LIMIT)) + "" + + if len(unseen_mails) > 1: # multiple new emails + summary = _("You have {0} new mails.").format(str(len(unseen_mails))) + else: + summary = _("You have a new mail.") + + self.notifications['0'].update(summary, body, "mail-unread") + self.notifications['0'].show() + + + def notify_single(self, new_mails): + for mail in new_mails: + n = self.get_notification(mail.sender, mail.subject, "mail-unread") + notification_id = str(id(n)) + n.add_action("mark-as-read", _("Mark as read"), self.__notification_action_handler, (mail, notification_id), None) + n.show() + self.notifications[notification_id] = n + + + def get_notification(self, summary, body, icon): + n = Notify.Notification.new(summary, body, icon) + n.set_category("email") + n.add_action("default", "default", self.__notification_action_handler, None, None) + + return n + + + def __notification_action_handler(self, n, action, user_data): + with self.mailcheck_lock: + if action == "default": + emailclient = cfg.get('general', 'mail_client').split(' ') # create list of command arguments + pid.append(subprocess.Popen(emailclient)) + + # clicking the notification bubble has closed all notifications + # so clear the reference array as well. + self.notifications = {} + + elif action == "mark-as-read": + self.reminder.set_to_seen(user_data[0].id) + self.reminder.save(self.mail_list) + + # clicking the action has closed the notification + # 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) # save to mailnag.dat - else: # keep 'list' filled - self.mail_list = [] # clear mail list - + self.reminder.save(self.mail_list) + + # close all notifications + for n in self.notifications.itervalues(): + n.close() + self.notifications = {} + + self.mail_list = [] + # Reminder ============================================================= class Reminder(dict): def load(self): # load last known messages from mailnag.dat remember = cfg.get('general', 'remember') - dat_file = os.path.join(user_path, 'mailnag.dat') + dat_file = os.path.join(cfg_folder, 'mailnag.dat') we_have_a_file = os.path.exists(dat_file) # check if file exists if remember == '1' and we_have_a_file: f = open(dat_file, 'r') # open file again @@ -595,7 +641,7 @@ class Reminder(dict): def save(self, mail_list): # save mail ids to file - dat_file = os.path.join(user_path, 'mailnag.dat') + dat_file = os.path.join(cfg_folder, 'mailnag.dat') f = open(dat_file, 'w') # open the file for overwrite for m in mail_list: try: @@ -609,11 +655,7 @@ class Reminder(dict): def contains(self, id): # check if mail id is in reminder list - try: - self[id] - return True - except KeyError: - return False + return (id in self) def set_to_seen(self, id): # set seen flag for this email on True @@ -626,10 +668,7 @@ class Reminder(dict): def unseen(self, id): # return True if flag == '0' try: flag = self[id] - if flag == '0': - return True - else: - return False + return (flag == '0') except KeyError: return True @@ -650,36 +689,48 @@ class Pid(list): # List class to manage subprocess PIDs def cleanup(): # clean up resources try: - mailchecker.notification.close() + for n in mailchecker.notifications.itervalues(): + n.close() except NameError: pass delete_pid() + +def sig_handler(signum, frame): + if mainloop != None: + mainloop.quit() + + # Main ================================================================= def main(): - global cfg, user_path, accounts, mails, mailchecker, pid + global mainloop, cfg, accounts, mails, mailchecker, pid + mainloop = None + set_procname("mailnag") - signal.signal(signal.SIGTERM, cleanup) - + signal.signal(signal.SIGTERM, sig_handler) + try: - user_path = os.path.join(bd.xdg_config_home, "mailnag") - write_pid() # write Mailnag's process id to file - cfg = read_config(os.path.join(user_path, 'mailnag.cfg')) # get configuration from file + write_pid() # write Mailnag's process id to file + cfg = read_config() + + if (cfg == None): + print 'Error: Cannot find configuration file. Please run mailnag_config first.' + exit(1) accounts = Accounts() # create Accounts object pid = Pid() # create Pid object mailchecker = MailChecker() # create MailChecker object mailchecker.timeout(True) # immediate check, firstcheck=True - if cfg.get('general', 'check_once') == '0': # wanna do more than one email check? - check_interval = int(cfg.get('general', 'check_interval')) - GObject.timeout_add_seconds(60 * check_interval, mailchecker.timeout) - Gtk.main() # start Loop - - cleanup() - return 0 + check_interval = int(cfg.get('general', 'check_interval')) + GObject.timeout_add_seconds(60 * check_interval, mailchecker.timeout) + mainloop = GObject.MainLoop() + mainloop.run() except KeyboardInterrupt: + pass # ctrl+c pressed + finally: cleanup() + if __name__ == '__main__': main() diff --git a/data/config_window.ui b/data/config_window.ui index 1c23cd2..06fd717 100644 --- a/data/config_window.ui +++ b/data/config_window.ui @@ -134,7 +134,7 @@ 6 6 6 - 5 + 6 2 6 6 @@ -150,8 +150,8 @@ 2 - 3 - 4 + 4 + 5 @@ -233,8 +233,8 @@ 2 - 4 - 5 + 5 + 6 @@ -254,8 +254,8 @@ - 2 - 3 + 3 + 4 GTK_FILL @@ -294,6 +294,40 @@ + + 1 + 2 + 3 + 4 + + + + + True + False + 0 + + + True + False + start + 0 + Notification mode: + + + + + 2 + 3 + GTK_FILL + + + + + True + False + liststore_notification_mode + 1 2 @@ -599,5 +633,19 @@ Copyright (c) 2011 Ralf Hersel + + + + + + + + Single + + + Summary + + +