diff --git a/frontends/src/bridge/DBus.py b/frontends/src/bridge/DBus.py
index 4d41ec9d..df269709 100755
--- a/frontends/src/bridge/DBus.py
+++ b/frontends/src/bridge/DBus.py
@@ -80,8 +80,8 @@ class DBusBridgeFrontend(BridgeFrontend):
def getWaitingSub(self, profile_key='@DEFAULT@'):
return self.db_comm_iface.getWaitingSub(profile_key)
- def sendMessage(self, to, message, type='chat', profile_key='@DEFAULT@'):
- return self.db_comm_iface.sendMessage(to, message, type, profile_key)
+ def sendMessage(self, to, message, subject='', type='chat', profile_key='@DEFAULT@'):
+ return self.db_comm_iface.sendMessage(to, message, subject, type, profile_key)
def setPresence(self, to="", show="", priority=0, statuses={}, profile_key='@DEFAULT@'):
return self.db_comm_iface.setPresence(to, show, priority, statuses, profile_key)
diff --git a/src/bridge/DBus.py b/src/bridge/DBus.py
index 15a8686b..2814718d 100755
--- a/src/bridge/DBus.py
+++ b/src/bridge/DBus.py
@@ -182,11 +182,11 @@ class DbusObject(dbus.service.Object):
return self.cb["getWaitingSub"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
- in_signature='ssss', out_signature='')
- def sendMessage(self, to, message, type='chat', profile_key='@DEFAULT@'):
+ in_signature='sssss', out_signature='')
+ def sendMessage(self, to, message, subject="", type='chat', profile_key='@DEFAULT@'):
debug("sendMessage...")
print "sendtype=", type #gof
- self.cb["sendMessage"](to, message, type, profile_key)
+ self.cb["sendMessage"](to, message, subject, type, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ssia{ss}s', out_signature='')
diff --git a/src/plugins/plugin_misc_imap.py b/src/plugins/plugin_misc_imap.py
index 3ad2ecdb..63e28124 100755
--- a/src/plugins/plugin_misc_imap.py
+++ b/src/plugins/plugin_misc_imap.py
@@ -26,6 +26,7 @@ from twisted.words.protocols.jabber import error as jab_error
from twisted.cred import portal,checkers,credentials
from twisted.cred import error as cred_error
from twisted.mail import imap4
+from twisted.python import failure
from email.parser import Parser
import email.message
import os,os.path
diff --git a/src/plugins/plugin_misc_smtp.py b/src/plugins/plugin_misc_smtp.py
new file mode 100755
index 00000000..9e9be671
--- /dev/null
+++ b/src/plugins/plugin_misc_smtp.py
@@ -0,0 +1,206 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+SàT plugin for managing smtp server
+Copyright (C) 2011 Jérôme Poisson (goffi@goffi.org)
+
+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 3 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, see .
+"""
+
+from logging import debug, info, error
+import warnings
+from twisted.internet import protocol,defer
+from twisted.words.protocols.jabber import error as jab_error
+from twisted.cred import portal,checkers,credentials
+from twisted.cred import error as cred_error
+from twisted.mail import smtp
+from twisted.python import failure
+from email.parser import Parser
+from twisted.mail.imap4 import LOGINCredentials, PLAINCredentials
+import os,os.path
+from twisted.internet import reactor
+import pdb
+
+
+from zope.interface import implements
+
+
+PLUGIN_INFO = {
+"name": "SMTP server Plugin",
+"import_name": "SMTP",
+"type": "Misc",
+"protocols": [],
+"dependencies": ["Maildir"],
+"main": "SMTP_server",
+"handler": "no",
+"description": _("""Create a SMTP server that you can use to send your "normal" type messages""")
+}
+
+class SMTP_server():
+
+ params = """
+
+
+
+
+
+
+
+ """
+
+ def __init__(self, host):
+ info(_("Plugin SMTP Server initialization"))
+ self.host = host
+
+ #parameters
+ host.memory.importParams(self.params)
+
+ port = int(self.host.memory.getParamA("Port", "SMTP Server"))
+ info(_("Launching SMTP server on port %d"), port)
+
+ self.server_factory = SmtpServerFactory(self.host)
+ reactor.listenTCP(port, self.server_factory)
+
+class SatSmtpMessage:
+ implements(smtp.IMessage)
+
+ def __init__(self, host, profile):
+ self.host=host
+ self.profile=profile
+ self.message=[]
+
+ def lineReceived(self, line):
+ """handle another line"""
+ self.message.append(line)
+
+ def eomReceived(self):
+ """handle end of message"""
+ mail = Parser().parsestr("\n".join(self.message))
+ self.host.sendMessage(mail['to'].decode('utf-8'), mail.get_payload().decode('utf-8'),
+ subject=mail['subject'].decode('utf-8'), type='normal', profile_key=self.profile)
+ self.message=None
+ return defer.succeed(None)
+
+ def connectionLost(self):
+ """handle message truncated"""
+ raise smtp.SMTPError
+
+class SatSmtpDelivery:
+ implements(smtp.IMessageDelivery)
+
+ def __init__(self, host, profile):
+ self.host=host
+ self.profile=profile
+
+ def receivedHeader(self, helo, origin, recipients):
+ """
+ Generate the Received header for a message
+ @param helo: The argument to the HELO command and the client's IP
+ address.
+ @param origin: The address the message is from
+ @param recipients: A list of the addresses for which this message
+ is bound.
+ @return: The full \"Received\" header string.
+ """
+ return "Received:"
+
+ def validateTo(self, user):
+ """
+ Validate the address for which the message is destined.
+ @param user: The address to validate.
+ @return: A Deferred which becomes, or a callable which
+ takes no arguments and returns an object implementing IMessage.
+ This will be called and the returned object used to deliver the
+ message when it arrives.
+ """
+ return lambda: SatSmtpMessage(self.host, self.profile)
+
+ def validateFrom(self, helo, origin):
+ """
+ Validate the address from which the message originates.
+ @param helo: The argument to the HELO command and the client's IP
+ address.
+ @param origin: The address the message is from
+ @return: origin or a Deferred whose callback will be
+ passed origin.
+ """
+ return origin
+
+class SmtpRealm:
+ implements(portal.IRealm)
+
+ def __init__(self,host):
+ self.host = host
+
+ def requestAvatar(self, avatarID, mind, *interfaces):
+ debug('requestAvatar')
+ profile=avatarID.decode('utf-8')
+ if smtp.IMessageDelivery not in interfaces:
+ raise NotImplementedError
+ return smtp.IMessageDelivery, SatSmtpDelivery(self.host,profile), lambda:None
+
+class SatProfileCredentialChecker:
+ """
+ This credential checker check against SàT's profile and associated jabber's password
+ Check if the profile exists, and if the password is OK
+ Return the profile as avatarId
+ """
+ implements(checkers.ICredentialsChecker)
+ credentialInterfaces = (credentials.IUsernamePassword,
+ credentials.IUsernameHashedPassword)
+
+
+ def __init__(self, host):
+ self.host = host
+
+ def _cbPasswordMatch(self, matched, profile):
+ if matched:
+ return profile.encode('utf-8')
+ else:
+ return failure.Failure(cred_error.UnauthorizedLogin())
+
+ def requestAvatarId(self, credentials):
+ profiles = self.host.memory.getProfilesList()
+ if not credentials.username in profiles:
+ return defer.fail(cred_error.UnauthorizedLogin())
+ password = self.host.memory.getParamA("Password", "Connection", profile_key=credentials.username)
+ return defer.maybeDeferred(
+ credentials.checkPassword,
+ password).addCallback(
+ self._cbPasswordMatch, credentials.username)
+
+class SmtpServerFactory(smtp.SMTPFactory):
+
+ def __init__(self, host):
+ self.protocol = smtp.ESMTP
+ self.host=host
+ _portal = portal.Portal(SmtpRealm(self.host))
+ _portal.registerChecker(SatProfileCredentialChecker(self.host))
+ smtp.SMTPFactory.__init__(self, _portal)
+
+ def startedConnecting(self, connector):
+ debug (_("SMTP server connection started"))
+ smtp.SMTPFactory.startedConnecting(self, connector)
+
+ def clientConnectionLost(self, connector, reason):
+ debug (_("SMTP server connection lost (reason: %s)"), reason)
+ smtp.SMTPFactory.clientConnectionLost(self, connector, reason)
+
+ def buildProtocol(self, addr):
+ p = smtp.SMTPFactory.buildProtocol(self, addr)
+ # add the challengers from imap4, more secure and complicated challengers are available
+ p.challengers = {"LOGIN": LOGINCredentials, "PLAIN": PLAINCredentials}
+ return p
+
diff --git a/src/sat.tac b/src/sat.tac
index 8ef76058..6d9f50c4 100644
--- a/src/sat.tac
+++ b/src/sat.tac
@@ -591,7 +591,7 @@ class SAT(service.Service):
## jabber methods ##
- def sendMessage(self,to,msg,type='chat', profile_key='@DEFAULT@'):
+ def sendMessage(self, to, msg, subject=None, type='chat', profile_key='@DEFAULT@'):
#FIXME: check validity of recipient
profile = self.memory.getProfileName(profile_key)
assert(profile)
@@ -601,6 +601,8 @@ class SAT(service.Service):
message["to"] = jid.JID(to).full()
message["from"] = current_jid.full()
message["type"] = type
+ if subject:
+ message.addElement("subject", "jabber:client", subject)
message.addElement("body", "jabber:client", msg)
self.profiles[profile].xmlstream.send(message)
self.memory.addToHistory(current_jid, current_jid, jid.JID(to), message["type"], unicode(msg))