Mercurial > prosody-hg
comparison plugins/mod_c2s.lua @ 4684:dc70c4ffb66d
Merge timber->trunk - thanks everyone!
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Tue, 24 Apr 2012 21:59:20 +0100 |
| parents | 9df9e87d0339 |
| children | 142e21f6bf8e |
comparison
equal
deleted
inserted
replaced
| 4529:12621337471f | 4684:dc70c4ffb66d |
|---|---|
| 1 -- Prosody IM | |
| 2 -- Copyright (C) 2008-2010 Matthew Wild | |
| 3 -- Copyright (C) 2008-2010 Waqas Hussain | |
| 4 -- | |
| 5 -- This project is MIT/X11 licensed. Please see the | |
| 6 -- COPYING file in the source package for more information. | |
| 7 -- | |
| 8 | |
| 9 module:set_global(); | |
| 10 | |
| 11 local add_task = require "util.timer".add_task; | |
| 12 local new_xmpp_stream = require "util.xmppstream".new; | |
| 13 local nameprep = require "util.encodings".stringprep.nameprep; | |
| 14 local portmanager = require "core.portmanager"; | |
| 15 local sessionmanager = require "core.sessionmanager"; | |
| 16 local st = require "util.stanza"; | |
| 17 local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; | |
| 18 local uuid_generate = require "util.uuid".generate; | |
| 19 | |
| 20 local xpcall, tostring, type = xpcall, tostring, type; | |
| 21 local format = string.format; | |
| 22 local traceback = debug.traceback; | |
| 23 | |
| 24 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; | |
| 25 | |
| 26 local log = module._log; | |
| 27 | |
| 28 local c2s_timeout = module:get_option_number("c2s_timeout"); | |
| 29 local opt_keepalives = module:get_option_boolean("tcp_keepalives", false); | |
| 30 | |
| 31 local sessions = module:shared("sessions"); | |
| 32 | |
| 33 local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza }; | |
| 34 local listener = {}; | |
| 35 | |
| 36 --- Stream events handlers | |
| 37 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; | |
| 38 local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; | |
| 39 | |
| 40 function stream_callbacks.streamopened(session, attr) | |
| 41 local send = session.send; | |
| 42 session.host = nameprep(attr.to); | |
| 43 if not session.host then | |
| 44 session:close{ condition = "improper-addressing", | |
| 45 text = "A valid 'to' attribute is required on stream headers" }; | |
| 46 return; | |
| 47 end | |
| 48 session.version = tonumber(attr.version) or 0; | |
| 49 session.streamid = uuid_generate(); | |
| 50 (session.log or session)("debug", "Client sent opening <stream:stream> to %s", session.host); | |
| 51 | |
| 52 if not hosts[session.host] then | |
| 53 -- We don't serve this host... | |
| 54 session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; | |
| 55 return; | |
| 56 end | |
| 57 | |
| 58 send("<?xml version='1.0'?>"); | |
| 59 send(format("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='%s' from='%s' version='1.0' xml:lang='en'>", session.streamid, session.host)); | |
| 60 | |
| 61 (session.log or log)("debug", "Sent reply <stream:stream> to client"); | |
| 62 session.notopen = nil; | |
| 63 | |
| 64 -- If session.secure is *false* (not nil) then it means we /were/ encrypting | |
| 65 -- since we now have a new stream header, session is secured | |
| 66 if session.secure == false then | |
| 67 session.secure = true; | |
| 68 end | |
| 69 | |
| 70 local features = st.stanza("stream:features"); | |
| 71 hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); | |
| 72 module:fire_event("stream-features", session, features); | |
| 73 | |
| 74 send(features); | |
| 75 end | |
| 76 | |
| 77 function stream_callbacks.streamclosed(session) | |
| 78 session.log("debug", "Received </stream:stream>"); | |
| 79 session:close(); | |
| 80 end | |
| 81 | |
| 82 function stream_callbacks.error(session, error, data) | |
| 83 if error == "no-stream" then | |
| 84 session.log("debug", "Invalid opening stream header"); | |
| 85 session:close("invalid-namespace"); | |
| 86 elseif error == "parse-error" then | |
| 87 (session.log or log)("debug", "Client XML parse error: %s", tostring(data)); | |
| 88 session:close("not-well-formed"); | |
| 89 elseif error == "stream-error" then | |
| 90 local condition, text = "undefined-condition"; | |
| 91 for child in data:children() do | |
| 92 if child.attr.xmlns == xmlns_xmpp_streams then | |
| 93 if child.name ~= "text" then | |
| 94 condition = child.name; | |
| 95 else | |
| 96 text = child:get_text(); | |
| 97 end | |
| 98 if condition ~= "undefined-condition" and text then | |
| 99 break; | |
| 100 end | |
| 101 end | |
| 102 end | |
| 103 text = condition .. (text and (" ("..text..")") or ""); | |
| 104 session.log("info", "Session closed by remote with error: %s", text); | |
| 105 session:close(nil, text); | |
| 106 end | |
| 107 end | |
| 108 | |
| 109 local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), traceback()); end | |
| 110 function stream_callbacks.handlestanza(session, stanza) | |
| 111 stanza = session.filter("stanzas/in", stanza); | |
| 112 if stanza then | |
| 113 return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); | |
| 114 end | |
| 115 end | |
| 116 | |
| 117 --- Session methods | |
| 118 local function session_close(session, reason) | |
| 119 local log = session.log or log; | |
| 120 if session.conn then | |
| 121 if session.notopen then | |
| 122 session.send("<?xml version='1.0'?>"); | |
| 123 session.send(st.stanza("stream:stream", default_stream_attr):top_tag()); | |
| 124 end | |
| 125 if reason then | |
| 126 if type(reason) == "string" then -- assume stream error | |
| 127 log("info", "Disconnecting client, <stream:error> is: %s", reason); | |
| 128 session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); | |
| 129 elseif type(reason) == "table" then | |
| 130 if reason.condition then | |
| 131 local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); | |
| 132 if reason.text then | |
| 133 stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); | |
| 134 end | |
| 135 if reason.extra then | |
| 136 stanza:add_child(reason.extra); | |
| 137 end | |
| 138 log("info", "Disconnecting client, <stream:error> is: %s", tostring(stanza)); | |
| 139 session.send(stanza); | |
| 140 elseif reason.name then -- a stanza | |
| 141 log("info", "Disconnecting client, <stream:error> is: %s", tostring(reason)); | |
| 142 session.send(reason); | |
| 143 end | |
| 144 end | |
| 145 end | |
| 146 session.send("</stream:stream>"); | |
| 147 session.conn:close(); | |
| 148 listener.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); | |
| 149 end | |
| 150 end | |
| 151 | |
| 152 --- Port listener | |
| 153 function listener.onconnect(conn) | |
| 154 local session = sm_new_session(conn); | |
| 155 sessions[conn] = session; | |
| 156 | |
| 157 session.log("info", "Client connected"); | |
| 158 | |
| 159 -- Client is using legacy SSL (otherwise mod_tls sets this flag) | |
| 160 if conn:ssl() then | |
| 161 session.secure = true; | |
| 162 end | |
| 163 | |
| 164 if opt_keepalives then | |
| 165 conn:setoption("keepalive", opt_keepalives); | |
| 166 end | |
| 167 | |
| 168 session.close = session_close; | |
| 169 | |
| 170 local stream = new_xmpp_stream(session, stream_callbacks); | |
| 171 session.stream = stream; | |
| 172 session.notopen = true; | |
| 173 | |
| 174 function session.reset_stream() | |
| 175 session.notopen = true; | |
| 176 session.stream:reset(); | |
| 177 end | |
| 178 | |
| 179 local filter = session.filter; | |
| 180 function session.data(data) | |
| 181 data = filter("bytes/in", data); | |
| 182 if data then | |
| 183 local ok, err = stream:feed(data); | |
| 184 if ok then return; end | |
| 185 log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); | |
| 186 session:close("not-well-formed"); | |
| 187 end | |
| 188 end | |
| 189 | |
| 190 | |
| 191 if c2s_timeout then | |
| 192 add_task(c2s_timeout, function () | |
| 193 if session.type == "c2s_unauthed" then | |
| 194 session:close("connection-timeout"); | |
| 195 end | |
| 196 end); | |
| 197 end | |
| 198 | |
| 199 session.dispatch_stanza = stream_callbacks.handlestanza; | |
| 200 end | |
| 201 | |
| 202 function listener.onincoming(conn, data) | |
| 203 local session = sessions[conn]; | |
| 204 if session then | |
| 205 session.data(data); | |
| 206 end | |
| 207 end | |
| 208 | |
| 209 function listener.ondisconnect(conn, err) | |
| 210 local session = sessions[conn]; | |
| 211 if session then | |
| 212 (session.log or log)("info", "Client disconnected: %s", err); | |
| 213 sm_destroy_session(session, err); | |
| 214 sessions[conn] = nil; | |
| 215 session = nil; | |
| 216 end | |
| 217 end | |
| 218 | |
| 219 function listener.associate_session(conn, session) | |
| 220 sessions[conn] = session; | |
| 221 end | |
| 222 | |
| 223 module:add_item("net-provider", { | |
| 224 name = "c2s"; | |
| 225 listener = listener; | |
| 226 default_port = 5222; | |
| 227 encryption = "starttls"; | |
| 228 multiplex = { | |
| 229 pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>"; | |
| 230 }; | |
| 231 }); | |
| 232 | |
| 233 module:add_item("net-provider", { | |
| 234 name = "legacy_ssl"; | |
| 235 listener = listener; | |
| 236 encryption = "ssl"; | |
| 237 multiplex = { | |
| 238 pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>"; | |
| 239 }; | |
| 240 }); | |
| 241 | |
| 242 |
