summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/leap/bitmask/core/configurable.py174
-rw-r--r--src/leap/bitmask/core/dispatcher.py1
-rw-r--r--src/leap/bitmask/core/mail_services.py64
3 files changed, 52 insertions, 187 deletions
diff --git a/src/leap/bitmask/core/configurable.py b/src/leap/bitmask/core/configurable.py
index 3b97916d..8e33de95 100644
--- a/src/leap/bitmask/core/configurable.py
+++ b/src/leap/bitmask/core/configurable.py
@@ -18,15 +18,15 @@
Configurable Backend for Bitmask Service.
"""
import ConfigParser
-import locale
import os
-import re
-import sys
from twisted.application import service
-from twisted.python import log
from leap.common import files
+from leap.common.config import get_path_prefix
+
+
+DEFAULT_BASEDIR = os.path.join(get_path_prefix(), 'leap')
class MissingConfigEntry(Exception):
@@ -40,7 +40,7 @@ class ConfigurableService(service.MultiService):
config_file = u"bitmaskd.cfg"
service_names = ('mail', 'eip', 'zmq', 'web')
- def __init__(self, basedir='~/.config/leap'):
+ def __init__(self, basedir=DEFAULT_BASEDIR):
service.MultiService.__init__(self)
path = os.path.abspath(os.path.expanduser(basedir))
@@ -61,10 +61,9 @@ class ConfigurableService(service.MultiService):
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
if default is None:
- fn = os.path.join(self.basedir, self.config_file)
+ fn = self._get_config_path()
raise MissingConfigEntry("%s is missing the [%s]%s entry"
- % (_quote_output(fn),
- section, option))
+ % fn, section, option)
return default
def set_config(self, section, option, value):
@@ -82,12 +81,8 @@ class ConfigurableService(service.MultiService):
if not os.path.isfile(bitmaskd_cfg):
self._create_default_config(bitmaskd_cfg)
- try:
- with open(bitmaskd_cfg, "rb") as f:
- self.config.readfp(f)
- except EnvironmentError:
- if os.path.exists(bitmaskd_cfg):
- raise
+ with open(bitmaskd_cfg, "rb") as f:
+ self.config.readfp(f)
def save_config(self):
bitmaskd_cfg = self._get_config_path()
@@ -109,154 +104,3 @@ eip = True
zmq = True
web = False
"""
-
-
-def canonical_encoding(encoding):
- if encoding is None:
- log.msg("Warning: falling back to UTF-8 encoding.", level=log.WEIRD)
- encoding = 'utf-8'
- encoding = encoding.lower()
- if encoding == "cp65001":
- encoding = 'utf-8'
- elif (encoding == "us-ascii" or encoding == "646" or encoding ==
- "ansi_x3.4-1968"):
- encoding = 'ascii'
-
- return encoding
-
-
-def check_encoding(encoding):
- # sometimes Python returns an encoding name that it doesn't support for
- # conversion fail early if this happens
- try:
- u"test".encode(encoding)
- except (LookupError, AttributeError):
- raise AssertionError(
- "The character encoding '%s' is not supported for conversion." % (
- encoding,))
-
-filesystem_encoding = None
-io_encoding = None
-is_unicode_platform = False
-
-
-def _reload():
- global filesystem_encoding, io_encoding, is_unicode_platform
-
- filesystem_encoding = canonical_encoding(sys.getfilesystemencoding())
- check_encoding(filesystem_encoding)
-
- if sys.platform == 'win32':
- # On Windows we install UTF-8 stream wrappers for sys.stdout and
- # sys.stderr, and reencode the arguments as UTF-8 (see
- # scripts/runner.py).
- io_encoding = 'utf-8'
- else:
- ioenc = None
- if hasattr(sys.stdout, 'encoding'):
- ioenc = sys.stdout.encoding
- if ioenc is None:
- try:
- ioenc = locale.getpreferredencoding()
- except Exception:
- pass # work around <http://bugs.python.org/issue1443504>
- io_encoding = canonical_encoding(ioenc)
-
- check_encoding(io_encoding)
-
- is_unicode_platform = sys.platform in ["win32", "darwin"]
-
-_reload()
-
-
-def _quote_output(s, quotemarks=True, quote_newlines=None, encoding=None):
- """
- Encode either a Unicode string or a UTF-8-encoded bytestring for
- representation on stdout or stderr, tolerating errors. If 'quotemarks' is
- True, the string is always quoted; otherwise, it is quoted only if
- necessary to avoid ambiguity or control bytes in the output. (Newlines are
- counted as control bytes iff quote_newlines is True.)
-
- Quoting may use either single or double quotes. Within single quotes, all
- characters stand for themselves, and ' will not appear. Within double
- quotes, Python-compatible backslash escaping is used.
-
- If not explicitly given, quote_newlines is True when quotemarks is True.
- """
- assert isinstance(s, (str, unicode))
- if quote_newlines is None:
- quote_newlines = quotemarks
-
- if isinstance(s, str):
- try:
- s = s.decode('utf-8')
- except UnicodeDecodeError:
- return 'b"%s"' % (
- ESCAPABLE_8BIT.sub(
- lambda m: _str_escape(m, quote_newlines), s),)
-
- must_double_quote = (quote_newlines and MUST_DOUBLE_QUOTE_NL or
- MUST_DOUBLE_QUOTE)
- if must_double_quote.search(s) is None:
- try:
- out = s.encode(encoding or io_encoding)
- if quotemarks or out.startswith('"'):
- return "'%s'" % (out,)
- else:
- return out
- except (UnicodeDecodeError, UnicodeEncodeError):
- pass
-
- escaped = ESCAPABLE_UNICODE.sub(
- lambda m: _unicode_escape(m, quote_newlines), s)
- return '"%s"' % (
- escaped.encode(encoding or io_encoding, 'backslashreplace'),)
-
-
-def _unicode_escape(m, quote_newlines):
- u = m.group(0)
- if u == u'"' or u == u'$' or u == u'`' or u == u'\\':
- return u'\\' + u
- elif u == u'\n' and not quote_newlines:
- return u
- if len(u) == 2:
- codepoint = (
- ord(u[0]) - 0xD800) * 0x400 + ord(u[1]) - 0xDC00 + 0x10000
- else:
- codepoint = ord(u)
- if codepoint > 0xFFFF:
- return u'\\U%08x' % (codepoint,)
- elif codepoint > 0xFF:
- return u'\\u%04x' % (codepoint,)
- else:
- return u'\\x%02x' % (codepoint,)
-
-
-def _str_escape(m, quote_newlines):
- c = m.group(0)
- if c == '"' or c == '$' or c == '`' or c == '\\':
- return '\\' + c
- elif c == '\n' and not quote_newlines:
- return c
- else:
- return '\\x%02x' % (ord(c),)
-
-MUST_DOUBLE_QUOTE_NL = re.compile(
- ur'[^\x20-\x26\x28-\x7E\u00A0-\uD7FF\uE000-\uFDCF\uFDF0-\uFFFC]',
- re.DOTALL)
-MUST_DOUBLE_QUOTE = re.compile(
- ur'[^\n\x20-\x26\x28-\x7E\u00A0-\uD7FF\uE000-\uFDCF\uFDF0-\uFFFC]',
- re.DOTALL)
-
-ESCAPABLE_8BIT = re.compile(
- r'[^ !#\x25-\x5B\x5D-\x5F\x61-\x7E]',
- re.DOTALL)
-
-# if we must double-quote, then we have to escape ", $ and `, but need not
-# escape '
-
-ESCAPABLE_UNICODE = re.compile(
- ur'([\uD800-\uDBFF][\uDC00-\uDFFF])|' # valid surrogate pairs
- ur'[^ !#\x25-\x5B\x5D-\x5F\x61-\x7E\u00A0-\uD7FF'
- ur'\uE000-\uFDCF\uFDF0-\uFFFC]',
- re.DOTALL)
diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py
index 4d7e1813..648cbe9b 100644
--- a/src/leap/bitmask/core/dispatcher.py
+++ b/src/leap/bitmask/core/dispatcher.py
@@ -84,6 +84,7 @@ class CommandDispatcher(object):
return d
def do_EIP(self, *parts):
+
subcmd = parts[1]
eip_label = 'eip'
diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py
index a0be1211..6472bdc2 100644
--- a/src/leap/bitmask/core/mail_services.py
+++ b/src/leap/bitmask/core/mail_services.py
@@ -1,3 +1,19 @@
+# -*- coding: utf-8 -*-
+# mail_services.py
+# Copyright (C) 2016 LEAP Encryption Acess Project
+#
+# 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/>.
"""
Mail services.
@@ -6,7 +22,6 @@ This should be moved to the different packages when it stabilizes.
"""
import json
import os
-from glob import glob
from collections import defaultdict
from collections import namedtuple
@@ -26,31 +41,30 @@ from leap.mail.incoming.service import IncomingMail, INCOMING_CHECK_PERIOD
from leap.mail import smtp
from leap.bitmask.core.uuid_map import UserMap
+from leap.bitmask.core.configurable import DEFAULT_BASEDIR
class Container(object):
- def __init__(self):
+ def __init__(self, service=None):
self._instances = defaultdict(None)
+ if service is not None:
+ self.service = service
def get_instance(self, key):
return self._instances.get(key, None)
+ def add_instance(self, key, data):
+ self._instances[key] = data
+
class ImproperlyConfigured(Exception):
pass
-def get_all_soledad_uuids():
- return [os.path.split(p)[-1].split('.db')[0] for p in
- glob(os.path.expanduser('~/.config/leap/soledad/*.db'))]
- # FIXME do not hardcode basedir
-
-
class SoledadContainer(Container):
- def __init__(self, basedir='~/.config/leap'):
- # FIXME do not hardcode basedir
+ def __init__(self, basedir=DEFAULT_BASEDIR):
self._basedir = os.path.expanduser(basedir)
self._usermap = UserMap()
super(SoledadContainer, self).__init__()
@@ -75,19 +89,17 @@ class SoledadContainer(Container):
uuid, passphrase, soledad_path, soledad_url,
cert_path, token)
- self._instances[userid] = soledad
+ self.add_instances(userid, soledad)
data = {'user': userid, 'uuid': uuid, 'token': token,
'soledad': soledad}
self.service.trigger_hook('on_new_soledad_instance', **data)
- def _create_soledad_instance(self, uuid, passphrase, basedir, server_url,
- cert_file, token):
+ def _create_soledad_instance(self, uuid, passphrase, soledad_path,
+ server_url, cert_file, token):
# setup soledad info
- secrets_path = os.path.join(
- basedir, '%s.secret' % uuid)
- local_db_path = os.path.join(
- basedir, '%s.db' % uuid)
+ secrets_path = os.path.join(soledad_path, '%s.secret' % uuid)
+ local_db_path = os.path.join(soledad_path, '%s.db' % uuid)
if token is None:
syncable = False
@@ -143,8 +155,7 @@ class SoledadService(HookableService):
def startService(self):
log.msg('Starting Soledad Service')
- self._container = SoledadContainer()
- self._container.service = self
+ self._container = SoledadContainer(service=self)
super(SoledadService, self).startService()
# hooks
@@ -209,7 +220,7 @@ class KeymanagerContainer(Container):
def _on_keymanager_ready_cb(self, keymanager, userid, soledad):
# TODO use onready-deferreds instead
- self._instances[userid] = keymanager
+ self.add_instance(userid, keymanager)
log.msg("Adding Keymanager instance for: %s" % userid)
data = {'userid': userid, 'soledad': soledad, 'keymanager': keymanager}
@@ -264,6 +275,11 @@ class KeymanagerContainer(Container):
token = self.service.tokens.get(userid)
km_args = (userid, nickserver_uri, soledad)
+
+ # TODO use the method in
+ # services.soledadbootstrapper._get_gpg_bin_path.
+ # That should probably live in keymanager package.
+
km_kwargs = {
"token": token, "uid": uuid,
"api_uri": api_uri, "api_version": "1",
@@ -373,10 +389,12 @@ class StandardMailService(service.MultiService, HookableService):
def initializeChildrenServices(self):
self.addService(IMAPService(self._soledad_sessions))
- self.addService(IncomingMailService(self))
self.addService(SMTPService(
self._soledad_sessions, self._keymanager_sessions,
self._sendmail_opts))
+ # TODO adapt the service to receive soledad/keymanager sessions object.
+ # See also the TODO before IncomingMailService.startInstance
+ self.addService(IncomingMailService(self))
def startService(self):
log.msg('Starting Mail Service...')
@@ -438,6 +456,7 @@ class StandardMailService(service.MultiService, HookableService):
if not active_user:
return defer.succeed('NO ACTIVE USER')
token = self._imap_tokens.get(active_user)
+ # TODO return just the tuple, no format.
return defer.succeed("IMAP TOKEN (%s): %s" % (active_user, token))
def get_smtp_token(self):
@@ -445,6 +464,7 @@ class StandardMailService(service.MultiService, HookableService):
if not active_user:
return defer.succeed('NO ACTIVE USER')
token = self._smtp_tokens.get(active_user)
+ # TODO return just the tuple, no format.
return defer.succeed("SMTP TOKEN (%s): %s" % (active_user, token))
def do_get_smtp_cert_path(self, userid):
@@ -560,7 +580,7 @@ class IncomingMailService(service.Service):
if start_sync:
incoming_instance.startService()
- acc = Account(soledad)
+ acc = Account(soledad, userid)
d = acc.callWhenReady(
lambda _: acc.get_collection_by_mailbox(INBOX_NAME))
d.addCallback(setUpIncomingMail)