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
128
|
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,
bootstrap=True)
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 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",))
log.msg("added msg")
self._soledad.delete_doc(doc)
log.msg("deleted doc")
|