Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16569321
D167.id1739.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D167.id1739.diff
View Options
diff --git a/conf/kolab.conf b/conf/kolab.conf
--- a/conf/kolab.conf
+++ b/conf/kolab.conf
@@ -428,9 +428,51 @@
result_attribute = mail
[wallace]
-modules = resources, invitationpolicy, footer
-footer_text = /etc/kolab/footer.text
-footer_html = /etc/kolab/footer.html
+; List the modules to load and apply, in order.
+;
+; Available modules include;
+;
+; * resources
+; * invitationpolicy
+; * footer
+; * signature
+;
+modules = resources, invitationpolicy
+
+; Footer module settings
+;footer_text = /etc/kolab/footer.text
+;footer_html = /etc/kolab/footer.html
+
+; Signature module settings
+;
+; Two modes: write out the exact signature in /etc/kolab/ as html and/or text,
+; or use rules.
+;
+; If files are configured, rules do not apply. If files are configured, a
+; fallback is /etc/kolab/signature_default.{html,txt}.
+;
+; signature_file_html = /etc/kolab/signature.d/%(mail)s.html
+; signature_file_text = /etc/kolab/signature.d/%(mail)s.txt
+;
+; A list of dicts, with each dict holding an attribute name ("o", "cn",
+; "entrydn"), and a regular expression to be matched against the attribute
+; value.
+;
+; The module takes the first match, and uses the "html" and "text" files as
+; templates.
+;
+;signature_rules = [
+; {
+; "entrydn": "uid=.*,ou=IT,ou=People,dc=example,dc=org",
+; "html": "/etc/kolab/signature_IT.html",
+; "text": "/etc/kolab/signature_IT.txt"
+; },
+; {
+; "entrydn": "uid=.*,ou=Finance,ou=People,dc=example,dc=org",
+; "html": "/etc/kolab/signature_Finance.html",
+; "text": "/etc/kolab/signature_Finance.txt"
+; }
+; ]
; default settings for kolabInvitationPolicy LDAP attribute on user records
kolab_invitation_policy = ACT_ACCEPT_IF_NO_CONFLICT:example.org, ACT_MANUAL
diff --git a/conf/signature_IT.html b/conf/signature_IT.html
new file mode 100644
--- /dev/null
+++ b/conf/signature_IT.html
@@ -0,0 +1,13 @@
+<br clear="all"/>
+<hr>
+This is an example HTML signature.
+
+<p style="font-size: small; color: gray;">
+%(o)s
+
+%(manager:cn)s
+
+M: %(mobile)s
+T: %(telephonenumber)s
+W: https://it.services.inc
+</p>
diff --git a/conf/signature_IT.txt b/conf/signature_IT.txt
new file mode 100644
--- /dev/null
+++ b/conf/signature_IT.txt
@@ -0,0 +1,9 @@
+This is an example TEXT signature.
+
+%(o)s
+
+%(manager:cn)s
+
+M: %(mobile)s
+T: %(telephonenumber)s
+W: https://it.services.inc
diff --git a/wallace/module_signature.py b/wallace/module_signature.py
new file mode 100644
--- /dev/null
+++ b/wallace/module_signature.py
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2019 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+import json
+import os
+import re
+import tempfile
+
+from email.encoders import encode_quopri
+from email.parser import Parser
+from email.utils import getaddresses
+
+import modules
+import pykolab
+
+from pykolab.auth import Auth
+from pykolab.translate import _
+
+# pylint: disable=invalid-name
+log = pykolab.getLogger('pykolab.wallace')
+conf = pykolab.getConf()
+
+mybasepath = '/var/spool/pykolab/wallace/signature/'
+
+
+def __init__():
+ modules.register('signature', execute, description=description())
+
+
+def description():
+ return """Append a signature to messages."""
+
+
+def set_part_content(part, content):
+ # Reset old encoding and use quoted-printable (#5414)
+ del part['Content-Transfer-Encoding']
+ part.set_payload(content)
+ encode_quopri(part)
+
+ return True
+
+
+def attr_resolve(sender_info, attr):
+ try:
+ attr, attr_val = attr.split(':')
+ except ValueError:
+ return None
+
+ auth = Auth()
+ auth.connect()
+
+ values = []
+
+ if not isinstance(sender_info[attr], list):
+ sender_info[attr] = [sender_info[attr]]
+
+ for sender_attr_val in sender_info[attr]:
+ values.append(auth.get_entry_attribute(None, sender_attr_val, attr_val))
+
+ return ", ".join(values)
+
+
+# pylint: disable=too-many-branches,too-many-locals,too-many-statements
+def execute(*args, **kw): # noqa: C901
+ if not os.path.isdir(mybasepath):
+ os.makedirs(mybasepath)
+
+ for stage in ['incoming', 'ACCEPT']:
+ if not os.path.isdir(os.path.join(mybasepath, stage)):
+ os.makedirs(os.path.join(mybasepath, stage))
+
+ # TODO: Test for correct call.
+ filepath = args[0]
+
+ if 'stage' in kw:
+ log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8)
+ log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8)
+ if hasattr(modules, 'cb_action_%s' % (kw['stage'])):
+ log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8)
+ exec('modules.cb_action_%s(%r, %r)' % (kw['stage'], 'signature', filepath))
+ return
+
+ log.debug(_("Executing module signature for %r, %r") % (args, kw), level=8)
+
+ new_filepath = os.path.join(
+ '/var/spool/pykolab/wallace/signature/incoming',
+ os.path.basename(filepath)
+ )
+
+ os.rename(filepath, new_filepath)
+ filepath = new_filepath
+
+ # parse message
+ message = Parser().parse(open(filepath, 'r'))
+
+ sender_address = [
+ address for displayname, address in getaddresses(message.get_all('X-Kolab-From'))
+ ][0]
+
+ auth = Auth()
+ auth.connect()
+
+ sender_dn = auth.find_recipient(sender_address)
+ if not sender_dn:
+ exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT', 'signature', filepath))
+ return
+
+ sender_info = auth.get_entry_attributes(None, sender_dn, ['*', 'entrydn', 'manager'])
+
+ log.debug("Sender info: %r" % (sender_info), level=7)
+
+ signature_rules = conf.get_raw('wallace', 'signature_rules')
+
+ if signature_rules:
+ signature_rules = json.loads(signature_rules)
+
+ log.debug("Signature rules: %r" % (signature_rules), level=7)
+
+ signature_html = None
+ signature_text = None
+
+ sig_html_conf = conf.get_raw('wallace', 'signature_file_html')
+ sig_text_conf = conf.get_raw('wallace', 'signature_file_text')
+
+ if sig_html_conf and sig_text_conf:
+ _sig_html_conf = sig_html_conf % sender_info
+ _sig_text_conf = sig_text_conf % sender_info
+
+ if not os.path.exists(_sig_html_conf):
+ _sig_html_conf = '/etc/kolab/signature.d/default.html'
+
+ if not os.path.exists(_sig_text_conf):
+ _sig_text_conf = '/etc/kolab/signature.d/default.txt'
+
+ if os.path.exists(_sig_html_conf):
+ signature_html = open(_sig_html_conf, 'r').read()
+
+ if os.path.exists(_sig_text_conf):
+ signature_text = open(_sig_text_conf, 'r').read()
+
+ if not signature_html and not signature_text:
+ for signature_rule in signature_rules:
+ try:
+ for attr, regex in signature_rule.iteritems():
+ if attr == "html":
+ if not os.path.exists(signature_rule['html']):
+ raise ValueError
+ continue
+
+ if attr == "text":
+ if not os.path.exists(signature_rule['text']):
+ raise ValueError
+ continue
+
+ if attr in sender_info and re.match(regex, sender_info[attr], flags=re.IGNORECASE):
+ success = False
+
+ while not success:
+ try:
+ signature_html = open(signature_rule['html'], 'r').read() % sender_info
+ signature_text = open(signature_rule['text'], 'r').read() % sender_info
+
+ success = True
+
+ except KeyError as errmsg:
+ sender_info[errmsg] = attr_resolve(sender_info, errmsg)
+ except ValueError:
+ continue
+
+ if signature_html is None and signature_text is None:
+ exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT', 'signature', filepath))
+ return
+
+ signature_added = False
+
+ try:
+ _signature_added = message.get("X-Wallace-Signature")
+
+ # pylint: disable=broad-except
+ except Exception:
+ pass
+
+ if _signature_added == "YES":
+ exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','signature', filepath))
+ return
+
+ for part in message.walk():
+ disposition = None
+
+ try:
+ content_type = part.get_content_type()
+
+ # pylint: disable=broad-except
+ except Exception:
+ continue
+
+ try:
+ disposition = part.get("Content-Disposition")
+
+ # pylint: disable=broad-except
+ except Exception:
+ pass
+
+ log.debug("Walking message part: %s; disposition = %r" % (content_type, disposition), level=8)
+
+ if disposition is not None:
+ continue
+
+ if content_type == "text/plain":
+ content = part.get_payload(decode=True)
+ content += "\n\n-- \n%s" % (signature_text)
+ signature_added = set_part_content(part, content)
+
+ elif content_type == "text/html":
+ content = part.get_payload(decode=True)
+ append = "\n<!-- signature appended by Wallace -->\n" + signature_html
+ if "</body>" in content:
+ content = content.replace("</body>", append + "</body>")
+ else:
+ content = "<html><body>" + content + append + "</body></html>"
+ signature_added = set_part_content(part, content)
+
+ if signature_added:
+ log.debug("Signature attached.", level=8)
+ message.add_header("X-Wallace-Signature", "YES")
+
+ (fp, new_filepath) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/signature/ACCEPT")
+ os.write(fp, message.as_string())
+ os.close(fp)
+ os.unlink(filepath)
+
+ exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','signature', new_filepath))
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Oct 31, 8:21 AM (5 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10074870
Default Alt Text
D167.id1739.diff (10 KB)
Attached To
Mode
D167: Append signatures from templates using LDAP information and conditionals.
Attached
Detach File
Event Timeline
Log In to Comment