package refactoring

- reorganize classes
- make it run even when pysqlite2 is not installed
- some tests to complete mailconnection_factory coverage

darcs-hash:20060724214007-86b55-9b38308b8f645c2067c7b200f17532da62ec825c.gz
This commit is contained in:
David Rousselie
2006-07-24 23:40:07 +02:00
parent 3a5c32041a
commit dfb597aca3
21 changed files with 269 additions and 185 deletions

0
src/jmc/__init__.py Normal file
View File

View File

View File

@@ -0,0 +1,479 @@
##
## mailconnection.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Fri Jan 7 11:06:42 2005
## $Id: mailconnection.py,v 1.11 2005/09/18 20:24:07 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import sys
import logging
import email
import email.Header
import traceback
import poplib
import imaplib
import socket
from jmc.utils.lang import Lang
IMAP4_TIMEOUT = 10
POP3_TIMEOUT = 10
DO_NOTHING = 0
DIGEST = 1
RETRIEVE = 2
default_encoding = "iso-8859-1"
## All MY* classes are implemented to add a timeout (settimeout)
## while connecting
class MYIMAP4(imaplib.IMAP4):
def open(self, host = '', port = imaplib.IMAP4_PORT):
"""Setup connection to remote server on "host:port"
(default: localhost:standard IMAP4 port).
This connection will be used by the routines:
read, readline, send, shutdown.
"""
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(IMAP4_TIMEOUT)
self.sock.connect((host, port))
self.sock.settimeout(None)
self.file = self.sock.makefile('rb')
class MYIMAP4_SSL(imaplib.IMAP4_SSL):
def open(self, host = '', port = imaplib.IMAP4_SSL_PORT):
"""Setup connection to remote server on "host:port".
(default: localhost:standard IMAP4 SSL port).
This connection will be used by the routines:
read, readline, send, shutdown.
"""
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(IMAP4_TIMEOUT)
self.sock.connect((host, port))
self.sock.settimeout(None)
self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
class MYPOP3(poplib.POP3):
def __init__(self, host, port = poplib.POP3_PORT):
self.host = host
self.port = port
msg = "getaddrinfo returns an empty list"
self.sock = None
for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
self.sock = socket.socket(af, socktype, proto)
self.sock.settimeout(POP3_TIMEOUT)
self.sock.connect(sa)
self.sock.settimeout(None)
except socket.error, msg:
if self.sock:
self.sock.close()
self.sock = None
continue
break
if not self.sock:
raise socket.error, msg
self.file = self.sock.makefile('rb')
self._debugging = 0
self.welcome = self._getresp()
class MYPOP3_SSL(poplib.POP3_SSL):
def __init__(self, host, port = poplib.POP3_SSL_PORT, keyfile = None, certfile = None):
self.host = host
self.port = port
self.keyfile = keyfile
self.certfile = certfile
self.buffer = ""
msg = "getaddrinfo returns an empty list"
self.sock = None
for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
self.sock = socket.socket(af, socktype, proto)
self.sock.settimeout(POP3_TIMEOUT)
self.sock.connect(sa)
self.sock.settimeout(None)
except socket.error, msg:
if self.sock:
self.sock.close()
self.sock = None
continue
break
if not self.sock:
raise socket.error, msg
self.file = self.sock.makefile('rb')
self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
self._debugging = 0
self.welcome = self._getresp()
class MailConnection(object):
""" Wrapper to mail connection and action.
Abstract class, do not represent real mail connection type"""
_logger = logging.getLogger("jmc.MailConnection")
def __init__(self, login = "", password = "", host = "", \
port = 110, ssl = False):
""" Initialize MailConnection object for common parameters of all
connections types
:Parameters:
- 'login': login used to connect mail server
- 'password': password associated with 'login' to connect mail server
- 'host': mail server hostname
- 'port': mail server port
- 'ssl': activate ssl to connect server
:Types:
- 'login': string
- 'password': string
- 'host': string
- 'port': int
- 'ssl': boolean"""
self.login = login
self.password = password
self.store_password = True
self.host = host
self.port = port
self.ssl = ssl
self.status = "offline"
self.connection = None
self.chat_action = RETRIEVE
self.online_action = RETRIEVE
self.away_action = RETRIEVE
self.xa_action = RETRIEVE
self.dnd_action = RETRIEVE
self.offline_action = DO_NOTHING
self.interval = 5
self.lastcheck = 0
self.default_lang_class = Lang.en
self.waiting_password_reply = False
self.in_error = False
self.live_email_only = False
self.first_check = True
def __eq__(self, other):
return self.get_type() == other.get_type() \
and self.login == other.login \
and (not self.store_password or self.password == other.password) \
and self.store_password == other.store_password \
and self.host == other.host \
and self.port == other.port \
and self.ssl == other.ssl \
and self.chat_action == other.chat_action \
and self.online_action == other.online_action \
and self.away_action == other.away_action \
and self.xa_action == other.xa_action \
and self.dnd_action == other.dnd_action \
and self.offline_action == other.offline_action \
and self.interval == other.interval \
and self.live_email_only == other.live_email_only
def __str__(self):
return self.get_type() + "#" + self.login + "#" + \
(self.store_password and self.password or "/\\") + "#" \
+ self.host + "#" + str(self.port) + "#" + str(self.chat_action) + "#" \
+ str(self.online_action) + "#" + str(self.away_action) + "#" + \
str(self.xa_action) + "#" + str(self.dnd_action) + "#" + \
str(self.offline_action) + "#" + str(self.interval) + "#" + str(self.live_email_only)
def get_decoded_part(self, part, charset_hint):
content_charset = part.get_content_charset()
result = u""
try:
if content_charset:
result = unicode(part.get_payload(decode=True).decode(content_charset))
else:
result = unicode(part.get_payload(decode=True))
except Exception, e:
try:
result = unicode(part.get_payload(decode=True).decode("iso-8859-1"))
except Exception, e:
try:
result = unicode(part.get_payload(decode=True).decode(default_encoding))
except Exception, e:
if charset_hint is not None:
try:
result = unicode(part.get_payload(decode=True).decode(charset_hint))
except Exception, e:
type, value, stack = sys.exc_info()
print >>sys.stderr, "".join(traceback.format_exception
(type, value, stack, 5))
return result
def format_message(self, email_msg, include_body = True):
from_decoded = email.Header.decode_header(email_msg["From"])
charset_hint = None
email_from = u""
result = u"From : "
for i in range(len(from_decoded)):
try:
if from_decoded[i][1]:
charset_hint = from_decoded[i][1]
email_from += unicode(from_decoded[i][0].decode(from_decoded[i][1]))
else:
email_from += unicode(from_decoded[i][0])
except Exception,e:
try:
email_from += unicode(from_decoded[i][0].decode("iso-8859-1"))
except Exception, e:
try:
email_from += unicode(from_decoded[i][0].decode(default_encoding))
except Exception, e:
type, value, stack = sys.exc_info()
print >>sys.stderr, "".join(traceback.format_exception
(type, value, stack, 5))
result += email_from + u"\n"
subject_decoded = email.Header.decode_header(email_msg["Subject"])
result += u"Subject : "
for i in range(len(subject_decoded)):
try:
if subject_decoded[i][1]:
charset_hint = subject_decoded[i][1]
result += unicode(subject_decoded[i][0].decode(subject_decoded[i][1]))
else:
result += unicode(subject_decoded[i][0])
except Exception,e:
try:
result += unicode(subject_decoded[i][0].decode("iso-8859-1"))
except Exception, e:
try:
result += unicode(subject_decoded[i][0].decode(default_encoding))
except Exception, e:
if charset_hint is not None:
try:
result += unicode(subject_decoded[i][0].decode(charset_hint))
except Exception, e:
type, value, stack = sys.exc_info()
print >>sys.stderr, "".join(traceback.format_exception
(type, value, stack, 5))
result += u"\n\n"
if include_body:
action = {
"text/plain" : lambda part: self.get_decoded_part(part, charset_hint),
"text/html" : lambda part: "\n<<<HTML part skipped>>>\n"
}
for part in email_msg.walk():
content_type = part.get_content_type()
if action.has_key(content_type):
result += action[content_type](part) + u'\n'
return (result, email_from)
def format_message_summary(self, email_msg):
return self.format_message(email_msg, False)
def get_status_msg(self):
return self.get_type() + "://" + self.login + "@" + self.host + ":" + \
unicode(self.port)
def connect(self):
pass
def disconnect(self):
pass
def get_mail_list(self):
return 0
def get_mail(self, index):
return None
def get_mail_summary(self, index):
return None
def get_next_mail_index(self, mail_list):
pass
# Does not modify server state but just internal JMC state
def mark_all_as_read(self):
pass
def get_action(self):
mapping = {"online": self.online_action,
"chat": self.chat_action,
"away": self.away_action,
"xa": self.xa_action,
"dnd": self.dnd_action,
"offline": self.offline_action}
if mapping.has_key(self.status):
return mapping[self.status]
return DO_NOTHING
action = property(get_action)
class IMAPConnection(MailConnection):
_logger = logging.getLogger("jmc.IMAPConnection")
def __init__(self, login = "", password = "", host = "", \
port = None, ssl = False, mailbox = "INBOX"):
if not port:
if ssl:
port = 993
else:
port = 143
MailConnection.__init__(self, login, password, host, port, ssl)
self.mailbox = mailbox
def __eq__(self, other):
return MailConnection.__eq__(self, other) \
and self.mailbox == other.mailbox
def __str__(self):
return MailConnection.__str__(self) + "#" + self.mailbox
def get_type(self):
if self.ssl:
return "imaps"
return "imap"
def get_status(self):
return MailConnection.get_status(self) + "/" + self.mailbox
def connect(self):
IMAPConnection._logger.debug("Connecting to IMAP server " \
+ self.login + "@" + self.host + ":" + str(self.port) \
+ " (" + self.mailbox + "). SSL=" \
+ str(self.ssl))
if self.ssl:
self.connection = MYIMAP4_SSL(self.host, self.port)
else:
self.connection = MYIMAP4(self.host, self.port)
self.connection.login(self.login, self.password)
def disconnect(self):
IMAPConnection._logger.debug("Disconnecting from IMAP server " \
+ self.host)
self.connection.logout()
def get_mail_list(self):
IMAPConnection._logger.debug("Getting mail list")
typ, data = self.connection.select(self.mailbox)
typ, data = self.connection.search(None, 'RECENT')
if typ == 'OK':
return data[0].split(' ')
return None
def get_mail(self, index):
IMAPConnection._logger.debug("Getting mail " + str(index))
typ, data = self.connection.select(self.mailbox, True)
typ, data = self.connection.fetch(index, '(RFC822)')
if typ == 'OK':
return self.format_message(email.message_from_string(data[0][1]))
return u"Error while fetching mail " + str(index)
def get_mail_summary(self, index):
IMAPConnection._logger.debug("Getting mail summary " + str(index))
typ, data = self.connection.select(self.mailbox, True)
typ, data = self.connection.fetch(index, '(RFC822)')
if typ == 'OK':
return self.format_message_summary(email.message_from_string(data[0][1]))
return u"Error while fetching mail " + str(index)
def get_next_mail_index(self, mail_list):
if not mail_list or mail_list[0] == '':
return None
return mail_list.pop(0)
def mark_all_as_read(self):
self.get_mail_list()
type = property(get_type)
class POP3Connection(MailConnection):
_logger = logging.getLogger("jmc.POP3Connection")
def __init__(self, login = "", password = "", host = "", \
port = None, ssl = False):
if not port:
if ssl:
port = 995
else:
port = 110
MailConnection.__init__(self, login, password, host, port, ssl)
self.__nb_mail = 0
self.__lastmail = 0
def get_type(self):
if self.ssl:
return "pop3s"
return "pop3"
def connect(self):
POP3Connection._logger.debug("Connecting to POP3 server " \
+ self.login + "@" + self.host + ":" + str(self.port)\
+ ". SSL=" + str(self.ssl))
if self.ssl:
self.connection = MYPOP3_SSL(self.host, self.port)
else:
self.connection = MYPOP3(self.host, self.port)
try:
self.connection.apop(self.login, self.password)
except:
self.connection.user(self.login)
self.connection.pass_(self.password)
def disconnect(self):
POP3Connection._logger.debug("Disconnecting from POP3 server " \
+ self.host)
self.connection.quit()
def get_mail_list(self):
POP3Connection._logger.debug("Getting mail list")
count, size = self.connection.stat()
self.__nb_mail = count
return [str(i) for i in range(1, count + 1)]
def get_mail(self, index):
POP3Connection._logger.debug("Getting mail " + str(index))
ret, data, size = self.connection.retr(index)
if ret[0:3] == '+OK':
return self.format_message(email.message_from_string('\n'.join(data)))
return u"Error while fetching mail " + str(index)
def get_mail_summary(self, index):
POP3Connection._logger.debug("Getting mail summary " + str(index))
ret, data, size = self.connection.retr(index)
if ret[0:3] == '+OK':
return self.format_message_summary(email.message_from_string('\n'.join(data)))
return u"Error while fetching mail " + str(index)
def get_next_mail_index(self, mail_list):
if self.__nb_mail == self.__lastmail:
return None
if self.__nb_mail < self.__lastmail:
self.__lastmail = 0
result = int(mail_list[self.__lastmail])
self.__lastmail += 1
return result
def mark_all_as_read(self):
self.get_mail_list()
self.__lastmail = self.__nb_mail
type = property(get_type)

View File

@@ -0,0 +1,142 @@
##
## mailconnection_factory.py
## Login : David Rousselie <david.rousselie@happycoders.org>
## Started on Fri May 20 10:41:46 2005 David Rousselie
## $Id: mailconnection_factory.py,v 1.2 2005/09/18 20:24:07 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import jmc.email.mailconnection as mailconnection
from jmc.email.mailconnection import IMAPConnection, POP3Connection
def get_new_mail_connection(type):
""" Static method to return an empty MailConnection object of given type
:Parameters:
- 'type': type of connection to return : 'imap', 'imaps', 'pop3', 'pop3s'
:return: MailConnection of given type in parameter, None if unknown type
:returntype: 'MailConnection'
"""
if type == "imap":
return IMAPConnection()
elif type == "imaps":
return IMAPConnection(ssl = True)
elif type == "pop3":
return POP3Connection()
elif type == "pop3s":
return POP3Connection(ssl = True)
raise Exception, "Connection type \"" + type + "\" unknown"
def str_to_mail_connection(connection_string):
""" Static methode to create a MailConnection object filled from string
:Parameters:
- 'connection_string': string containing MailConnection parameters separated
by '#'. ex: 'pop3#login#password#host#110#chat_action#online_action#away_action#xa_action#dnd_action#offline_action#check_interval#liv_email_only(#Mailbox)'
:Types:
- 'connection_string': string
:return: MailConnection of given type found in string parameter
:returntype: 'MailConnection'
"""
arg_list = connection_string.split("#")
# optionals values must be the at the beginning of the list to pop them
# last
arg_list.reverse()
type = arg_list.pop()
login = arg_list.pop()
password = arg_list.pop()
if password == "/\\":
password = None
store_password = False
else:
store_password = True
host = arg_list.pop()
port = int(arg_list.pop())
chat_action = None
online_action = None
away_action = None
xa_action = None
dnd_action = None
offline_action = None
interval = None
live_email_only = False
result = None
if type[0:4] == "imap":
if len(arg_list) == 9:
chat_action = int(arg_list.pop())
online_action = int(arg_list.pop())
away_action = int(arg_list.pop())
xa_action = int(arg_list.pop())
dnd_action = int(arg_list.pop())
offline_action = int(arg_list.pop())
interval = int(arg_list.pop())
live_email_only = (arg_list.pop().lower() == "true")
else:
retrieve = bool(arg_list.pop() == "True")
if retrieve:
chat_action = online_action = away_action = xa_action = dnd_action = mailconnection.RETRIEVE
else:
chat_action = online_action = away_action = xa_action = dnd_action = mailconnection.DIGEST
offline_action = mailconnection.DO_NOTHING
mailbox = arg_list.pop()
result = IMAPConnection(login = login, \
password = password, \
host = host, \
ssl = (len(type) == 5), \
port = port, \
mailbox = mailbox)
elif type[0:4] == "pop3":
if len(arg_list) == 8:
chat_action = int(arg_list.pop())
online_action = int(arg_list.pop())
away_action = int(arg_list.pop())
xa_action = int(arg_list.pop())
dnd_action = int(arg_list.pop())
offline_action = int(arg_list.pop())
interval = int(arg_list.pop())
live_email_only = (arg_list.pop().lower() == "true")
else:
retrieve = bool(arg_list.pop() == "True")
if retrieve:
chat_action = online_action = away_action = xa_action = dnd_action = mailconnection.RETRIEVE
else:
chat_action = online_action = away_action = xa_action = dnd_action = mailconnection.DIGEST
offline_action = mailconnection.DO_NOTHING
result = POP3Connection(login = login, \
password = password, \
host = host, \
port = port, \
ssl = (len(type) == 5))
if result is None:
raise Exception, "Connection type \"" + type + "\" unknown"
result.store_password = store_password
result.chat_action = chat_action
result.online_action = online_action
result.away_action = away_action
result.xa_action = xa_action
result.dnd_action = dnd_action
result.offline_action = offline_action
if interval is not None:
result.interval = interval
result.live_email_only = live_email_only
return result

View File

940
src/jmc/jabber/component.py Normal file
View File

@@ -0,0 +1,940 @@
# -*- coding: UTF-8 -*-
##
## component.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Fri Jan 7 11:06:42 2005
## $Id: component.py,v 1.12 2005/09/18 20:24:07 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import re
import signal
import threading
import thread
import logging
import sys
import anydbm
import os
import time
import traceback
from jmc.email.mailconnection import *
from jmc.jabber.x import *
from jmc.utils.storage import *
from jmc.utils.lang import Lang
import jmc.email.mailconnection_factory
import pyxmpp.jabberd
from pyxmpp.presence import Presence
from pyxmpp.message import Message
from pyxmpp.streambase import StreamError, FatalStreamError
from pyxmpp.jid import JID
from pyxmpp.jabber.disco import DiscoItems, DiscoItem, DiscoInfo, DiscoIdentity
from pyxmpp.jabberd.component import Component
class ComponentFatalError(RuntimeError):
pass
class MailComponent(Component):
def __init__(self,
jid,
secret,
server,
port,
default_lang,
check_interval,
spool_dir,
storage,
name):
Component.__init__(self, \
JID(jid), \
secret, \
server, \
port, \
disco_category = "gateway", \
disco_type = "headline")
self.__logger = logging.getLogger("jmc.jabber.Component")
self.__shutdown = 0
self.__lang = Lang(default_lang)
self.__name = name
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
self.__interval = check_interval
spool_dir += "/" + jid
try:
self.__storage = globals()[storage \
+ "Storage"](2, spool_dir = spool_dir)
except:
print >>sys.stderr, "Cannot find " \
+ storage + "Storage class"
sys.exit(1)
# dump registered accounts (save) every hour
self.__count = 60
self.running = False
def __del__(self):
logging.shutdown()
""" Register Form creator """
def get_reg_form(self, lang_class):
reg_form = X()
reg_form.xmlns = "jabber:x:data"
reg_form.title = lang_class.register_title
reg_form.instructions = lang_class.register_instructions
reg_form.type = "form"
reg_form.add_field(type = "text-single", \
label = lang_class.account_name, \
var = "name")
reg_form.add_field(type = "text-single", \
label = lang_class.account_login, \
var = "login")
reg_form.add_field(type = "text-private", \
label = lang_class.account_password, \
var = "password")
reg_form.add_field(type = "boolean", \
label = lang_class.account_password_store, \
var = "store_password",
value = "1")
reg_form.add_field(type = "text-single", \
label = lang_class.account_host, \
var = "host")
reg_form.add_field(type = "text-single", \
label = lang_class.account_port, \
var = "port")
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_type, \
var = "type")
field.add_option(label = "POP3", \
value = "pop3")
field.add_option(label = "POP3S", \
value = "pop3s")
field.add_option(label = "IMAP", \
value = "imap")
field.add_option(label = "IMAPS", \
value = "imaps")
reg_form.add_field(type = "text-single", \
label = lang_class.account_mailbox, \
var = "mailbox", \
value = "INBOX")
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_ffc_action, \
var = "chat_action", \
value = str(mailconnection.RETRIEVE))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_online_action, \
var = "online_action", \
value = str(mailconnection.RETRIEVE))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_away_action, \
var = "away_action", \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_xa_action, \
var = "xa_action", \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_dnd_action, \
var = "dnd_action", \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form.add_field(type = "list-single", \
label = lang_class.account_offline_action, \
var = "offline_action", \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
# default interval in config file
reg_form.add_field(type = "text-single", \
label = lang_class.account_check_interval, \
var = "interval", \
value = unicode(self.__interval))
reg_form.add_field(type = "boolean", \
label = lang_class.account_live_email_only, \
var = "live_email_only")
return reg_form
""" Register Form modifier for existing accounts """
def get_reg_form_init(self, lang_class, jid, name):
if not self.__storage.has_key((jid, name)):
return None
account = self.__storage[(jid, name)]
reg_form_init = X()
reg_form_init.xmlns = "jabber:x:data"
reg_form_init.title = lang_class.update_title
reg_form_init.instructions = lang_class.update_instructions % \
(name,)
reg_form_init.type = "form"
reg_form_init.add_field(type = "hidden", \
label = lang_class.account_name, \
var = "name", \
value = name)
reg_form_init.add_field(type = "text-single", \
label = lang_class.account_login, \
var = "login", \
value = account.login)
reg_form_init.add_field(type = "text-private", \
label = lang_class.account_password, \
var = "password", \
value = account.password)
reg_form_init.add_field(type = "boolean", \
label = lang_class.account_password_store, \
var = "store_password", \
value = str(account.store_password).lower())
reg_form_init.add_field(type = "text-single", \
label = lang_class.account_host, \
var = "host", \
value = account.host)
reg_form_init.add_field(type = "text-single", \
label = lang_class.account_port, \
var = "port", \
value = unicode(account.port))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_type, \
var = "type", \
value = account.get_type())
if account.get_type()[0:4] == "imap":
field.add_option(label = "IMAP", \
value = "imap")
field.add_option(label = "IMAPS", \
value = "imaps")
field = reg_form_init.add_field(type = "text-single", \
label = lang_class.account_mailbox, \
var = "mailbox")
field.value = account.mailbox
else:
field.add_option(label = "POP3", \
value = "pop3")
field.add_option(label = "POP3S", \
value = "pop3s")
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_ffc_action, \
var = "chat_action", \
value = str(account.chat_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_online_action, \
var = "online_action", \
value = str(account.online_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_away_action, \
var = "away_action", \
value = str(account.away_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_xa_action, \
var = "xa_action", \
value = str(account.xa_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_dnd_action, \
var = "dnd_action", \
value = str(account.dnd_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
field = reg_form_init.add_field(type = "list-single", \
label = lang_class.account_offline_action, \
var = "offline_action", \
value = str(account.offline_action))
field.add_option(label = lang_class.action_nothing, \
value = str(mailconnection.DO_NOTHING))
field.add_option(label = lang_class.action_digest, \
value = str(mailconnection.DIGEST))
field.add_option(label = lang_class.action_retrieve, \
value = str(mailconnection.RETRIEVE))
reg_form_init.add_field(type = "text-single", \
label = lang_class.account_check_interval, \
var = "interval", \
value = str(account.interval))
reg_form_init.add_field(type = "boolean", \
label = lang_class.account_live_email_only, \
var = "live_email_only", \
value = str(account.live_email_only).lower())
return reg_form_init
""" Looping method """
def run(self, timeout):
self.connect()
self.running = True
thread.start_new_thread(self.time_handler, ())
try:
while (not self.__shutdown and self.stream
and not self.stream.eof and self.stream.socket is not None):
try:
self.stream.loop_iter(timeout)
except (KeyboardInterrupt, SystemExit, FatalStreamError, \
StreamError):
raise
except:
self.__logger.exception("Exception cought:")
finally:
## TODO : for jid in self.__storage.keys(())
## for name in self.__storage.keys((jid,))
self.running = False
if self.stream:
for jid in self.__storage.keys(()):
p = Presence(from_jid = unicode(self.jid), to_jid = jid, \
stanza_type = "unavailable")
self.stream.send(p)
for jid, name in self.__storage.keys():
if self.__storage[(jid, name)].status != "offline":
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = jid, \
stanza_type = "unavailable")
self.stream.send(p)
threads = threading.enumerate()
for th in threads:
try:
th.join(10 * timeout)
except:
pass
for th in threads:
try:
th.join(timeout)
except:
pass
self.disconnect()
del self.__storage
self.__logger.debug("Exitting normally")
""" Stop method handler """
def signal_handler(self, signum, frame):
self.__logger.debug("Signal %i received, shutting down..." % (signum,))
self.__shutdown = 1
""" SIGALRM signal handler """
def time_handler(self):
self.__logger.debug("Check mail thread started...")
while self.running:
self.check_all_mail()
self.__logger.debug("Resetting alarm signal")
if self.__count == 0:
self.__logger.debug("Dumping registered accounts Database")
self.__storage.sync()
self.__count = 60
else:
self.__count -= 1
time.sleep(60)
""" Component authentication handler """
def authenticated(self):
self.__logger.debug("AUTHENTICATED")
Component.authenticated(self)
for jid in self.__storage.keys(()):
p = Presence(from_jid = unicode(self.jid), \
to_jid = jid, stanza_type = "probe")
self.stream.send(p)
for jid, name in self.__storage.keys():
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = jid, stanza_type = "probe")
self.stream.send(p)
self.stream.set_iq_get_handler("query", "jabber:iq:version", \
self.get_version)
self.stream.set_iq_get_handler("query", "jabber:iq:register", \
self.get_register)
self.stream.set_iq_set_handler("query", "jabber:iq:register", \
self.set_register)
self.stream.set_presence_handler("available", \
self.presence_available)
self.stream.set_presence_handler("probe", \
self.presence_available)
self.stream.set_presence_handler("unavailable", \
self.presence_unavailable)
self.stream.set_presence_handler("unsubscribe", \
self.presence_unsubscribe)
self.stream.set_presence_handler("unsubscribed", \
self.presence_unsubscribed)
self.stream.set_presence_handler("subscribe", \
self.presence_subscribe)
self.stream.set_presence_handler("subscribed", \
self.presence_subscribed)
self.stream.set_message_handler("normal", \
self.message)
def stream_state_changed(self,state,arg):
self.__logger.debug("*** State changed: %s %r ***" % (state,arg))
""" Discovery get info handler """
def disco_get_info(self, node, iq):
self.__logger.debug("DISCO_GET_INFO")
di = DiscoInfo()
if node is None:
di.add_feature("jabber:iq:version")
di.add_feature("jabber:iq:register")
DiscoIdentity(di, self.__name, "headline", "newmail")
else:
di.add_feature("jabber:iq:register")
return di
""" Discovery get nested nodes handler """
def disco_get_items(self, node, iq):
self.__logger.debug("DISCO_GET_ITEMS")
lang_class = self.__lang.get_lang_class_from_node(iq.get_node())
base_from_jid = unicode(iq.get_from().bare())
di = DiscoItems()
if not node:
for name in self.__storage.keys((base_from_jid,)):
account = self.__storage[(base_from_jid, name)]
str_name = lang_class.connection_label % (account.get_type().upper(), name)
if account.get_type()[0:4] == "imap":
str_name += " (" + account.mailbox + ")"
DiscoItem(di, JID(name + "@" + unicode(self.jid)), \
name, str_name)
return di
""" Get Version handler """
def get_version(self, iq):
self.__logger.debug("GET_VERSION")
iq = iq.make_result_response()
q = iq.new_query("jabber:iq:version")
q.newTextChild(q.ns(), "name", self.__name)
q.newTextChild(q.ns(), "version", "0.2.1")
self.stream.send(iq)
return 1
""" Send back register form to user """
def get_register(self, iq):
self.__logger.debug("GET_REGISTER")
lang_class = self.__lang.get_lang_class_from_node(iq.get_node())
base_from_jid = unicode(iq.get_from().bare())
to = iq.get_to()
iq = iq.make_result_response()
q = iq.new_query("jabber:iq:register")
if to and to != self.jid:
self.get_reg_form_init(lang_class,
base_from_jid,
to.node).attach_xml(q)
else:
self.get_reg_form(lang_class).attach_xml(q)
self.stream.send(iq)
return 1
""" Handle user registration response """
def set_register(self, iq):
self.__logger.debug("SET_REGISTER")
lang_class = self.__lang.get_lang_class_from_node(iq.get_node())
to = iq.get_to()
from_jid = iq.get_from()
base_from_jid = unicode(from_jid.bare())
remove = iq.xpath_eval("r:query/r:remove", \
{"r" : "jabber:iq:register"})
if remove:
for name in self.__storage.keys((base_from_jid,)):
self.__logger.debug("Deleting " + name + " for " + base_from_jid)
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = from_jid, \
stanza_type = "unsubscribe")
self.stream.send(p)
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = from_jid, \
stanza_type = "unsubscribed")
self.stream.send(p)
del self.__storage[(base_from_jid, name)]
p = Presence(from_jid = self.jid, to_jid = from_jid, \
stanza_type = "unsubscribe")
self.stream.send(p)
p = Presence(from_jid = self.jid, to_jid = from_jid, \
stanza_type = "unsubscribed")
self.stream.send(p)
return 1
query = iq.get_query()
x = X()
x.from_xml(query.children)
if x.fields.has_key("name"):
name = x.fields["name"].value.lower()
else:
name = u""
if x.fields.has_key("login"):
login = x.fields["login"].value
else:
login = u""
if x.fields.has_key("password"):
password = x.fields["password"].value
else:
password = u""
store_password = x.fields.has_key("store_password") \
and (x.fields["store_password"].value == "1")
if x.fields.has_key("host"):
host = x.fields["host"].value
else:
host = u""
if x.fields.has_key("mailbox"):
mailbox = x.fields["mailbox"].value
else:
mailbox = u""
if x.fields.has_key("type"):
type = x.fields["type"].value
else:
type = u"pop3"
if x.fields.has_key("port") and x.fields["port"].value != "":
port = int(x.fields["port"].value)
else:
port = None
if x.fields.has_key("chat_action") and x.fields["chat_action"].value != "":
chat_action = int(x.fields["chat_action"].value)
else:
chat_action = mailconnection.DO_NOTHING
if x.fields.has_key("online_action") and x.fields["online_action"].value != "":
online_action = int(x.fields["online_action"].value)
else:
online_action = mailconnection.DO_NOTHING
if x.fields.has_key("away_action") and x.fields["away_action"].value != "":
away_action = int(x.fields["away_action"].value)
else:
away_action = mailconnection.DO_NOTHING
if x.fields.has_key("xa_action") and x.fields["xa_action"].value != "":
xa_action = int(x.fields["xa_action"].value)
else:
xa_action = mailconnection.DO_NOTHING
if x.fields.has_key("dnd_action") and x.fields["dnd_action"].value != "":
dnd_action = int(x.fields["dnd_action"].value)
else:
dnd_action = mailconnection.DO_NOTHING
if x.fields.has_key("offline_action") and x.fields["offline_action"].value != "":
offline_action = int(x.fields["offline_action"].value)
else:
offline_action = mailconnection.DO_NOTHING
if x.fields.has_key("interval") and x.fields["interval"].value != "":
interval = int(x.fields["interval"].value)
else:
interval = None
live_email_only = x.fields.has_key("live_email_only") \
and (x.fields["live_email_only"].value == "1")
self.__logger.debug(u"New Account: %s, %s, %s, %s, %s, %s, %s, %s %i %i %i %i %i %i %i %s" \
% (name, login, password, str(store_password), host, str(port), \
mailbox, type, chat_action, online_action, away_action, \
xa_action, dnd_action, offline_action, interval, str(live_email_only)))
iq = iq.make_result_response()
self.stream.send(iq)
if not self.__storage.has_key((base_from_jid,)):
p = Presence(from_jid = self.jid, to_jid = base_from_jid, \
stanza_type="subscribe")
self.stream.send(p)
## Update account
if port != None:
socket = host + ":" + str(port)
else:
socket = host
if self.__storage.has_key((base_from_jid, name)):
account = self.__storage[(base_from_jid, name)]
m = Message(from_jid = self.jid, to_jid = from_jid, \
stanza_type = "normal", \
subject = lang_class.update_account_message_subject \
% (type, name), \
body = lang_class.update_account_message_body \
% (login, password, socket))
self.stream.send(m)
else:
account = mailconnection_factory.get_new_mail_connection(type)
m = Message(from_jid = self.jid, to_jid = from_jid, \
stanza_type = "normal", \
subject = lang_class.new_account_message_subject \
% (type, name), \
body = lang_class.new_account_message_body \
% (login, password, socket))
self.stream.send(m)
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = base_from_jid, \
stanza_type="subscribe")
self.stream.send(p)
account.login = login
account.password = password
account.store_password = store_password
account.host = host
account.chat_action = chat_action
account.online_action = online_action
account.away_action = away_action
account.xa_action = xa_action
account.dnd_action = dnd_action
account.offline_action = offline_action
account.interval = interval
account.live_email_only = live_email_only
if port:
account.port = port
if type[0:4] == "imap":
account.mailbox = mailbox
self.__storage[(base_from_jid, name)] = account
return 1
def __send_presence_available(self, name, to_account, to_jid, show, lang_class):
to_account.default_lang_class = lang_class
to_account.first_check = True
old_status = to_account.status
# Make available to receive mail only when online
if show is None:
to_account.status = "online" # TODO get real status = (not show)
else:
to_account.status = show
p = Presence(from_jid = name + "@" + \
unicode(self.jid), \
to_jid = to_jid, \
status = to_account.get_status_msg(), \
show = show, \
stanza_type = "available")
self.stream.send(p)
if to_account.store_password == False \
and old_status == "offline":
self.__ask_password(name, to_jid, lang_class, to_account)
""" Handle presence availability """
def presence_available(self, stanza):
self.__logger.debug("PRESENCE_AVAILABLE")
from_jid = stanza.get_from()
base_from_jid = unicode(from_jid.bare())
name = stanza.get_to().node
lang_class = self.__lang.get_lang_class_from_node(stanza.get_node())
show = stanza.get_show()
self.__logger.debug("SHOW : " + str(show))
if name:
self.__logger.debug("TO : " + name + " " + base_from_jid)
if not name and self.__storage.has_key((base_from_jid,)):
p = Presence(from_jid = self.jid, \
to_jid = from_jid, \
status = \
str(len(self.__storage.keys((base_from_jid,)))) \
+ " accounts registered.", \
show = show, \
stanza_type = "available")
self.stream.send(p)
for name in self.__storage.keys((base_from_jid,)):
account = self.__storage[(base_from_jid, name)]
self.__send_presence_available(name, account, from_jid, show, lang_class)
elif self.__storage.has_key((base_from_jid, name)):
account = self.__storage[(base_from_jid, name)]
self.__send_presence_available(name, account, from_jid, show, lang_class)
return 1
def __ask_password(self, name, from_jid, lang_class, account):
if not account.waiting_password_reply \
and account.status != "offline":
account.waiting_password_reply = True
msg = Message(from_jid = name + "@" + unicode(self.jid), \
to_jid = from_jid, \
stanza_type = "normal", \
subject = u"[PASSWORD] " + lang_class.ask_password_subject, \
body = lang_class.ask_password_body % \
(account.host, account.login))
self.stream.send(msg)
""" handle presence unavailability """
def presence_unavailable(self, stanza):
self.__logger.debug("PRESENCE_UNAVAILABLE")
from_jid = stanza.get_from()
base_from_jid = unicode(from_jid.bare())
if stanza.get_to() == unicode(self.jid):
for name in self.__storage.keys((base_from_jid,)):
account = self.__storage[(base_from_jid, name)]
account.status = "offline"
account.waiting_password_reply = False
if account.store_password == False:
self.__logger.debug("Forgetting password")
account.password = None
p = Presence(from_jid = name + "@" + unicode(self.jid), \
to_jid = from_jid, \
stanza_type = "unavailable")
self.stream.send(p)
p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \
stanza_type = "unavailable")
self.stream.send(p)
return 1
""" handle subscribe presence from user """
def presence_subscribe(self, stanza):
self.__logger.debug("PRESENCE_SUBSCRIBE")
p = stanza.make_accept_response()
self.stream.send(p)
return 1
""" handle subscribed presence from user """
def presence_subscribed(self, stanza):
self.__logger.debug("PRESENCE_SUBSCRIBED")
name = stanza.get_to().node
from_jid = stanza.get_from()
base_from_jid = unicode(from_jid.bare())
if name is not None and self.__storage.has_key((base_from_jid, name)):
account = self.__storage[(base_from_jid, name)]
account.status = "online" # TODO retrieve real status
p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \
status = account.get_status_msg(), \
stanza_type = "available")
self.stream.send(p)
return 1
""" handle unsubscribe presence from user """
def presence_unsubscribe(self, stanza):
self.__logger.debug("PRESENCE_UNSUBSCRIBE")
name = stanza.get_to().node
from_jid = stanza.get_from()
base_from_jid = unicode(from_jid.bare())
if name is not None and self.__storage.has_key((base_from_jid, name)):
del self.__storage[(base_from_jid, name)]
p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \
stanza_type = "unsubscribe")
self.stream.send(p)
p = stanza.make_accept_response()
self.stream.send(p)
return 1
""" handle unsubscribed presence from user """
def presence_unsubscribed(self, stanza):
self.__logger.debug("PRESENCE_UNSUBSCRIBED")
p = Presence(from_jid = stanza.get_to(), to_jid = stanza.get_from(), \
stanza_type = "unavailable")
self.stream.send(p)
self.__storage.sync()
return 1
""" Handle new message """
def message(self, message):
self.__logger.debug("MESSAGE: " + message.get_body())
lang_class = self.__lang.get_lang_class_from_node(message.get_node())
name = message.get_to().node
base_from_jid = unicode(message.get_from().bare())
if re.compile("\[PASSWORD\]").search(message.get_subject()) is not None \
and self.__storage.has_key((base_from_jid, name)):
account = self.__storage[(base_from_jid, name)]
account.password = message.get_body()
account.waiting_password_reply = False
msg = Message(from_jid = name + "@" + unicode(self.jid), \
to_jid = message.get_from(), \
stanza_type = "normal", \
subject = lang_class.password_saved_for_session, \
body = lang_class.password_saved_for_session)
self.stream.send(msg)
return 1
""" Check mail account """
def check_mail(self, jid, name):
self.__logger.debug("\nCHECK_MAIL " + unicode(jid) + " " + name)
account = self.__storage[(jid, name)]
action = account.action
if action != mailconnection.DO_NOTHING:
try:
if account.password is None:
self.__ask_password(name, jid, account.default_lang_class, account)
return
self.__logger.debug("Checking " + name)
self.__logger.debug("\t" + account.login \
+ "@" + account.host)
account.connect()
mail_list = account.get_mail_list()
if action == mailconnection.RETRIEVE:
mail_index = account.get_next_mail_index(mail_list)
while mail_index is not None:
(body, email_from) = account.get_mail(mail_index)
mesg = Message(from_jid = name + "@" + \
unicode(self.jid), \
to_jid = jid, \
stanza_type = "normal", \
subject = account.default_lang_class.new_mail_subject % (email_from), \
body = body)
self.stream.send(mesg)
mail_index = account.get_next_mail_index(mail_list)
else:
body = ""
new_mail_count = 0
mail_index = account.get_next_mail_index(mail_list)
while mail_index:
(tmp_body, from_email) = \
account.get_mail_summary(mail_index)
body += tmp_body + "\n----------------------------------\n"
mail_index = account.get_next_mail_index(mail_list)
new_mail_count += 1
if body != "":
mesg = Message(from_jid = name + "@" + \
unicode(self.jid), \
to_jid = jid, \
stanza_type = "headline", \
subject = account.default_lang_class.new_digest_subject % (new_mail_count), \
body = body)
self.stream.send(mesg)
account.disconnect()
account.in_error = False
self.__logger.debug("\nCHECK_MAIL ends " + unicode(jid) + " " + name)
except Exception,e:
if account.in_error == False:
account.in_error = True
msg = Message(from_jid = name + "@" + unicode(self.jid), \
to_jid = jid, \
stanza_type = "error", \
subject = account.default_lang_class.check_error_subject, \
body = account.default_lang_class.check_error_body \
% (e))
self.stream.send(msg)
type, value, stack = sys.exc_info()
self.__logger.debug("Error while checking mail : %s\n%s" \
% (e, "".join(traceback.format_exception
(type, value, stack, 5))))
""" check mail handler """
def check_all_mail(self):
self.__logger.debug("CHECK_ALL_MAIL")
for jid, name in self.__storage.keys():
account = self.__storage[(jid, name)]
if account.first_check and account.live_email_only:
account.first_check = False
if account.password is None:
self.__ask_password(name, jid, account.default_lang_class, account)
return
try:
account.connect()
account.mark_all_as_read()
account.disconnect()
account.in_error = False
except Exception,e:
if account.in_error == False:
account.in_error = True
msg = Message(from_jid = name + "@" + unicode(self.jid), \
to_jid = jid, \
stanza_type = "error", \
subject = account.default_lang_class.check_error_subject, \
body = account.default_lang_class.check_error_body \
% (e))
self.stream.send(msg)
type, value, stack = sys.exc_info()
self.__logger.debug("Error while first checking mail : %s\n%s" \
% (e, "".join(traceback.format_exception
(type, value, stack, 5))))
account.lastcheck += 1
if account.lastcheck == account.interval:
account.lastcheck = 0
self.check_mail(jid, name)

129
src/jmc/jabber/x.py Normal file
View File

@@ -0,0 +1,129 @@
##
## x.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Fri Jan 7 11:06:42 2005
## $Id: x.py,v 1.3 2005/09/18 20:24:07 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import sys
from pyxmpp.stanza import common_doc
class Option(object):
def __init__(self, label, value):
self.__label = label
self.__value = value
def get_xml(self, parent):
if parent is None:
option = common_doc.newChild(None, "option", None)
else:
option = parent.newChild(None, "option", None)
option.setProp("label", self.__label)
option.newChild(None, "value", self.__value)
return option
class Field(object):
def __init__(self, type, label, var, value):
self.__type = type
self.__label = label
self.__var = var
self.value = value
self.__options = []
def add_option(self, label, value):
option = Option(label, value)
self.__options.append(option)
return option
def get_xml(self, parent):
if parent is None:
raise Exception, "parent field should not be None"
else:
field = parent.newChild(None, "field", None)
field.setProp("type", self.__type)
if not self.__label is None:
field.setProp("label", self.__label)
if not self.__var is None:
field.setProp("var", self.__var)
if self.value:
field.newChild(None, "value", self.value)
for option in self.__options:
option.get_xml(field)
return field
class X(object):
def __init__(self):
self.fields = {}
self.fields_tab = []
self.title = None
self.instructions = None
self.type = None
self.xmlns = None
def add_field(self, type = "fixed", label = None, var = None, value = ""):
field = Field(type, label, var, value)
self.fields[var] = field
# fields_tab exist to keep added fields order
self.fields_tab.append(field)
return field
def attach_xml(self, iq):
node = iq.newChild(None, "x", None)
_ns = node.newNs(self.xmlns, None)
node.setNs(_ns)
if not self.title is None:
node.newTextChild(None, "title", self.title)
if not self.instructions is None:
node.newTextChild(None, "instructions", self.instructions)
for field in self.fields_tab:
field.get_xml(node)
return node
def from_xml(self, node):
## TODO : test node type and ns and clean that loop !!!!
while node and node.type != "element":
node = node.next
child = node.children
while child:
## TODO : test child type (element) and ns (jabber:x:data)
if child.type == "element" and child.name == "field":
if child.hasProp("type"):
type = child.prop("type")
else:
type = ""
if child.hasProp("label"):
label = child.prop("label")
else:
label = ""
if child.hasProp("var"):
var = child.prop("var")
else:
var = ""
xval = child.children
while xval and xval.name != "value":
xval = xval.next
if xval:
value = xval.getContent()
else:
value = ""
field = Field(type, label, var, value)
self.fields[var] = field
child = child.next

91
src/jmc/jmc.py Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/python -u
##
## Jabber Mail Component
## jmc.py
## Login : David Rousselie <david.rousselie@happycoders.org>
## Started on Fri Jan 7 11:06:42 2005
## $Id: jmc.py,v 1.3 2005/07/11 20:39:31 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import sys
import os.path
import logging
reload(sys)
sys.setdefaultencoding('utf-8')
del sys.setdefaultencoding
from jmc import mailconnection
from jmc.component import MailComponent, ComponentFatalError
from jmc.config import Config
def main(config_file = "jmc.xml", isDebug = 0):
try:
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler())
if isDebug > 0:
logger.setLevel(logging.DEBUG)
try:
logger.debug("Loading config file " + config_file)
config = Config(config_file)
except:
print >>sys.stderr, "Couldn't load config file:", \
str(sys.exc_value)
sys.exit(1)
pidfile = open(config.get_content("config/pidfile"), "w")
pidfile.write(str(os.getpid()))
pidfile.close()
mailconnection.default_encoding = config.get_content("config/mail_default_encoding")
print "creating component..."
mailcomp = MailComponent(config.get_content("config/jabber/service"), \
config.get_content("config/jabber/secret"), \
config.get_content("config/jabber/server"), \
int(config.get_content("config/jabber/port")), \
config.get_content("config/jabber/language"), \
int(config.get_content("config/check_interval")), \
config.get_content("config/spooldir"), \
config.get_content("config/storage"), \
config.get_content("config/jabber/vCard/FN"))
print "starting..."
mailcomp.run(1)
except ComponentFatalError,e:
print e
print "Aborting."
sys.exit(1)
if __name__ == "__main__":
var_option = 0
file_num = 0
index = 0
debug_level = 0
for opt in sys.argv:
if var_option == 0 and len(opt) == 2 and opt == "-c":
var_option += 1
elif (var_option & 1) == 1 and len(opt) > 0:
var_option += 1
file_num = index
if len(opt) == 2 and opt == "-D":
debug_level = 1
index += 1
if (var_option & 2) == 2:
main(sys.argv[file_num], debug_level)
else:
main("/etc/jabber/jmc.xml", debug_level)

View File

47
src/jmc/utils/config.py Normal file
View File

@@ -0,0 +1,47 @@
##
## config.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Fri Jan 7 11:06:42 2005
## $Id: config.py,v 1.2 2005/03/13 11:39:36 dax Exp $
##
## Copyright (C) 2005
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import libxml2
import os
from pyxmpp.jid import JID
from jmc.jabber.component import ComponentFatalError
class Config:
def __init__(self, config_file):
self.doc = None
self.config_file = config_file
# libxml2.initializeCatalog()
# libxml2.loadCatalog(os.path.join(data_dir, "catalog.xml"))
parser = libxml2.createFileParserCtxt(config_file)
# parser.validate(1)
parser.parseDocument()
if not parser.isValid():
raise ComponentFatalError, "Invalid configuration"
self.doc = parser.doc()
def get_content(self, xpath):
return self.doc.xpathEval(xpath)[0].getContent()
def __del__(self):
if self.doc:
self.doc.freeDoc()

210
src/jmc/utils/lang.py Normal file
View File

@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
##
## lang.py
## Login : David Rousselie <david.rousselie@happycoders.org>
## Started on Sat Jan 28 16:37:11 2006 David Rousselie
## $Id$
##
## Copyright (C) 2006 David Rousselie
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
class Lang:
def __init__(self, default_lang = "en"):
self.default_lang = default_lang
def get_lang_from_node(self, node):
lang = node.getLang()
if lang is None:
print "Using default lang " + self.default_lang
lang = self.default_lang
return lang
def get_lang_class(self, lang):
return getattr(Lang, lang)
def get_lang_class_from_node(self, node):
return self.get_lang_class(self.get_lang_from_node(node))
class en:
register_title = u"Jabber Mail connection registration"
register_instructions = u"Enter connection parameters"
account_name = u"Connection name"
account_login = u"Login"
account_password = u"Password"
account_password_store = u"Store password on Jabber server?"
account_host = u"Host"
account_port = u"Port"
account_type = u"Mail server type"
account_mailbox = u"Mailbox path (IMAP)"
account_ffc_action = u"Action when state is 'Free For Chat'"
account_online_action = u"Action when state is 'Online'"
account_away_action = u"Action when state is 'Away'"
account_xa_action = u"Action when state is 'Not Available'"
account_dnd_action = u"Action when state is 'Do not Disturb'"
account_offline_action = u"Action when state is 'Offline'"
account_check_interval = u"Mail check interval (in minutes)"
account_live_email_only = u"Reports only emails received while connected to Jabber"
action_nothing = u"Do nothing"
action_retrieve = u"Retrieve mail"
action_digest = u"Send mail digest"
update_title = u"Jabber mail connection update"
update_instructions = u"Modifying connection '%s'"
connection_label = u"%s connection '%s'"
update_account_message_subject = u"Updated %s connection '%s'"
update_account_message_body = u"Registered with username '%s' and " \
"password '%s' on '%s'"
new_account_message_subject = u"New %s connection '%s' created"
new_account_message_body = u"Registered with " \
"username '%s' and password '%s' on '%s'"
ask_password_subject = u"Password request"
ask_password_body = u"Reply to this message with the password " \
"for the following account: \n" \
"\thost = %s\n" \
"\tlogin = %s\n"
password_saved_for_session = u"Password will be kept during your Jabber session"
check_error_subject = u"Error while checking emails."
check_error_body = u"An error appears while checking emails:\n\t%s"
new_mail_subject = u"New email from %s"
new_digest_subject = u"%i new email(s)"
class fr:
register_title = u"Enregistrement d'une nouvelle connexion à un serveur email."
register_instructions = u"Entrer les paramètres de connexion"
account_name = u"Nom de la connexion"
account_login = u"Nom d'utilisateur"
account_password = u"Mot de passe"
account_password_store = u"Sauvegarder le mot de passe sur le serveur Jabber ?"
account_host = u"Adresse du serveur email"
account_port = u"Port du serveur email"
account_type = u"Type du serveur email"
account_mailbox = u"Chemin de la boîte email (IMAP)"
account_ffc_action = u"Action lorsque l'état est 'Free For Chat'"
account_online_action = u"Action lorsque l'état est 'Online'"
account_away_action = u"Action lorsque l'état est 'Away'"
account_xa_action = u"Action lorsque l'état est 'Not Available'"
account_dnd_action = u"Action lorsque l'état est 'Do not Disturb'"
account_offline_action = u"Action lorsque l'état est 'Offline'"
account_check_interval = u"Interval de vérification de nouveaux emails (en minutes)"
account_live_email_only = u"Vérifier les nouveaux emails seulement " \
"lorsqu'une session Jabber est ouverte"
action_nothing = u"Ne rien faire"
action_retrieve = u"Récupérer l'email"
action_digest = u"Envoyer un résumé"
update_title = u"Mise à jour du compte JMC"
update_instructions = u"Modification de la connexion '%s'"
connection_label = u"Connexion %s '%s'"
update_account_message_subject = u"La connexion %s '%s' a été mise à jour"
update_account_message_body = u"Nom d'utilisateur : '%s'\nMot de passe : '%s'\nsur : '%s'"
new_account_message_subject = u"La connexion %s '%s' a été créée"
new_account_message_body = u"Nom d'utilisateur : '%s'\nMot de passe : '%s'\nsur : '%s'"
ask_password_subject = u"Demande de mot de passe"
ask_password_body = u"Répondre à ce message avec le mot de passe du " \
"compte suivant : \n" \
"\thost = %s\n" \
"\tlogin = %s\n"
password_saved_for_session = u"Le mot de passe sera garder tout au " \
"long de la session Jabber."
check_error_subject = u"Erreur lors de la vérification des emails."
check_error_body = u"Une erreur est survenue lors de la vérification " \
"des emails :\n\t%s"
new_mail_subject = u"Nouvel email de %s"
new_digest_subject = u"%i nouveau(x) email(s)"
class nl:
register_title = u"Registratie van verbindingen voor Jabber Mail"
register_instructions = u"Instellingen voor verbinding"
account_name = u"Accountnaam"
account_login = u"Gebruikersnaam"
account_password = u"Wachtwoord"
account_password_store = u"Wachtwoord opslaan op Jabber-server?"
account_host = u"Server"
account_port = u"Poort"
account_type = u"Soort account"
account_mailbox = u"Pad naar mailbox (IMAP)"
account_ffc_action = u"Actie bij aanwezigheid 'Chat'"
account_online_action = u"Actie bij aanwezigheid 'Beschikbaar'"
account_away_action = u"Actie bij aanwezigheid 'Afwezig'"
account_xa_action = u"Actie bij aanwezigheid 'Langdurig afwezig'"
account_dnd_action = u"Actie bij aanwezigheid 'Niet storen'"
account_offline_action = u"Actie bij aanwezigheid 'Niet beschikbaar'"
account_check_interval = u"Controle-interval (in minuten)"
account_live_email_only = u"Enkel controleren op e-mails als er een" \
"verbinding is met Jabber"
action_nothing = u"Niets doen"
action_retrieve = u"E-mail ophalen"
action_digest = u"Samenvatting verzenden"
update_title = u"Bijwerken van JMC"
update_instructions = u"Verbinding '%s' aanpassen"
connection_label = u"%s verbinding '%s'"
update_account_message_subject = u"Verbinding %s '%s' werd bijgewerkt"
update_account_message_body = u"Geregistreerd met gebruikersnaam '%s'"\
"en wachtwoord '%s' op '%s'"
new_account_message_subject = u"Nieuwe %s verbinding '%s' aangemaakt"
new_account_message_body = u"Geregistreerd met " \
"gebruikersnaam '%s' en wachtwoord '%s' op '%s'"
ask_password_subject = u"Wachtwoordaanvraag"
ask_password_body = u"Antwoord dit bericht met het volgende wachtwoord" \
"voor de volgende account: \n" \
"\thost = %s\n" \
"\tlogin = %s\n"
password_saved_for_session = u"Het wachtwoord zal worden bewaard tijdens uw Jabber-sessie"
check_error_subject = u"Fout tijdens controle op e-mails."
check_error_body = u"Fout tijdens controle op e-mails:\n\t%s"
new_mail_subject = u"Nieuwe e-mail van %s"
new_digest_subject = u"%i nieuwe e-mail(s)"
class es:
register_title = u"Registro de nueva cuenta de email"
register_instructions = u"Inserta los datos para la nueva cuenta"
account_name = u"Nombre para la cuenta"
account_login = u"Usuario (login)"
account_password = u"Contraseña"
account_password_store = u"¿Guardar la contraseña en el servidor Jabber?"
account_host = u"Host"
account_port = u"Puerto"
account_type = u"Tipo de servidor Mail"
account_mailbox = u"Ruta del mailbox (solo para IMAP)"
account_ffc_action = u"Acción para cuando tu estado sea 'Listopara hablar'"
account_online_action = u"Acción para cuando tu estado sea 'Conectado'"
account_away_action = u"Acción para cuando tu estado sea 'Ausente'"
account_xa_action = u"Acción para cuando tu estado sea 'No disponible'"
account_dnd_action = u"Acción para cuando tu estado sea 'No molestar'"
account_offline_action = u"Acción para cuando tu estado sea 'Desconectado'"
account_check_interval = u"Intervalo para comprobar emails nuevos (en minutos)"
account_live_email_only = u"Avisarme de emails nuevos solo cuando esté conectado"
action_nothing = u"No hacer nada"
action_retrieve = u"Mostrarme el email"
action_digest = u"Enviar resúmen"
update_title = u"Actualización de cuenta de email"
update_instructions = u"Modifica los datos de la cuenta '%s'"
connection_label = u"%s conexión '%s'"
update_account_message_subject = u"Actualizada %s conexión '%s'"
update_account_message_body = u"Registrado con el usuario '%s' y " \
"contraseña '%s' en '%s'"
new_account_message_subject = u"Nueva %s conexión '%s' creada"
new_account_message_body = u"Registrado con " \
"usuario '%s' y contraseña '%s' en '%s'"
ask_password_subject = u"Petición de contraseña"
ask_password_body = u"Para avisarte de emails nuevos, contesta a este mensaje con la contraseña " \
"de la cuenta: \n" \
"\tHost = %s\n" \
"\tUsuario = %s\n"
password_saved_for_session = u"La contraseña será guardada para esta sesión únicamente."
check_error_subject = u"Error al revisar los emails."
check_error_body = u"Un error apareció al revisar los emails:\n\t%s"
new_mail_subject = u"Nuevo email en %s"
new_digest_subject = u"%i email(s) nuevo(s)"

33
src/jmc/utils/release.py Normal file
View File

@@ -0,0 +1,33 @@
##
## release.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Mon Jul 24 22:37:00 2006 dax
## $Id$
##
## Copyright (C) 2006 dax
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
version = "0.2.2"
author = "David Rousselie"
email = "dax@happycoders.org"
license = "GPL"
long_description = """Jabber Mail Component
JMC is a jabber service to check email from POP3 and IMAP4 server and retrieve
them or just a notification of new emails. Jabber users can register multiple
email accounts.
"""

297
src/jmc/utils/storage.py Normal file
View File

@@ -0,0 +1,297 @@
##
## storage.py
## Login : David Rousselie <dax@happycoders.org>
## Started on Wed Jul 20 20:26:53 2005 dax
## $Id: storage.py,v 1.1 2005/09/18 20:24:07 dax Exp $
##
## Copyright (C) 2005 dax
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import os
import re
import os.path
import sys
import anydbm
import logging
from UserDict import UserDict
import jmc.email.mailconnection_factory as mailconnection_factory
class Storage(UserDict):
def __init__(self, nb_pk_fields = 1, spool_dir = ".", db_file = None):
UserDict.__init__(self)
self.nb_pk_fields = nb_pk_fields
if db_file is None:
self._spool_dir = ""
self.set_spool_dir(spool_dir)
self.file = self._spool_dir + "/registered.db"
else:
spool_dir = os.path.dirname(db_file) or "."
self.set_spool_dir(spool_dir)
self.file = db_file
self._registered = self.load()
def __setitem__(self, pk_tuple, obj):
# print "Adding " + "#".join(map(str, pk_tuple)) + " = " + str(obj)
self._registered[str("#".join(map(str, pk_tuple)))] = obj
def __getitem__(self, pk_tuple):
# print "Getting " + "#".join(map(str, pk_tuple))
if len(pk_tuple) == self.nb_pk_fields:
return self._registered[str("#".join(map(str, pk_tuple)))]
else:
partial_key = str("#".join(map(str, pk_tuple)))
regexp = re.compile(partial_key)
return [self._registered[key]
for key in self._registered.keys()
if regexp.search(key)]
def __delitem__(self, pk_tuple):
#print "Deleting " + "#".join(map(str, pk_tuple))
del self._registered[str("#".join(map(str, pk_tuple)))]
def get_spool_dir(self):
return self._spool_dir
def set_spool_dir(self, spool_dir):
self._spool_dir = spool_dir
if not os.path.isdir(self._spool_dir):
os.makedirs(self._spool_dir)
spool_dir = property(get_spool_dir, set_spool_dir)
def has_key(self, pk_tuple):
if len(pk_tuple) == self.nb_pk_fields:
return self._registered.has_key(str("#".join(map(str, pk_tuple))))
else:
partial_key = str("#".join(map(str, pk_tuple)))
regexp = re.compile("^" + partial_key)
for key in self._registered.keys():
if regexp.search(key):
return True
return False
def keys(self, pk_tuple = None):
if pk_tuple is None:
return [tuple(key.split("#")) for key in self._registered.keys()]
else:
level = len(pk_tuple)
partial_key = str("#".join(map(str, pk_tuple)))
regexp = re.compile("^" + partial_key)
result = {}
for key in self._registered.keys():
if regexp.search(key):
result[key.split("#")[level]] = None
return result.keys()
def dump(self):
for pk in self._registered.keys():
print pk + " = " + str(self._registered[pk])
def load(self):
pass
class DBMStorage(Storage):
_logger = logging.getLogger("jmc.utils.DBMStorage")
def __init__(self, nb_pk_fields = 1, spool_dir = ".", db_file = None):
# print "DBM INIT"
Storage.__init__(self, nb_pk_fields, spool_dir, db_file)
def __del__(self):
# print "DBM STOP"
self.sync()
def load(self):
str_registered = anydbm.open(self.file, \
'c')
result = {}
try:
for pk in str_registered.keys():
result[pk] = mailconnection_factory.str_to_mail_connection(str_registered[pk])
except Exception, e:
print >>sys.stderr, "Cannot load registered.db : "
print >>sys.stderr, e
str_registered.close()
return result
def sync(self):
#print "DBM SYNC"
self.store()
def __store(self, nb_pk_fields, registered, pk):
if nb_pk_fields > 0:
for key in registered.keys():
if pk:
self.__store(nb_pk_fields - 1, \
registered[key], pk + "#" + key)
else:
self.__store(nb_pk_fields - 1, \
registered[key], key)
else:
self.__str_registered[pk] = str(registered)
# print "STORING : " + pk + " = " + str(registered)
def store(self):
# print "DBM STORE"
try:
str_registered = anydbm.open(self.file, \
'n')
for pk in self._registered.keys():
str_registered[pk] = str(self._registered[pk])
except Exception, e:
print >>sys.stderr, "Cannot save to registered.db : "
print >>sys.stderr, e
str_registered.close()
def __setitem__(self, pk_tuple, obj):
Storage.__setitem__(self, pk_tuple, obj)
self.sync()
def __delitem__(self, pk_tuple):
Storage.__delitem__(self, pk_tuple)
self.sync()
# Do not fail if pysqlite is not installed
try:
from pysqlite2 import dbapi2 as sqlite
class SQLiteStorage(Storage):
_logger = logging.getLogger("jmc.utils.SQLiteStorage")
def __init__(self, nb_pk_fields = 1, spool_dir = ".", db_file = None):
self.__connection = None
Storage.__init__(self, nb_pk_fields, spool_dir, db_file)
def create(self):
SQLiteStorage._logger.debug("creating new Table")
cursor = self.__connection.cursor()
cursor.execute("""
create table account(
jid STRING,
name STRING,
type STRING,
login STRING,
password STRING,
host STRING,
port INTEGER,
chat_action INTEGER,
online_action INTEGER,
away_action INTEGER,
xa_action INTEGER,
dnd_action INTEGER,
offline_action INTEGER,
interval INTEGER,
live_email_only BOOLEAN,
mailbox STRING,
PRIMARY KEY(jid, name)
)
""")
self.__connection.commit()
cursor.close()
def __del__(self):
self.__connection.close()
def sync(self):
pass
def load(self):
if not os.path.exists(self.file):
self.__connection = sqlite.connect(self.file)
self.create()
else:
self.__connection = sqlite.connect(self.file)
cursor = self.__connection.cursor()
cursor.execute("""select * from account""")
result = {}
for row in cursor.fetchall():
# print "Creating new " + row[self.nb_pk_fields] + " connection."
account_type = row[self.nb_pk_fields]
account = result["#".join(row[0:self.nb_pk_fields])] = mailconnection_factory.get_new_mail_connection(account_type)
account.login = row[self.nb_pk_fields + 1]
account.password = row[self.nb_pk_fields + 2]
if account.password is None:
account.store_password = False
else:
account.store_password = True
account.host = row[self.nb_pk_fields + 3]
account.port = int(row[self.nb_pk_fields + 4])
account.chat_action = int(row[self.nb_pk_fields + 5])
account.online_action = int(row[self.nb_pk_fields + 6])
account.away_action = int(row[self.nb_pk_fields + 7])
account.xa_action = int(row[self.nb_pk_fields + 8])
account.dnd_action = int(row[self.nb_pk_fields + 9])
account.offline_action = int(row[self.nb_pk_fields + 10])
account.interval = int(row[self.nb_pk_fields + 11])
account.live_email_only = (row[self.nb_pk_fields + 12] == 1)
if account_type[0:4] == "imap":
account.mailbox = row[self.nb_pk_fields + 13]
# for field_index in range(self.nb_pk_fields + 1, len(row)):
# print "\tSetting " + str(cursor.description[field_index][0]) + \
# " to " + str(row[field_index])
# setattr(account,
# cursor.description[field_index][0],
# row[field_index])
cursor.close()
return result
def __setitem__(self, pk_tuple, obj):
Storage.__setitem__(self, pk_tuple, obj)
cursor = self.__connection.cursor()
mailbox = None
password = None
if obj.type[0:4] == "imap":
mailbox = obj.mailbox
if obj.store_password == True:
password = obj.password
cursor.execute("""
insert or replace into account values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(pk_tuple[0],
pk_tuple[1],
obj.type,
obj.login,
password,
obj.host,
obj.port,
obj.chat_action,
obj.online_action,
obj.away_action,
obj.xa_action,
obj.dnd_action,
obj.offline_action,
obj.interval,
obj.live_email_only,
mailbox))
self.__connection.commit()
cursor.close()
def __delitem__(self, pk_tuple):
Storage.__delitem__(self, pk_tuple)
cursor = self.__connection.cursor()
cursor.execute("""
delete from account where jid = ? and name = ?
""",
(pk_tuple[0],
pk_tuple[1]))
self.__connection.commit()
cursor.close()
except ImportError:
pass

View File

@@ -0,0 +1,73 @@
##
## jmc_backend_converter.py
## Login : David Rousselie <david.rousselie@happycoders.org>
## Started on Sun Jan 29 18:46:29 2006 David Rousselie
## $Id$
##
## Copyright (C) 2006 David Rousselie
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import sys
sys.path.insert(0, "..")
import types
import jabber.storage
import os.path
import re
if len(sys.argv) != 5:
print >>sys.stderr, "Usage: " + sys.argv[0] + " from_type from_db_file to_type to_db_file"
print >>sys.stderr, "Supported DB type are :"
for var in [aclass
for aclass in dir(jabber.storage)
if type(getattr(jabber.storage, aclass)) == types.ClassType \
and re.compile(".+Storage$").match(aclass) is not None]:
print >>sys.stderr, "\t" + var
sys.exit(1)
from_storage_class = sys.argv[1]
from_file = sys.argv[2]
to_storage_class = sys.argv[3]
to_file = sys.argv[4]
if not os.path.exists(from_file):
print >>sys.stderr, from_file + " does not exist."
sys.exit(1)
from jabber.storage import *
from_storage = None
to_storage = None
try:
from_storage = globals()[from_storage_class + "Storage"](2, db_file = from_file)
except Exception,e:
print >>sys.stderr, e
print >>sys.stderr, "Cannot find " + from_storage_class + "Storage class"
sys.exit(1)
try:
to_storage = globals()[to_storage_class + "Storage"](2, db_file = to_file)
except Exception,e:
print >>sys.stderr, e
print >>sys.stderr, "Cannot find " + to_storage_class + "Storage class"
sys.exit(1)
for jid, name in from_storage.keys():
print "Converting " + jid + "/" + name + " from " + from_storage_class + \
" to " + to_storage_class + "."
to_storage[(jid, name)] = from_storage[(jid, name)]
to_storage.sync()

58
src/utils/backend_dump.py Normal file
View File

@@ -0,0 +1,58 @@
##
## dump.py
## Login : David Rousselie <david.rousselie@happycoders.org>
## Started on Mon Jan 30 21:43:38 2006 David Rousselie
## $Id$
##
## Copyright (C) 2006
## 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import sys
sys.path.insert(0, "..")
import types
import jabber.storage
import os.path
import re
if len(sys.argv) != 3:
print >>sys.stderr, "Usage: " + sys.argv[0] + " db_type db_file"
print >>sys.stderr, "Supported DB type are :"
for var in [aclass
for aclass in dir(jabber.storage)
if type(getattr(jabber.storage, aclass)) == types.ClassType \
and re.compile(".+Storage$").match(aclass) is not None]:
print >>sys.stderr, "\t" + var
sys.exit(1)
from_storage_class = sys.argv[1]
from_file = sys.argv[2]
if not os.path.exists(from_file):
print >>sys.stderr, from_file + " does not exist."
sys.exit(1)
from jabber.storage import *
try:
from_storage = globals()[from_storage_class + "Storage"](2, db_file = from_file)
except Exception,e:
print >>sys.stderr, e
print >>sys.stderr, "Cannot find " + from_storage_class + "Storage class"
sys.exit(1)
for jid, name in from_storage.keys():
print jid + "/" + name + " from " + from_storage_class + " = " + str(from_storage[(jid, name)])