comparison sat/plugins/plugin_xep_0070.py @ 2562:26edcf3a30eb

core, setup: huge cleaning: - moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention - move twisted directory to root - removed all hacks from setup.py, and added missing dependencies, it is now clean - use https URL for website in setup.py - removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed - renamed sat.sh to sat and fixed its installation - added python_requires to specify Python version needed - replaced glib2reactor which use deprecated code by gtk3reactor sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author Goffi <goffi@goffi.org>
date Mon, 02 Apr 2018 19:44:50 +0200
parents src/plugins/plugin_xep_0070.py@33c8c4973743
children 56f94936df1e
comparison
equal deleted inserted replaced
2561:bd30dc3ffe5a 2562:26edcf3a30eb
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # SAT plugin for managing xep-0070
5 # Copyright (C) 2009-2016 Geoffrey POUZET (chteufleur@kingpenguin.tk)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 from sat.core.i18n import _, D_
20 from sat.core.constants import Const as C
21 from sat.core.log import getLogger
22 from twisted.words.protocols.jabber import xmlstream
23 from twisted.words.protocols import jabber
24 log = getLogger(__name__)
25 from sat.tools import xml_tools
26
27 from wokkel import disco, iwokkel
28 from zope.interface import implements
29 try:
30 from twisted.words.protocols.xmlstream import XMPPHandler
31 except ImportError:
32 from wokkel.subprotocols import XMPPHandler
33
34
35 NS_HTTP_AUTH = 'http://jabber.org/protocol/http-auth'
36
37 IQ = 'iq'
38 IQ_GET = '/'+IQ+'[@type="get"]'
39 IQ_HTTP_AUTH_REQUEST = IQ_GET + '/confirm[@xmlns="' + NS_HTTP_AUTH + '"]'
40
41 MSG = 'message'
42 MSG_GET = '/'+MSG+'[@type="normal"]'
43 MSG_HTTP_AUTH_REQUEST = MSG_GET + '/confirm[@xmlns="' + NS_HTTP_AUTH + '"]'
44
45
46 PLUGIN_INFO = {
47 C.PI_NAME: "XEP-0070 Plugin",
48 C.PI_IMPORT_NAME: "XEP-0070",
49 C.PI_TYPE: "XEP",
50 C.PI_PROTOCOLS: ["XEP-0070"],
51 C.PI_DEPENDENCIES: [],
52 C.PI_MAIN: "XEP_0070",
53 C.PI_HANDLER: "yes",
54 C.PI_DESCRIPTION: _("""Implementation of HTTP Requests via XMPP""")
55 }
56
57
58 class XEP_0070(object):
59 """
60 Implementation for XEP 0070.
61 """
62
63 def __init__(self, host):
64 log.info(_(u"Plugin XEP_0070 initialization"))
65 self.host = host
66 self._dictRequest = dict()
67
68 def getHandler(self, client):
69 return XEP_0070_handler(self, client.profile)
70
71 def onHttpAuthRequestIQ(self, iq_elt, client):
72 """This method is called on confirmation request received (XEP-0070 #4.5)
73
74 @param iq_elt: IQ element
75 @param client: %(doc_client)s
76 """
77 log.info(_("XEP-0070 Verifying HTTP Requests via XMPP (iq)"))
78 self._treatHttpAuthRequest(iq_elt, IQ, client)
79
80 def onHttpAuthRequestMsg(self, msg_elt, client):
81 """This method is called on confirmation request received (XEP-0070 #4.5)
82
83 @param msg_elt: message element
84 @param client: %(doc_client)s
85 """
86 log.info(_("XEP-0070 Verifying HTTP Requests via XMPP (message)"))
87 self._treatHttpAuthRequest(msg_elt, MSG, client)
88
89 def _treatHttpAuthRequest(self, elt, stanzaType, client):
90 elt.handled = True
91 auth_elt = elt.elements(NS_HTTP_AUTH, 'confirm').next()
92 auth_id = auth_elt['id']
93 auth_method = auth_elt['method']
94 auth_url = auth_elt['url']
95 self._dictRequest[client] = (auth_id, auth_method, auth_url, stanzaType, elt)
96
97 confirm_ui = xml_tools.XMLUI("form", title=D_(u"Auth confirmation"), submit_id='')
98 confirm_ui.addText(D_(u"{} needs to validate your identity, do you agreeĀ ?".format(auth_url)))
99 confirm_ui.addText(D_(u"Validation code : {}".format(auth_id)))
100 confirm_ui.addText(D_(u"Please check that this code is the same as on {}".format(auth_url)))
101 confirm_ui.addText(u"")
102 confirm_ui.addText(D_(u"Submit to authorize, cancel otherwise."))
103 d = xml_tools.deferredUI(self.host, confirm_ui, chained=False)
104 d.addCallback(self._authRequestCallback, client.profile)
105 self.host.actionNew({u"xmlui": confirm_ui.toXml()}, profile=client.profile)
106
107 def _authRequestCallback(self, result, profile):
108 client = self.host.getClient(profile)
109 try:
110 cancelled = result['cancelled']
111 except KeyError:
112 cancelled = False
113
114 authorized = False
115
116 if cancelled:
117 auth_id, auth_method, auth_url, stanzaType, elt = self._dictRequest[client]
118 del self._dictRequest[client]
119 authorized = False
120 else:
121 try:
122 auth_id, auth_method, auth_url, stanzaType, elt = self._dictRequest[client]
123 del self._dictRequest[client]
124 authorized = True
125 except KeyError:
126 authorized = False
127
128 if authorized:
129 if (stanzaType == IQ):
130 # iq
131 log.debug(_(u"XEP-0070 reply iq"))
132 iq_result_elt = xmlstream.toResponse(elt, 'result')
133 client.send(iq_result_elt)
134 elif (stanzaType == MSG):
135 # message
136 log.debug(_(u"XEP-0070 reply message"))
137 msg_result_elt = xmlstream.toResponse(elt, 'result')
138 msg_result_elt.addChild(elt.elements(NS_HTTP_AUTH, 'confirm').next())
139 client.send(msg_result_elt)
140 else:
141 log.debug(_(u"XEP-0070 reply error"))
142 result_elt = jabber.error.StanzaError("not-authorized").toResponse(elt)
143 client.send(result_elt)
144
145
146 class XEP_0070_handler(XMPPHandler):
147 implements(iwokkel.IDisco)
148
149 def __init__(self, plugin_parent, profile):
150 self.plugin_parent = plugin_parent
151 self.host = plugin_parent.host
152 self.profile = profile
153
154 def connectionInitialized(self):
155 self.xmlstream.addObserver(IQ_HTTP_AUTH_REQUEST, self.plugin_parent.onHttpAuthRequestIQ, client=self.parent)
156 self.xmlstream.addObserver(MSG_HTTP_AUTH_REQUEST, self.plugin_parent.onHttpAuthRequestMsg, client=self.parent)
157
158 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
159 return [disco.DiscoFeature(NS_HTTP_AUTH)]
160
161 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
162 return []