rewritten account stuff (unfortunately this breaks existing config files)

This commit is contained in:
Patrick Ulbrich
2011-11-26 20:23:24 +01:00
parent b6a318766b
commit fb32f75ca5
16 changed files with 521 additions and 600 deletions

125
Mailnag/common/account.py Normal file
View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# account.py
#
# Copyright 2011 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
# 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.
#
PACKAGE_NAME = "mailnag"
import gettext
import time
import poplib
import daemon.imaplib2 as imaplib
gettext.bindtextdomain(PACKAGE_NAME, './locale')
gettext.textdomain(PACKAGE_NAME)
_ = gettext.gettext
account_defaults = {
'enabled' : '0',
'name' : '',
'user' : '',
'server' : '',
'port' : '',
'ssl' : '0',
'imap' : '0',
'idle' : '0',
'folder' : ''
}
class Account:
def __init__(self, enabled = False, name = _('Unnamed'), user = '', \
password = '', server = '', port = '', ssl = False, imap = False, idle = False, folder = '' ):
self.enabled = enabled # bool
self.name = name
self.user = user
self.password = password
self.server = server
self.port = port
self.ssl = ssl # bool
self.imap = imap # bool
self.idle = idle # bool
self.folder = folder
def get_connection(self): # get email server connection
if self.imap: # IMAP
try:
srv = self._get_IMAP_connection()
except:
print "Warning: Cannot connect to IMAP account: %s. " \
"Next try in 30 seconds." % self.server
time.sleep(30) # wait 30 seconds
try:
srv = self._get_IMAP_connection()
except:
print "Error: Cannot connect to IMAP account: %s. " % self.server
srv = None
else: # POP
try:
srv = self._get_POP3_connection()
except:
print "Warning: Cannot connect to POP account: %s. " \
"Next try in 30 seconds." % self.server
time.sleep(30) # wait 30 seconds
try:
srv = self._get_POP3_connection()
except:
print "Error: Cannot connect to POP account: %s. " % self.server
srv = 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
def _get_POP3_connection(self):
if self.ssl:
if self.port == '':
srv = poplib.POP3_SSL(self.server)
else:
srv = poplib.POP3_SSL(self.server, self.port)
else:
if self.port == '':
srv = poplib.POP3(self.server)
else:
srv = poplib.POP3(self.server, self.port)
srv.getwelcome()
srv.user(self.user)
srv.pass_(self.password)
return srv

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# accountlist.py
#
# 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
# 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.
#
PACKAGE_NAME = "mailnag"
from ConfigParser import NoSectionError
from common.account import Account, account_defaults
from common.keyring import Keyring
class AccountList(list):
def __init__(self):
self._keyring = Keyring()
def load_from_cfg(self, cfg, enabled_only=False):
del self[:]
i = 1
section_name = "Account" + str(i)
while cfg.has_section(section_name):
enabled = bool(int( self._get_account_cfg(cfg, section_name, 'enabled') ))
if (not enabled_only) or (enabled_only and enabled):
name = self._get_account_cfg(cfg, section_name, 'name')
user = self._get_account_cfg(cfg, section_name, 'user')
server = self._get_account_cfg(cfg, section_name, 'server')
port = self._get_account_cfg(cfg, section_name, 'port')
ssl = bool(int( self._get_account_cfg(cfg, section_name, 'ssl') ))
imap = bool(int( self._get_account_cfg(cfg, section_name, 'imap') ))
idle = bool(int( self._get_account_cfg(cfg, section_name, 'idle') ))
folder = self._get_account_cfg(cfg, section_name, 'folder')
protocol = 'imap' if imap else 'pop'
password = self._keyring.get(protocol, user, server)
acc = Account(enabled, name, user, password, server, port, ssl, imap, idle, folder)
self.append(acc)
i = i + 1
section_name = "Account" + str(i)
def save_to_cfg(self, cfg):
# remove existing accounts from cfg
i = 1
section_name = "Account" + str(i)
while cfg.has_section(section_name):
cfg.remove_section(section_name)
i = i + 1
section_name = "Account" + str(i)
# add accounts
i = 1
for acc in self:
section_name = "Account" + str(i)
cfg.add_section(section_name)
cfg.set(section_name, 'enabled', int(acc.enabled))
cfg.set(section_name, 'name', acc.name)
cfg.set(section_name, 'user', acc.user)
cfg.set(section_name, 'server', acc.server)
cfg.set(section_name, 'port', acc.port)
cfg.set(section_name, 'ssl', int(acc.ssl))
cfg.set(section_name, 'imap', int(acc.imap))
cfg.set(section_name, 'idle', int(acc.idle))
cfg.set(section_name, 'folder', acc.folder)
protocol = 'imap' if acc.imap else 'pop'
self._keyring.set(protocol, acc.user, acc.server, acc.password)
i = i + 1
# delete obsolete entries from Keyring
self._keyring.remove(self)
def import_from_keyring(self):
# append imported accounts to existing accounts
self.extend(self._keyring.import_accounts())
def _get_account_cfg(self, cfg, section_name, option_name):
if cfg.has_option(section_name, option_name):
return cfg.get(section_name, option_name)
else:
return account_defaults[option_name]

View File

@@ -21,45 +21,33 @@
# MA 02110-1301, USA.
#
import os
import ConfigParser
import xdg.BaseDirectory as bd
from ConfigParser import RawConfigParser
from utils import get_default_mail_reader
mailnag_defaults = {
'general':
{
'mail_client' : get_default_mail_reader(),
'messagetray_label' : "mailnag",
'check_interval' : 5,
'notification_mode' : 0,
'sender_format' : 1,
'playsound' : 1,
'messagetray_label' : 'mailnag',
'check_interval' : '5',
'notification_mode' : '0',
'sender_format' : '1',
'playsound' : '1',
'soundfile' : 'mailnag.ogg',
'autostart' : 1
'autostart' : '1'
},
'filter':
{
'filter_on' : 0,
'filter_enabled' : '0',
'filter_text' : 'newsletter, viagra'
},
'script':
{
'script0_on' : 0,
'script1_on' : 0,
'script0_enabled' : '0',
'script1_enabled' : '0',
'script0_file' : '',
'script1_file' : '',
},
'account':
{
'on' : '',
'name' : '',
'server' : '',
'user' : '',
'imap' : '',
'folder' : '',
'port' : ''
'script1_file' : ''
}
}
@@ -71,7 +59,7 @@ def cfg_exists():
def read_cfg():
cfg = ConfigParser.RawConfigParser()
cfg = RawConfigParser()
cfg._sections = mailnag_defaults # HACK : use cfg.read_dict(mailnag_defaults) in python 3
if os.path.exists(cfg_file):

View File

@@ -27,7 +27,9 @@ from gi.repository import GObject, GLib, GdkPixbuf, Gtk
import locale
import gettext
import gnomekeyring
from utils import get_data_file
from common.utils import get_data_file
from common.account import Account
PACKAGE_NAME = "mailnag"
@@ -89,7 +91,7 @@ class Keyring:
return ''
def import_accounts(self): # get email accounts from Gnome-Keyring
def import_accounts(self): # get email accounts from Gnome-Keyring
accounts = []
if gnomekeyring.list_item_ids_sync(self.defaultKeyring):
displayNameDict = {}
@@ -101,21 +103,26 @@ class Keyring:
or displayName.startswith('imap://'):
server = displayName.split('@')[1][:-1]
if displayName.startswith('imap://'):
imap = 1
imap = True
user = displayName.split('@')[0][7:]
else:
imap = 0
imap = False
user = displayName.split('@')[0][6:]
user = user.replace('%40','@')
if ';' in user:
user = user.split(';')[0]
password = gnomekeyring.item_get_info_sync(self.defaultKeyring, \
displayNameDict[displayName]).get_secret()
accounts.append([server, user, password, imap])
accounts.append(Account(enabled = True, name = "%s (%s)" % (server, user), \
server = server, user = user, password = password, imap = imap))
return accounts
def set(self, protocol, user, server, password): # store password in Gnome-Keyring
def set(self, protocol, user, server, password): # store password in Gnome-Keyring
if password != '':
displayNameDict = {}
for identity in gnomekeyring.list_item_ids_sync(self.defaultKeyring):

View File

@@ -1,40 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# account.py
#
# Copyright 2011 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
# 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.
#
class Account:
def __init__(self, on, name, server, user, password, imap, folder, port):
self.id = str(id(self)) # unique identifier
self.on = on # int
self.name = name
self.server = server
self.user = user
self.password = password
self.imap = imap # int
self.folder = folder
self.port = port
def get_row(self):
return [self.id, self.on, self.name]

View File

@@ -23,8 +23,8 @@
PACKAGE_NAME = "mailnag"
from gi.repository import GLib, GdkPixbuf, Gtk
import gettext
from gi.repository import GLib, GdkPixbuf, Gtk
from common.utils import get_data_file
gettext.bindtextdomain(PACKAGE_NAME, './locale')
@@ -38,8 +38,8 @@ class AccountDialog:
builder.set_translation_domain(PACKAGE_NAME)
builder.add_from_file(get_data_file("account_dialog.ui"))
builder.connect_signals({ \
"account_type_changed" : self.__on_cmb_account_type_changed, \
"entry_changed" : self.__on_entry_changed, \
"chk_account_imap_toggled" : self.__on_chk_account_imap_toggled, \
"btn_cancel_clicked" : self.__on_btn_cancel_clicked, \
"btn_save_clicked" : self.__on_btn_save_clicked \
})
@@ -47,15 +47,20 @@ class AccountDialog:
self.window = builder.get_object("account_dialog")
self.window.set_transient_for(parent)
self.cmb_account_type = builder.get_object("cmb_account_type")
self.entry_account_name = builder.get_object("entry_account_name")
self.entry_account_user = builder.get_object("entry_account_user")
self.entry_account_password = builder.get_object("entry_account_password")
self.entry_account_server = builder.get_object("entry_account_server")
self.entry_account_port = builder.get_object("entry_account_port")
self.chk_account_imap = builder.get_object("chk_account_imap")
self.label_account_folder = builder.get_object("label_account_folder")
self.entry_account_folder = builder.get_object("entry_account_folder")
self.chk_account_push = builder.get_object("chk_account_push")
self.chk_account_ssl = builder.get_object("chk_account_ssl")
self.button_save = builder.get_object("button_save")
self.cmb_account_type.set_active(0) # default to POP3
def run(self):
return self.window.run()
@@ -83,7 +88,14 @@ class AccountDialog:
self.button_save.set_sensitive(ok)
def __on_chk_account_imap_toggled(self, widget):
self.entry_account_folder.set_sensitive(self.chk_account_imap.get_active())
def __on_cmb_account_type_changed(self, widget):
if self.cmb_account_type.get_active() == 0: # POP3
self.label_account_folder.set_visible(False)
self.entry_account_folder.set_visible(False)
self.chk_account_push.set_visible(False)
else: # IMAP
self.label_account_folder.set_visible(True)
self.entry_account_folder.set_visible(True)
self.chk_account_push.set_visible(True)

View File

@@ -1,141 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# accounts.py
#
# Copyright 2011 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
# 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.
#
PACKAGE_NAME = "mailnag"
import gettext
from configuration.account import Account
gettext.bindtextdomain(PACKAGE_NAME, './locale')
gettext.textdomain(PACKAGE_NAME)
_ = gettext.gettext
class Accounts:
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
self.account.append(Account(on, name, server, user, \
password, imap, folder, port))
this_account = self.account[-1] # get last element of the list
id = this_account.id
return id
def remove(self, id): # delete account by id
for acc in self.account[:]: # iterate copy of account list
if acc.id == id: # find matching account
self.account.remove(acc) # delete it
break # stop iteration
def load(self):
self.account = [] # empty account list
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)
name_list = name.split(separator)
server_list = server.split(separator)
user_list = user.split(separator)
imap_list = imap.split(separator)
folder_list = folder.split(separator)
port_list = port.split(separator)
for i in range(len(name_list)): # iterate 0 to nr of elements in name_list
name = name_list[i]
if name == '': continue
on = int(on_list[i])
server = server_list[i]
user = user_list[i]
imap = int(imap_list[i])
folder = folder_list[i]
port = port_list[i]
if imap: protocol = 'imap'
else: protocol = 'pop'
password = self.keyring.get(protocol, user, server)
self.add(on, name, server, user, password, imap, folder, port) # fill Account list
def get(self, id): # return all data of one account
self.current = id
for acc in self.account:
if acc.id == id:
return acc
return None
def get_current(self): # return current account
if self.current != None:
for acc in self.account:
if acc.id == self.current:
return acc
return None
def get_cfg(self): # return arrays of account strings for cfg
separator = '|'
on_list = []
name_list = []
server_list = []
user_list = []
password_list = []
imap_list = []
folder_list = []
port_list = []
for acc in self.account: # collect all values
on_list.append(str(int(acc.on)))
name_list.append(acc.name)
server_list.append(acc.server)
user_list.append(acc.user)
password_list.append(acc.password)
imap_list.append(str(int(acc.imap)))
folder_list.append(acc.folder)
port_list.append(acc.port)
cfg_on = separator.join(on_list) # concatenate values
cfg_name = separator.join(name_list)
cfg_server = separator.join(server_list)
cfg_user = separator.join(user_list)
cfg_imap = separator.join(imap_list)
cfg_folder = separator.join(folder_list)
cfg_port = separator.join(port_list)
return cfg_on, cfg_name, cfg_server, cfg_user, password_list, cfg_imap, cfg_folder, cfg_port

View File

@@ -31,9 +31,9 @@ import gettext
from common.utils import get_data_file
from common.config import read_cfg, write_cfg
from common.keyring import Keyring
from common.accountlist import AccountList
from common.account import Account
from configuration.accountdialog import AccountDialog
from configuration.accounts import Accounts
locale.bindtextdomain(PACKAGE_NAME, './locale')
gettext.bindtextdomain(PACKAGE_NAME, './locale')
@@ -61,13 +61,12 @@ 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.cfg, self.keyring)
self.accounts = AccountList()
self.treeview_accounts = builder.get_object("treeview_accounts")
self.liststore_accounts = builder.get_object("liststore_accounts")
@@ -75,11 +74,6 @@ class ConfigWindow:
self.button_edit = builder.get_object("button_edit")
self.button_remove = builder.get_object("button_remove")
# colhead = [('Id'), _('Active'), _('Name')] # column headings
#
# renderer_id = Gtk.CellRendererText()
# column_id = Gtk.TreeViewColumn(colhead[0], renderer_id, text=0) # Account Id
renderer_on = Gtk.CellRendererToggle()
renderer_on.connect("toggled", self.__on_account_toggled) # bind toggle signal
column_on = Gtk.TreeViewColumn(_('Enabled'), renderer_on) # Account On/Off
@@ -135,38 +129,35 @@ class ConfigWindow:
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_playsound.set_active(bool(int(self.cfg.get('general', 'playsound'))))
self.chk_autostart.set_active(bool(int(self.cfg.get('general', 'autostart'))))
self.chk_enable_filter.set_active(int(self.cfg.get('filter', 'filter_on')))
self.chk_enable_filter.set_active(bool(int(self.cfg.get('filter', 'filter_enabled'))))
self.textbuffer_filter.set_text(self.cfg.get('filter', 'filter_text'))
self.chk_script0.set_active(int(self.cfg.get('script', 'script0_on')))
self.chk_script0.set_active(bool(int(self.cfg.get('script', 'script0_enabled'))))
tmp = self.cfg.get('script', 'script0_file')
if len(tmp) > 0:
self.filechooser_script0.set_filename(tmp)
self.chk_script1.set_active(int(self.cfg.get('script', 'script1_on')))
self.chk_script1.set_active(bool(int(self.cfg.get('script', 'script1_enabled'))))
tmp = self.cfg.get('script', 'script1_file')
if len(tmp) > 0:
self.filechooser_script1.set_filename(tmp)
self.accounts.load()
self.accounts.load_from_cfg(self.cfg)
if len(self.accounts.account) == 0:
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:
self.accounts.add(name = "%s (%s)" % (arr[1], arr[0]), \
on = 1, server = arr[0], user = arr[1], \
password = arr[2], imap = arr[3])
for acc in self.accounts.account:
row = acc.get_row()
if len(self.accounts) == 0:
self.accounts.import_from_keyring()
if len(self.accounts) > 0 and \
(not self.show_yesno_dialog(_("Mailnag found %s mail accounts on this computer.\n\nDo you want to import them?") % len(self.accounts))):
del self.accounts[:]
for acc in self.accounts:
row = [acc, acc.enabled, acc.name]
self.liststore_accounts.append(row)
self.select_path((0,))
@@ -180,34 +171,22 @@ class ConfigWindow:
autostart = self.chk_autostart.get_active()
self.cfg.set('general', 'autostart', int(autostart))
self.cfg.set('filter', 'filter_on', int(self.chk_enable_filter.get_active()))
self.cfg.set('filter', 'filter_enabled', int(self.chk_enable_filter.get_active()))
start, end = self.textbuffer_filter.get_bounds()
self.cfg.set('filter', 'filter_text', self.textbuffer_filter.get_text(start, end, True))
self.cfg.set('script', 'script0_on', int(self.chk_script0.get_active()))
self.cfg.set('script', 'script0_enabled', int(self.chk_script0.get_active()))
tmp = self.filechooser_script0.get_filename()
if tmp == None: tmp = ""
self.cfg.set('script', 'script0_file', tmp)
self.cfg.set('script', 'script1_on', int(self.chk_script1.get_active()))
self.cfg.set('script', 'script1_enabled', int(self.chk_script1.get_active()))
tmp = self.filechooser_script1.get_filename()
if tmp == None: tmp = ""
self.cfg.set('script', 'script1_file', tmp)
on, name, server, user, password, imap, folder, port = self.accounts.get_cfg()
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'
self.keyring.set(protocol, acc.user, acc.server, acc.password)
self.accounts.save_to_cfg(self.cfg)
write_cfg(self.cfg)
if autostart: self.create_autostart()
@@ -227,9 +206,9 @@ class ConfigWindow:
treeselection = self.treeview_accounts.get_selection() # get tree_selection object
selection = treeselection.get_selected() # get selected tupel (model, iter)
model, iter = selection # get selected iter
if iter != None: id = model.get_value(iter, 0) # get account_id from treeviews 1. column
else: id = None
return id, model, iter
if iter != None: acc = model.get_value(iter, 0) # get account object from treeviews 1. column
else: acc = None
return acc, model, iter
def select_path(self, path): # select path in treeview
@@ -239,18 +218,20 @@ class ConfigWindow:
def edit_account(self):
id, model, iter = self.get_selected_account()
acc, model, iter = self.get_selected_account()
if iter != None:
acc = self.accounts.get(id)
d = AccountDialog(self.window)
d.cmb_account_type.set_active(acc.imap)
d.entry_account_name.set_text(acc.name)
d.entry_account_user.set_text(acc.user)
d.entry_account_password.set_text(acc.password)
d.entry_account_server.set_text(acc.server)
d.entry_account_port.set_text(acc.port)
d.chk_account_imap.set_active(acc.imap)
d.entry_account_folder.set_text(acc.folder)
d.chk_account_push.set_active(acc.idle)
d.chk_account_ssl.set_active(acc.ssl)
res = d.run()
@@ -260,8 +241,16 @@ class ConfigWindow:
acc.password = d.entry_account_password.get_text()
acc.server = d.entry_account_server.get_text()
acc.port = d.entry_account_port.get_text()
acc.imap = d.chk_account_imap.get_active()
acc.folder = d.entry_account_folder.get_text()
acc.ssl = d.chk_account_ssl.get_active()
if d.cmb_account_type.get_active() == 0: # POP3
acc.imap = False
acc.folder = ''
acc.idle = False
else: # IMAP
acc.imap = True
acc.folder = d.entry_account_folder.get_text()
acc.idle = d.chk_account_push.get_active()
model.set_value(iter, 2, acc.name)
@@ -303,25 +292,33 @@ class ConfigWindow:
def __on_account_toggled(self, cell, path): # chk_box account_on toggled
model = self.liststore_accounts
iter = model.get_iter(path)
id = model.get_value(iter, 0)
acc = self.accounts.get(id) # get account by id
acc.on = not acc.on # update account.on
acc = model.get_value(iter, 0)
acc.enabled = not acc.enabled
self.liststore_accounts.set_value(iter, 1, not cell.get_active())
def __on_btn_add_clicked(self, widget):
d = AccountDialog(self.window)
res = d.run()
if res == 1:
id = self.accounts.add(1, d.entry_account_name.get_text(),
d.entry_account_server.get_text(), d.entry_account_user.get_text(),
d.entry_account_password.get_text(), d.chk_account_imap.get_active(),
d.entry_account_folder.get_text(), d.entry_account_port.get_text()
)
if d.cmb_account_type.get_active() == 0: # POP3
imap = False
folder = ''
idle = False
else: # IMAP
imap = True
folder = d.entry_account_folder.get_text()
idle = d.chk_account_push.get_active()
row = [id, 1, d.entry_account_name.get_text()]
acc = Account(enabled = True, name = d.entry_account_name.get_text(), \
user = d.entry_account_user.get_text(), password = d.entry_account_password.get_text(), \
server = d.entry_account_server.get_text(), port = d.entry_account_port.get_text(), \
ssl = d.chk_account_ssl.get_active(), imap = imap, idle = idle , folder = folder)
self.accounts.append(acc)
row = [acc, True, acc.name]
iter = self.liststore_accounts.append(row)
model = self.treeview_accounts.get_model()
path = model.get_path(iter)
@@ -336,11 +333,10 @@ class ConfigWindow:
def __on_btn_remove_clicked(self, widget):
id, model, iter = self.get_selected_account()
acc, model, iter = self.get_selected_account()
if iter != None:
name = model.get_value(iter, 2) # get account_name
if self.show_yesno_dialog(_('Delete this account:') + \
'\n\n' + name):
'\n\n' + acc.name):
p = model.get_path(iter)
if not p.prev():
@@ -348,7 +344,7 @@ class ConfigWindow:
self.select_path(p) # select prev/next account
model.remove(iter) # delete in treeview
self.accounts.remove(id) # delete in accounts list
self.accounts.remove(acc) # delete in accounts list
def __on_treeview_accounts_row_activated(self, treeview, path, view_column):
@@ -378,8 +374,7 @@ class ConfigWindow:
def __save_and_quit(self):
self.save_config()
self.keyring.remove(self.accounts.account) # delete obsolete entries from Keyring
self.save_config()
Gtk.main_quit()

View File

@@ -1,116 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# account.py
#
# Copyright 2011 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
# 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 time
import poplib
import daemon.imaplib2 as imaplib
class Account:
def __init__(self, check_interval, name, server, user, password, imap, folder, port):
self.check_interval = check_interval
self.name = name
self.server = server
self.user = user
self.password = password
self.imap = imap # int
self.folder = folder
self.port = port
self.mail_count = 0
def get_connection(self): # get email server connection
if self.imap: # IMAP
try:
try:
if self.port == '':
srv = imaplib.IMAP4_SSL(self.server) # SSL
else:
srv = imaplib.IMAP4_SSL(self.server, self.port)
except:
if self.port == '':
srv = imaplib.IMAP4(self.server) # non SSL
else:
srv = imaplib.IMAP4(self.server, self.port)
srv.login(self.user, self.password)
except:
print "Warning: Cannot connect to IMAP account: %s. " \
"Next try in 30 seconds." % self.server
time.sleep(30) # wait 30 seconds
try:
try:
if self.port == '':
srv = imaplib.IMAP4_SSL(self.server) # SSL
else:
srv = imaplib.IMAP4_SSL(self.server, self.port)
except:
if self.port == '':
srv = imaplib.IMAP4(self.server) # non SSL
else:
srv = imaplib.IMAP4(self.server, self.port)
srv.login(self.user, self.password)
except:
print "Error: Cannot connect to IMAP account: %s. " \
"Next try in %s minutes." % (self.server, self.check_interval)
srv = None
else: # POP
try:
try:
if self.port == '':
srv = poplib.POP3_SSL(self.server) # connect to Email-Server via SSL
else:
srv = poplib.POP3_SSL(self.server, self.port)
except:
if self.port == '':
srv = poplib.POP3(self.server) # non SSL
else:
srv = poplib.POP3(self.server, self.port)
srv.getwelcome()
srv.user(self.user)
srv.pass_(self.password)
except:
print "Warning: Cannot connect to POP account: %s. " \
"Next try in 30 seconds." % self.server
time.sleep(30) # wait 30 seconds
try:
try:
if self.port == '':
srv = poplib.POP3_SSL(self.server) # try it again
else:
srv = poplib.POP3_SSL(self.server, self.port)
except:
if self.port == '':
srv = poplib.POP3(self.server) # non SSL
else:
srv = poplib.POP3(self.server, self.port)
srv.getwelcome()
srv.user(self.user)
srv.pass_(self.password)
except:
print "Error: Cannot connect to POP account: %s. " \
"Next try in %s minutes." % (self.server, self.check_interval)
srv = None
return srv # server object

View File

@@ -1,71 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# accounts.py
#
# Copyright 2011 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
# 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 common.keyring import Keyring
from daemon.account import Account
class Accounts:
def __init__(self, cfg):
self.account = []
keyring = Keyring()
self.keyring_was_locked = keyring.was_locked
separator = '|'
on_list = cfg.get('account', 'on').split(separator)
name_list = cfg.get('account', 'name').split(separator)
server_list = cfg.get('account', 'server').split(separator)
user_list = cfg.get('account', 'user').split(separator)
imap_list = cfg.get('account', 'imap').split(separator)
folder_list = cfg.get('account', 'folder').split(separator)
port_list = cfg.get('account', 'port').split(separator)
check_interval = cfg.get('general', 'check_interval')
# check if the account list is empty
if len(name_list) == 1 and name_list[0] == '':
return
for i in range(len(name_list)): # iterate 0 to nr of elements in name_list
on = int(on_list[i])
name = name_list[i]
if not on or name == '': continue # ignore accounts that are off or have no name
server = server_list[i]
user = user_list[i]
imap = int(imap_list[i])
folder = folder_list[i]
port = port_list[i]
if imap: protocol = 'imap'
else: protocol = 'pop'
password = keyring.get(protocol, user, server)
self.account.append(Account(check_interval, name, server, user, password, imap, folder, port))
def get_count(self, name): # get number of emails for this provider
count = 'error'
for acc in self.account:
if acc.name == name:
count = str(acc.mail_count)
break
if count == 'error':
print 'Cannot find account (get_count)'
return count

View File

@@ -32,8 +32,8 @@ class Idlers:
def run(self):
for acc in self._accounts.account:
if acc.imap: # TODO : and enable_push
for acc in self._accounts:
if acc.imap and acc.idle:
try:
self._new_idler(acc)
except:
@@ -52,7 +52,7 @@ class Idlers:
return
# Need to get out of AUTH mode.
if account.folder:
if len(account.folder) > 0:
server.select(account.folder)
else:
server.select("INBOX")

View File

@@ -187,14 +187,14 @@ class MailChecker:
def run_user_scripts(self, event, data):
if event == "on_mail_check":
if self.cfg.get('script', 'script0_on') == '1':
if self.cfg.get('script', 'script0_enabled') == '1':
script_file = self.cfg.get('script', 'script0_file')
if script_file != '' and os.path.exists(script_file):
self.pid.append(subprocess.Popen("%s %s" % (script_file, data), shell = True))
else:
print 'Warning: cannot execute script:', script_file
if (data != '0') and (self.cfg.get('script', 'script1_on') == '1'):
if (data != '0') and (self.cfg.get('script', 'script1_enabled') == '1'):
script_file = self.cfg.get('script', 'script1_file')
if script_file != '' and os.path.exists(script_file):
self.pid.append(subprocess.Popen("%s %s" % (script_file, data), shell = True))

View File

@@ -41,9 +41,9 @@ class Mails:
mail_list = [] # initialize list of mails
mail_ids = [] # initialize list of mail ids
while not self.is_online(): time.sleep(5) # wait for internet connection
filter_on = int(self.cfg.get('filter', 'filter_on')) # get filter switch
filter_enabled = bool(int(self.cfg.get('filter', 'filter_enabled'))) # get filter switch
for acc in self.accounts.account: # loop all email accounts
for acc in self.accounts:
srv = acc.get_connection() # get server connection for this account
if srv == None:
continue # continue with next account if server is empty
@@ -116,7 +116,7 @@ class Mails:
id = str(hash(subject)) # create emergency id
if id not in mail_ids: # prevent duplicates caused by Gmail labels
if not (filter_on and self.in_filter(sender + subject)): # check filter
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
@@ -178,7 +178,7 @@ class Mails:
uidl = str(hash(subject)) # create emergency id
id = acc.user + uidl.split(' ')[2] # create unique id
if not (filter_on and self.in_filter(sender + subject)): # check filter
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

View File

@@ -29,7 +29,7 @@ import signal
from common.config import read_cfg, cfg_exists, cfg_folder
from common.utils import set_procname
from daemon.accounts import Accounts
from common.accountlist import AccountList
from daemon.mailchecker import MailChecker
from daemon.idlers import Idlers
@@ -91,14 +91,20 @@ def main():
print 'Error: Cannot find configuration file. Please run mailnag_config first.'
exit(1)
accounts = Accounts(cfg)
accounts = AccountList()
accounts.load_from_cfg(cfg, enabled_only = True)
mailchecker = MailChecker(cfg, accounts)
mailchecker.check(True) # immediate check, firstcheck=True
check_interval = int(cfg.get('general', 'check_interval'))
GObject.timeout_add_seconds(60 * check_interval, mailchecker.check)
# 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:
mailchecker.check(True) # immediate check, firstcheck=True
check_interval = int(cfg.get('general', 'check_interval'))
GObject.timeout_add_seconds(60 * check_interval, mailchecker.check)
if False:
# 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)
idlers.run()

View File

@@ -67,67 +67,25 @@
<property name="margin_top">6</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<property name="n_rows">7</property>
<property name="n_rows">9</property>
<property name="n_columns">2</property>
<child>
<object class="GtkAlignment" id="alignment4">
<object class="GtkAlignment" id="alignment8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label11">
<object class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Accountname:</property>
<property name="label" translatable="yes">Port:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">User:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Password:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="top_attach">5</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@@ -146,27 +104,6 @@
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Port (optional):</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
@@ -175,23 +112,86 @@
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_name">
<object class="GtkAlignment" id="alignment6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<signal name="changed" handler="entry_changed" swapped="no"/>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Password:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_user">
<object class="GtkAlignment" id="alignment5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">User:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label11">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Accountname:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_port">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<property name="placeholder_text">optional</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_server">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
@@ -201,7 +201,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="top_attach">4</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@@ -218,13 +218,13 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_server">
<object class="GtkEntry" id="entry_account_user">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
@@ -234,59 +234,34 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry_account_port">
<object class="GtkEntry" id="entry_account_name">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<signal name="changed" handler="entry_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="top_padding">12</property>
<child>
<object class="GtkCheckButton" id="chk_account_imap">
<property name="label" translatable="yes">IMAP</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="chk_account_imap_toggled" swapped="no"/>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment10">
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="left_padding">24</property>
<child>
<object class="GtkLabel" id="label17">
<object class="GtkLabel" id="label_account_folder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
@@ -304,11 +279,11 @@
<child>
<object class="GtkEntry" id="entry_account_folder">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<property name="placeholder_text">optional</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -317,9 +292,81 @@
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="chk_account_push">
<property name="label" translatable="yes">Enable Push-IMAP</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Account type:</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="cmb_account_type">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item>POP3</item>
<item>IMAP</item>
</items>
<signal name="changed" handler="account_type_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkCheckButton" id="chk_account_ssl">
<property name="label" translatable="yes">Enable SSL encryption</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>

View File

@@ -623,9 +623,9 @@ Copyright (c) 2011 Ralf Hersel</property>
</object>
<object class="GtkListStore" id="liststore_accounts">
<columns>
<!-- column-name account_id -->
<column type="gchararray"/>
<!-- column-name account_on -->
<!-- column-name account_obj -->
<column type="PyObject"/>
<!-- column-name account_enabled -->
<column type="gboolean"/>
<!-- column-name account_name -->
<column type="gchararray"/>