connection.py 8.48 KB
Newer Older
louiz’'s avatar
louiz’ committed
1
# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org>
2
3
4
5
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
6
# it under the terms of the zlib license. See the COPYING file.
7

8
9
10
11
"""
Defines the Connection class
"""

12
13
14
import logging
log = logging.getLogger(__name__)

15

16
import getpass
mathieui's avatar
mathieui committed
17
import subprocess
18
import sys
mathieui's avatar
mathieui committed
19

louiz’'s avatar
louiz’ committed
20
21
import slixmpp
from slixmpp.plugins.xep_0184 import XEP_0184
22

23
import common
24
import fixes
25
from common import safeJID
26
from config import config, options
27

louiz’'s avatar
louiz’ committed
28
class Connection(slixmpp.ClientXMPP):
29
    """
30
31
    Receives everything from Jabber and emits the
    appropriate signals
32
    """
louiz’'s avatar
louiz’ committed
33
    __init = False
34
    def __init__(self):
35
        resource = config.get('resource')
mathieui's avatar
mathieui committed
36
37
38
39

        keyfile = config.get('keyfile')
        certfile = config.get('certfile')

40
        if config.get('jid'):
41
42
            # Field used to know if we are anonymous or not.
            # many features will be handled differently
43
            # depending on this setting
44
            self.anon = False
45
            jid = '%s' % config.get('jid')
46
            if resource:
47
                jid = '%s/%s'% (jid, resource)
mathieui's avatar
mathieui committed
48
            password = config.get('password')
mathieui's avatar
mathieui committed
49
50
            eval_password = config.get('eval_password')
            if not password and not eval_password and not (keyfile and certfile):
mathieui's avatar
mathieui committed
51
                password = getpass.getpass()
mathieui's avatar
mathieui committed
52
            elif not password and not (keyfile and certfile):
53
                sys.stderr.write("No password or certificates provided, using the eval_password command.\n")
mathieui's avatar
mathieui committed
54
55
                process = subprocess.Popen(['sh', '-c', eval_password], stdin=subprocess.PIPE,
                                                        stdout=subprocess.PIPE, close_fds=True)
56
57
58
59
60
61
                code = process.wait()
                if code != 0:
                    sys.stderr.write('The eval_password command (%s) returned a '
                                     'nonzero status code: %s.\n' % (eval_password, code))
                    sys.stderr.write('Poezio will now exit\n')
                    sys.exit(code)
mathieui's avatar
mathieui committed
62
                password = process.stdout.readline().decode('utf-8').strip('\n')
63
        else: # anonymous auth
64
            self.anon = True
65
            jid = config.get('server')
66
67
            if resource:
                jid = '%s/%s' % (jid, resource)
68
            password = None
69
        jid = safeJID(jid)
mathieui's avatar
mathieui committed
70
        # TODO: use the system language
louiz’'s avatar
louiz’ committed
71
        slixmpp.ClientXMPP.__init__(self, jid, password,
72
                                      lang=config.get('lang'))
73

74
        force_encryption = config.get('force_encryption')
mathieui's avatar
mathieui committed
75
76
77
78
79
80
        if force_encryption:
            self['feature_mechanisms'].unencrypted_plain = False
            self['feature_mechanisms'].unencrypted_digest = False
            self['feature_mechanisms'].unencrypted_cram = False
            self['feature_mechanisms'].unencrypted_scram = False

mathieui's avatar
mathieui committed
81
82
83
84
85
86
87
        self.keyfile = config.get('keyfile')
        self.certfile = config.get('certfile')
        if keyfile and not certfile:
            log.error('keyfile is present in configuration file without certfile')
        elif certfile and not keyfile:
            log.error('certfile is present in configuration file without keyfile')

88
        self.core = None
89
        self.auto_reconnect = config.get('auto_reconnect')
mathieui's avatar
mathieui committed
90
        self.reconnect_max_attempts = 0
louiz’'s avatar
louiz’ committed
91
        self.auto_authorize = None
92
93
        # prosody defaults, lowest is AES128-SHA, it should be a minimum
        # for anything that came out after 2002
94
95
96
        self.ciphers = config.get('ciphers',
                                  'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK'
                                    ':!SRP:!3DES:!aNULL')
97
98
99
        self.ca_certs = config.get('ca_cert_path') or None
        interval = config.get('whitespace_interval')
        if int(interval) > 0:
mathieui's avatar
mathieui committed
100
101
            self.whitespace_keepalive_interval = int(interval)
        else:
102
            self.whitespace_keepalive = False
103
        self.register_plugin('xep_0004')
104
        self.register_plugin('xep_0012')
105
        self.register_plugin('xep_0030')
106
        self.register_plugin('xep_0045')
mathieui's avatar
mathieui committed
107
        self.register_plugin('xep_0048')
108
        self.register_plugin('xep_0050')
mathieui's avatar
mathieui committed
109
        self.register_plugin('xep_0054')
110
        self.register_plugin('xep_0060')
111
        self.register_plugin('xep_0066')
112
        self.register_plugin('xep_0071')
113
114
        self.register_plugin('xep_0077')
        self.plugin['xep_0077'].create_account = False
115
        self.register_plugin('xep_0085')
mathieui's avatar
mathieui committed
116
        self.register_plugin('xep_0115')
117

118
119
120
        # monkey-patch xep_0184 to avoid requesting receipts for messages
        # without a body
        XEP_0184._filter_add_receipt_request = fixes._filter_add_receipt_request
121
        self.register_plugin('xep_0184')
122
123
        self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts')
        self.plugin['xep_0184'].auto_request = config.get('request_message_receipts')
124

125
        self.register_plugin('xep_0191')
mathieui's avatar
mathieui committed
126
        self.register_plugin('xep_0198')
127
        self.register_plugin('xep_0199')
128

129
        if config.get('enable_user_tune'):
130
            self.register_plugin('xep_0118')
mathieui's avatar
mathieui committed
131

132
        if config.get('enable_user_nick'):
mathieui's avatar
mathieui committed
133
            self.register_plugin('xep_0172')
mathieui's avatar
mathieui committed
134

135
        if config.get('enable_user_mood'):
mathieui's avatar
mathieui committed
136
137
            self.register_plugin('xep_0107')

138
        if config.get('enable_user_activity'):
mathieui's avatar
mathieui committed
139
140
            self.register_plugin('xep_0108')

141
        if config.get('enable_user_gaming'):
mathieui's avatar
mathieui committed
142
143
            self.register_plugin('xep_0196')

144
        if config.get('send_poezio_info'):
145
            info = {'name':'poezio',
146
                    'version': options.version}
147
            if config.get('send_os_info'):
148
                info['os'] = common.get_os_info()
mathieui's avatar
mathieui committed
149
150
            self.plugin['xep_0030'].set_identities(
                    identities=set([('client', 'pc', None, 'Poezio')]))
151
152
        else:
            info = {'name': '', 'version': ''}
mathieui's avatar
mathieui committed
153
154
            self.plugin['xep_0030'].set_identities(
                    identities=set([('client', 'pc', None, '')]))
155
        self.register_plugin('xep_0092', pconfig=info)
156
        if config.get('send_time'):
157
            self.register_plugin('xep_0202')
158
        self.register_plugin('xep_0224')
159
        self.register_plugin('xep_0231')
160
        self.register_plugin('xep_0249')
mathieui's avatar
mathieui committed
161
        self.register_plugin('xep_0257')
mathieui's avatar
mathieui committed
162
163
        self.register_plugin('xep_0280')
        self.register_plugin('xep_0297')
Link Mauve's avatar
Link Mauve committed
164
        self.register_plugin('xep_0308')
165
        self.register_plugin('xep_0319')
166
        self.register_plugin('xep_0334')
mathieui's avatar
mathieui committed
167
        self.register_plugin('xep_0352')
louiz’'s avatar
louiz’ committed
168
        self.init_plugins()
169

170
171
    def set_keepalive_values(self, option=None, value=None):
        """
172
        Called after the XMPP session has been started, or triggered when one of
173
        "connection_timeout_delay" and "connection_check_interval" options
174
        is changed.  Unload and reload the ping plugin, with the new values.
175
        """
176
177
178
179
        if not self.is_connected():
            # Happens when we change the value with /set while we are not
            # connected. Do nothing in that case
            return
180
181
        ping_interval = config.get('connection_check_interval')
        timeout_delay = config.get('connection_timeout_delay')
182
        if timeout_delay <= 0:
183
184
185
186
187
            # We help the stupid user (with a delay of 0, poezio will try to
            # reconnect immediately because the timeout is immediately
            # passed)
            # 1 second is short, but, well
            timeout_delay = 1
188
        self.plugin['xep_0199'].disable_keepalive()
189
190
        # If the ping_interval is 0 or less, we just disable the keepalive
        if ping_interval > 0:
191
192
            self.plugin['xep_0199'].enable_keepalive(ping_interval,
                                                     timeout_delay)
193

194
    def start(self):
195
196
197
        """
        Connect and process events.
        """
198
        custom_host = config.get('custom_host')
199
        custom_port = config.get('custom_port', 5222)
200
201
        if custom_port == -1:
            custom_port = 5222
202
        if custom_host:
louiz’'s avatar
louiz’ committed
203
            self.connect((custom_host, custom_port))
mathieui's avatar
mathieui committed
204
        elif custom_port != 5222 and custom_port != -1:
louiz’'s avatar
louiz’ committed
205
            self.connect((self.boundjid.host, custom_port))
206
        else:
louiz’'s avatar
louiz’ committed
207
            self.connect()
208

louiz’'s avatar
louiz’ committed
209
    def send_raw(self, data):
210
211
212
213
214
        """
        Overrides XMLStream.send_raw, with an event added
        """
        if self.core:
            self.core.outgoing_stanza(data)
louiz’'s avatar
louiz’ committed
215
        slixmpp.ClientXMPP.send_raw(self, data)
216

louiz’'s avatar
louiz’ committed
217
class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase):
mathieui's avatar
mathieui committed
218
219
220
    """
    Callback to retrieve all the stanzas for the XML tab
    """
221
    def match(self, xml):
222
        "match everything"
223
        return True