summaryrefslogtreecommitdiff
path: root/src/leap/mail/imap/fetch.py
blob: bcd89014d5624b2b0a9946eb7b0190be1d6760b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import json
import os
#import hmac

from xdg import BaseDirectory

from twisted.python import log

from leap.common.check import leap_assert
from leap.soledad import Soledad

from leap.common.keymanager import openpgp


class LeapIncomingMail(object):
    """
    Fetches mail from the incoming queue.
    """
    def __init__(self, keymanager, user_uuid, soledad_pass, server_url,
                 server_pemfile, token, imap_account,
                 **kwargs):
        """
        Initialize LeapIMAP.

        :param user: The user adress in the form C{user@provider}.
        :type user: str

        :param soledad_pass: The password for the local database replica.
        :type soledad_pass: str

        :param server_url: The URL of the remote server to sync against.
        :type couch_url: str

        :param server_pemfile: The pemfile for the remote sync server TLS
                               handshake.
        :type server_pemfile: str

        :param token: a session token valid for this user.
        :type token: str

        :param imap_account: a SoledadBackedAccount instance to which
                             the incoming mail will be saved to

        :param **kwargs: Used to pass arguments to Soledad instance. Maybe
            Soledad instantiation could be factored out from here, and maybe
            we should have a standard for all client code.
        """
        leap_assert(user_uuid, "need an user uuid to initialize")

        self._keymanager = keymanager
        self._user_uuid = user_uuid
        self._server_url = server_url
        self._soledad_pass = soledad_pass

        base_config = BaseDirectory.xdg_config_home
        secret_path = os.path.join(
            base_config, "leap", "soledad", "%s.secret" % user_uuid)
        soledad_path = os.path.join(
            base_config, "leap", "soledad", "%s-incoming.u1db" % user_uuid)

        self.imapAccount = imap_account
        self._soledad = Soledad(
            user_uuid,
            soledad_pass,
            secret_path,
            soledad_path,
            server_url,
            server_pemfile,
            token)

        self._pkey = self._keymanager.get_all_keys_in_local_db(
            private=True).pop()
        log.msg('fetcher got soledad instance')

    def fetch(self):
        """
        Get new mail by syncing database, store it in the INBOX for the
        user account, and remove from the incoming db.
        """
        self._soledad.sync()

        #log.msg('getting all docs')
        gen, doclist = self._soledad.get_all_docs()
        #log.msg("there are %s docs" % (len(doclist),))

        if doclist:
            inbox = self.imapAccount.getMailbox('inbox')

        #import ipdb; ipdb.set_trace()

        key = self._pkey
        for doc in doclist:
            keys = doc.content.keys()
            if '_enc_scheme' in keys and '_enc_json' in keys:

                # XXX should check for _enc_scheme == "pubkey" || "none"
                # that is what incoming mail uses.

                encdata = doc.content['_enc_json']
                decrdata = openpgp.decrypt_asym(
                    encdata, key,
                    passphrase=self._soledad_pass)
                if decrdata:
                    self.process_decrypted(doc, decrdata, inbox)
        # XXX launch sync callback

    def process_decrypted(self, doc, data, inbox):
        """
        Process a successfully decrypted message
        """
        log.msg("processing incoming message!")
        msg = json.loads(data)
        if not isinstance(msg, dict):
            return False
        if not msg.get('incoming', False):
            return False
        # ok, this is an incoming message
        rawmsg = msg.get('content', None)
        if not rawmsg:
            return False
        #log.msg("we got raw message")

        # add to inbox and delete from soledad
        inbox.addMessage(rawmsg, ("\\Recent",))
        doc_id = doc.doc_id
        self._soledad.delete_doc(doc)
        log.msg("deleted doc %s from incoming" % doc_id)