aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2009-03-03 17:48:04 +0100
committerTobias Markmann <tm@ayena.de>2009-03-03 17:48:04 +0100
commit7a7a89844a038b071f02fd1af309ba9ebbcda7de (patch)
tree73946c977be10a965d59de85b3ce6ba3743bc771
parent3d1e7adbbc0afb8395f02ecd42fe3c38ce6a8d36 (diff)
parentb6b9906c3c653935b55ad79ad4a2c41118f0a0e3 (diff)
downloadprosody-7a7a89844a038b071f02fd1af309ba9ebbcda7de.tar.gz
prosody-7a7a89844a038b071f02fd1af309ba9ebbcda7de.zip
Merged with main tip.
-rw-r--r--core/componentmanager.lua76
-rw-r--r--core/presencemanager.lua17
-rw-r--r--core/s2smanager.lua7
-rw-r--r--core/stanza_router.lua11
-rw-r--r--core/xmlhandlers.lua24
-rw-r--r--net/server.lua29
-rw-r--r--net/xmppclient_listener.lua5
-rw-r--r--plugins/mod_bosh.lua18
-rw-r--r--plugins/mod_muc.lua160
-rw-r--r--plugins/mod_register.lua2
-rwxr-xr-xprosody28
-rw-r--r--util/dataforms.lua70
-rw-r--r--util/dependencies.lua30
-rw-r--r--util/multitable.lua49
-rw-r--r--util/pubsub.lua60
-rw-r--r--util/stanza.lua21
-rw-r--r--util/timer.lua51
17 files changed, 466 insertions, 192 deletions
diff --git a/core/componentmanager.lua b/core/componentmanager.lua
index 04909a07..15931167 100644
--- a/core/componentmanager.lua
+++ b/core/componentmanager.lua
@@ -7,20 +7,20 @@
--
-
-
+
+
local log = require "util.logger".init("componentmanager");
local configmanager = require "core.configmanager";
local eventmanager = require "core.eventmanager";
-local modulemanager = require "core.modulemanager";
-local jid_split = require "util.jid".split;
+local modulemanager = require "core.modulemanager";
+local jid_split = require "util.jid".split;
local hosts = hosts;
-local pairs, type, tostring = pairs, type, tostring;
-
-local components = {};
-
-module "componentmanager"
+local pairs, type, tostring = pairs, type, tostring;
+
+local components = {};
+
+module "componentmanager"
function load_enabled_components(config)
local defined_hosts = config or configmanager.getconfig();
@@ -39,34 +39,40 @@ function load_enabled_components(config)
end
eventmanager.add_event_hook("server-starting", load_enabled_components);
-
-function handle_stanza(origin, stanza)
- local node, host = jid_split(stanza.attr.to);
+
+function handle_stanza(origin, stanza)
+ local node, host = jid_split(stanza.attr.to);
local component = nil;
if not component then component = components[stanza.attr.to]; end -- hack to allow hooking node@server/resource and server/resource
- if not component then component = components[node.."@"..host]; end -- hack to allow hooking node@server
+ if not component then component = components[node.."@"..host]; end -- hack to allow hooking node@server
if not component then component = components[host]; end
- if component then
- log("debug", "stanza being handled by component: "..host);
- component(origin, stanza, hosts[host]);
- else
- log("error", "Component manager recieved a stanza for a non-existing component: " .. stanza.attr.to);
- end
-end
-
-function register_component(host, component)
- if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
- -- TODO check for host well-formedness
- components[host] = component;
- hosts[host] = { type = "component", host = host, connected = true, s2sout = {} };
+ if component then
+ log("debug", "stanza being handled by component: "..host);
+ component(origin, stanza, hosts[host]);
+ else
+ log("error", "Component manager recieved a stanza for a non-existing component: " .. stanza.attr.to);
+ end
+end
+
+function create_component(host, component)
+ -- TODO check for host well-formedness
+ session = session or { type = "component", host = host, connected = true, s2sout = {}, send = component };
+ return session;
+end
+
+function register_component(host, component, session)
+ if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
+ components[host] = component;
+ hosts[host] = session or create_component(host, component);
+
-- FIXME only load for a.b.c if b.c has dialback, and/or check in config
- modulemanager.load(host, "dialback");
- log("debug", "component added: "..host);
- return hosts[host];
- else
- log("error", "Attempt to set component for existing host: "..host);
- end
-end
+ modulemanager.load(host, "dialback");
+ log("debug", "component added: "..host);
+ return session or hosts[host];
+ else
+ log("error", "Attempt to set component for existing host: "..host);
+ end
+end
function deregister_component(host)
if components[host] then
@@ -79,5 +85,5 @@ function deregister_component(host)
log("error", "Attempt to remove component for non-existing host: "..host);
end
end
-
-return _M;
+
+return _M;
diff --git a/core/presencemanager.lua b/core/presencemanager.lua
index 8fdf3612..f94ffd55 100644
--- a/core/presencemanager.lua
+++ b/core/presencemanager.lua
@@ -95,13 +95,16 @@ function handle_normal_presence(origin, stanza, core_route_stanza)
end
origin.priority = 0;
if stanza.attr.type == "unavailable" then
- origin.presence = nil;
- if origin.directed then
- for jid in pairs(origin.directed) do
- stanza.attr.to = jid;
- core_route_stanza(origin, stanza);
- end
- origin.directed = nil;
+ origin.presence = nil;
+ if origin.directed then
+ local old_from = stanza.attr.from;
+ stanza.attr.from = origin.full_jid;
+ for jid in pairs(origin.directed) do
+ stanza.attr.to = jid;
+ core_route_stanza(origin, stanza);
+ end
+ stanza.attr.from = old_from;
+ origin.directed = nil;
end
else
origin.presence = stanza;
diff --git a/core/s2smanager.lua b/core/s2smanager.lua
index 4f205418..db6d2fe5 100644
--- a/core/s2smanager.lua
+++ b/core/s2smanager.lua
@@ -206,22 +206,19 @@ function streamopened(session, attr)
session.version = 0; --tonumber(attr.version) or 0;
if session.version >= 1.0 and not (attr.to and attr.from) then
- --print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
log("warn", (session.to_host or "(unknown)").." failed to specify 'to' or 'from' hostname as per RFC");
end
if session.direction == "incoming" then
-- Send a reply stream header
-
- --for k,v in pairs(attr) do print("", tostring(k), ":::", tostring(v)); end
-
session.to_host = attr.to;
session.from_host = attr.from;
session.streamid = uuid_gen();
(session.log or log)("debug", "incoming s2s received <stream:stream>");
send("<?xml version='1.0'?>");
- send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag());
+ send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
+ ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag());
if session.to_host and not hosts[session.to_host] then
-- Attempting to connect to a host we don't serve
session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host });
diff --git a/core/stanza_router.lua b/core/stanza_router.lua
index 1ebc158d..23b7a37d 100644
--- a/core/stanza_router.lua
+++ b/core/stanza_router.lua
@@ -217,6 +217,9 @@ function core_route_stanza(origin, stanza)
session.send(stanza);
end
end
+ elseif resource and stanza.attr.type == 'groupchat' then
+ -- Groupchat message sent to offline resource
+ origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
else
local priority = 0;
local recipients = {};
@@ -263,10 +266,14 @@ function core_route_stanza(origin, stanza)
if stanza.attr.type == "chat" or stanza.attr.type == "normal" or not stanza.attr.type then
offlinemanager.store(node, host, stanza);
-- FIXME don't store messages with only chat state notifications
+ elseif stanza.attr.type == "groupchat" then
+ local reply = st.error_reply(stanza, "cancel", "service-unavailable");
+ reply.attr.from = to;
+ origin.send(reply);
end
-- TODO allow configuration of offline storage
-- TODO send error if not storing offline
- elseif stanza.name == "iq" then
+ elseif stanza.name == "iq" and (stanza.attr.type == "get" or stanza.attr.type == "set") then
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
end
else -- user does not exist
@@ -277,7 +284,7 @@ function core_route_stanza(origin, stanza)
origin.send(st.presence({from = to_bare, to = from_bare, type = "unsubscribed"}));
end
-- else ignore
- else
+ elseif stanza.attr.type ~= "error" and (stanza.name ~= "iq" or stanza.attr.type ~= "result") then
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
end
end
diff --git a/core/xmlhandlers.lua b/core/xmlhandlers.lua
index 020e08db..ea136c8d 100644
--- a/core/xmlhandlers.lua
+++ b/core/xmlhandlers.lua
@@ -121,17 +121,19 @@ function init_xmlhandlers(session, stream_callbacks)
cb_error(session, "parse-error", "unexpected-element-close", name);
end
end
- if stanza and #chardata > 0 then
- -- We have some character data in the buffer
- stanza:text(t_concat(chardata));
- chardata = {};
- end
- -- Complete stanza
- if #stanza.last_add == 0 then
- cb_handlestanza(session, stanza);
- stanza = nil;
- else
- stanza:up();
+ if stanza then
+ if #chardata > 0 then
+ -- We have some character data in the buffer
+ stanza:text(t_concat(chardata));
+ chardata = {};
+ end
+ -- Complete stanza
+ if #stanza.last_add == 0 then
+ cb_handlestanza(session, stanza);
+ stanza = nil;
+ else
+ stanza:up();
+ end
end
end
return xml_handlers;
diff --git a/net/server.lua b/net/server.lua
index a5c8e24c..8d0d3b1a 100644
--- a/net/server.lua
+++ b/net/server.lua
@@ -369,7 +369,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
end
bufferqueuelen = bufferqueuelen + 1
bufferqueue[ bufferqueuelen ] = data
- _writetimes[ handler ] = _writetimes[ handler ] or _currenttime
+ if handler then
+ _writetimes[ handler ] = _writetimes[ handler ] or _currenttime
+ end
return true
end
handler.write = write
@@ -436,7 +438,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
--out_put( "server.lua: read data '", buffer, "', error: ", err )
return dispatch( handler, buffer, err )
else -- connections was closed or fatal error
- out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
+ out_put( "server.lua: client ", ip, ":", tostring(clientport), " error: ", tostring(err) )
fatalerror = true
disconnect( handler, err )
_ = handler and handler.close( )
@@ -470,7 +472,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
fatalerror = true
disconnect( handler, err )
- handler.close( )
+ _ = handler and handler.close( )
return false
end
end
@@ -478,16 +480,19 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
if sslctx then -- ssl?
ssl = true
local wrote
+ local read
local handshake = coroutine_wrap( function( client ) -- create handshake coroutine
local err
for i = 1, 10 do -- 10 handshake attemps
+ _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen
+ _readlistlen = ( read and removesocket( _readlist, socket, _readlistlen ) ) or _readlistlen
+ read, wrote = nil, nil
_, err = client:dohandshake( )
if not err then
- --out_put( "server.lua: ssl handshake done" )
- _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen
+ out_put( "server.lua: ssl handshake done" )
handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions
handler.sendbuffer = _sendbuffer
- --return dispatch( handler )
+ -- return dispatch( handler )
return true
else
out_put( "server.lua: error during ssl handshake: ", err )
@@ -495,12 +500,18 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
_sendlistlen = _sendlistlen + 1
_sendlist[ _sendlistlen ] = client
wrote = true
+ elseif err == "wantread" and not read then
+ _readlistlen = _readlistlen + 1
+ _readlist [ _readlistlen ] = client
+ read = true
+ else
+ break;
end
--coroutine_yield( handler, nil, err ) -- handshake not finished
coroutine_yield( )
end
end
- disconnect( handler, "max handshake attemps exceeded" )
+ disconnect( handler, "ssl handshake failed" )
handler.close( true ) -- forced disconnect
return false -- handshake failed
end
@@ -556,8 +567,8 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
handler.starttls = nil
needtls = nil
- handler.receivedata = handler.handshake
- handler.dispatchdata = handler.handshake
+ handler.readbuffer = handshake
+ handler.sendbuffer = handshake
handshake( socket ) -- do handshake
end
handler.readbuffer = _readbuffer
diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua
index ad1fd948..e7a87c1e 100644
--- a/net/xmppclient_listener.lua
+++ b/net/xmppclient_listener.lua
@@ -29,9 +29,10 @@ local stream_callbacks = { stream_tag = "http://etherx.jabber.org/streams|stream
function stream_callbacks.error(session, error, data)
if error == "no-stream" then
+ session.log("debug", "Invalid opening stream header");
session:close("invalid-namespace");
- else
- session.log("debug", "Client XML parse error: %s", tostring(error));
+ elseif session.close then
+ (session.log or log)("debug", "Client XML parse error: %s", tostring(error));
session:close("xml-not-well-formed");
end
end
diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua
index df89f88e..4d0f5cd9 100644
--- a/plugins/mod_bosh.lua
+++ b/plugins/mod_bosh.lua
@@ -32,6 +32,19 @@ local inactive_sessions = {}; -- Sessions which have no open requests
local waiting_requests = {};
function on_destroy_request(request)
waiting_requests[request] = nil;
+ local session = request.session;
+ if session then
+ local requests = session.requests;
+ for i,r in pairs(requests) do
+ if r == request then requests[i] = nil; break; end
+ end
+
+ -- If this session now has no requests open, mark it as inactive
+ if #requests == 0 and session.bosh_max_inactive and not inactive_sessions[session] then
+ inactive_sessions[session] = os_time();
+ (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]);
+ end
+ end
end
function handle_request(method, body, request)
@@ -151,10 +164,6 @@ function stream_callbacks.streamopened(request, attr)
end
elseif s ~= "" then
log("debug", "Saved to send buffer because there are %d open requests", #r);
- if session.bosh_max_inactive and not inactive_sessions[session] then
- inactive_sessions[session] = os_time();
- (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]);
- end
-- Hmm, no requests are open :(
t_insert(session.send_buffer, tostring(s));
log("debug", "There are now %d things in the send_buffer", #session.send_buffer);
@@ -243,7 +252,6 @@ function on_timer()
(session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now);
sessions[session.sid] = nil;
inactive_sessions[session] = nil;
- session.bosh_max_inactive = nil; -- Stop us marking this session as active during destroy
sm_destroy_session(session, "BOSH client silent for over "..session.bosh_max_inactive.." seconds");
end
else
diff --git a/plugins/mod_muc.lua b/plugins/mod_muc.lua
index 9a559f2d..1f8d04e9 100644
--- a/plugins/mod_muc.lua
+++ b/plugins/mod_muc.lua
@@ -1,3 +1,10 @@
+-- Prosody IM v0.3
+-- Copyright (C) 2008-2009 Matthew Wild
+-- Copyright (C) 2008-2009 Waqas Hussain
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
local register_component = require "core.componentmanager".register_component;
@@ -62,7 +69,7 @@ function filter_xmlns_from_stanza(stanza, filters)
end
local presence_filters = {["http://jabber.org/protocol/muc"]=true;["http://jabber.org/protocol/muc#user"]=true};
function get_filtered_presence(stanza)
- return filter_xmlns_from_stanza(st.deserialize(st.preserialize(stanza)), presence_filters);
+ return filter_xmlns_from_stanza(st.clone(stanza), presence_filters);
end
function getUsingPath(stanza, path, getText)
local tag = stanza;
@@ -116,60 +123,12 @@ function set_subject(current_nick, room, subject)
if subject == "" then subject = nil; end
rooms_info:set(room, 'subject', subject);
save_room();
- local msg = st.message({type='groupchat', from=from})
+ local msg = st.message({type='groupchat', from=current_nick})
:tag('subject'):text(subject):up();
broadcast_message_stanza(room, msg, false);
- --broadcast_message(current_nick, room, subject or "", nil);
return true;
end
-function broadcast_presence(type, from, room, code, newnick)
- local data = rooms:get(room, from);
- local stanza = st.presence({type=type, from=from})
- :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
- :tag("item", {affiliation=data.affiliation, role=data.role, nick = newnick}):up();
- if code then
- stanza:tag("status", {code=code}):up();
- end
- local me;
- local r = rooms:get(room);
- if r then
- for occupant, o_data in pairs(r) do
- if occupant ~= from then
- stanza.attr.to = o_data.jid;
- core_route_stanza(component, stanza);
- else
- me = o_data.jid;
- end
- end
- end
- if me then
- stanza:tag("status", {code='110'});
- stanza.attr.to = me;
- core_route_stanza(component, stanza);
- end
-end
-function broadcast_message(from, room, subject, body)
- local stanza = st.message({type='groupchat', from=from});
- if subject then stanza:tag('subject'):text(subject):up(); end
- if body then stanza:tag('body'):text(body):up(); end
- local r = rooms:get(room);
- if r then
- for occupant, o_data in pairs(r) do
- stanza.attr.to = o_data.jid;
- core_route_stanza(component, stanza);
- end
- if not subject and body then -- add to history
- local history = rooms_info:get(room, 'history');
- if not history then history = {}; rooms_info:set(room, 'history', history); end
- -- stanza = st.deserialize(st.preserialize(stanza));
- stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
- stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
- t_insert(history, st.preserialize(stanza));
- while #history > history_length do t_remove(history, 1) end
- end
- end
-end
function broadcast_message_stanza(room, stanza, historic)
local r = rooms:get(room);
if r then
@@ -182,10 +141,10 @@ function broadcast_message_stanza(room, stanza, historic)
if historic then -- add to history
local history = rooms_info:get(room, 'history');
if not history then history = {}; rooms_info:set(room, 'history', history); end
- -- stanza = st.deserialize(st.preserialize(stanza));
+ -- stanza = st.clone(stanza);
stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
- t_insert(history, st.preserialize(stanza));
+ t_insert(history, st.clone(st.preserialize(stanza)));
while #history > history_length do t_remove(history, 1) end
end
end
@@ -226,57 +185,58 @@ function handle_to_occupant(origin, stanza) -- PM, vCards, etc
local room = jid_bare(to);
local current_nick = jid_nick:get(from, room);
local type = stanza.attr.type;
+ log("debug", "room: %s, current_nick: %s, stanza: %s", room or "nil", current_nick or "nil", stanza:top_tag());
+ if (select(2, jid_split(from)) == muc_domain) then error("Presence from the MUC itself!!!"); end
if stanza.name == "presence" then
local pr = get_filtered_presence(stanza);
- pr.attr.from = to;
+ pr.attr.from = current_nick;
if type == "error" then -- error, kick em out!
if current_nick then
- local data = rooms:get(room, to);
- data.role = 'none';
- local pr = st.presence({type='unavailable', from=current_nick}):tag('status'):text('This participant is kicked from the room because he sent an error presence'):up()
- :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
- :tag("item", {affiliation=data.affiliation, role=data.role}):up();
- broadcast_presence_stanza(room, pr);
- --broadcast_presence('unavailable', to, room); -- TODO also add <status>This participant is kicked from the room because he sent an error presence: badformed error stanza</status>
- rooms:remove(room, to);
- jid_nick:remove(from, room);
+ log("debug", "kicking %s from %s", current_nick, room);
+ handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error presence')); -- send unavailable
end
elseif type == "unavailable" then -- unavailable
if current_nick then
- local data = rooms:get(room, to);
+ log("debug", "%s leaving %s", current_nick, room);
+ local data = rooms:get(room, current_nick);
data.role = 'none';
broadcast_presence_stanza(room, pr);
- --broadcast_presence('unavailable', to, room);
- rooms:remove(room, to);
+ rooms:remove(room, current_nick);
jid_nick:remove(from, room);
end
elseif not type then -- available
if current_nick then
- if current_nick == to then -- simple presence
- broadcast_presence_stanza(room, pr);
- -- FIXME check if something was filtered. if it was, then user may be rejoining
- else -- change nick
- if rooms:get(room, to) then
- origin.send(st.error_reply(stanza, "cancel", "conflict"));
- else
- local data = rooms:get(room, current_nick);
- local to_nick = select(3, jid_split(to));
- if to_nick then
- local p = st.presence({type='unavailable', from=current_nick});
- --[[:tag('x', {xmlns='http://jabber.org/protocol/muc#user'})
- :tag('item', {affiliation=data.affiliation, role=data.role, nick=to_nick}):up()
- :tag('status', {code='303'});]]
- broadcast_presence_stanza(room, p, '303', to_nick);
- --broadcast_presence('unavailable', current_nick, room, '303', to_nick);
- rooms:remove(room, current_nick);
- rooms:set(room, to, data);
- jid_nick:set(from, room, to);
- broadcast_presence_stanza(room, pr);
- --broadcast_presence(nil, to, room, nil);
+ if #pr == #stanza or current_nick ~= to then
+ if current_nick == to then -- simple presence
+ log("debug", "%s broadcasted presence", current_nick);
+ rooms:get(room, current_nick).sessions[from] = pr;
+ broadcast_presence_stanza(room, pr);
+ else -- change nick
+ if rooms:get(room, to) then
+ log("debug", "%s couldn't change nick", current_nick);
+ origin.send(st.error_reply(stanza, "cancel", "conflict"));
else
- --TODO malformed-jid
+ local data = rooms:get(room, current_nick);
+ local to_nick = select(3, jid_split(to));
+ if to_nick then
+ log("debug", "%s (%s) changing nick to %s", current_nick, data.jid, to);
+ local p = st.presence({type='unavailable', from=current_nick});
+ broadcast_presence_stanza(room, p, '303', to_nick);
+ rooms:remove(room, current_nick);
+ rooms:set(room, to, data);
+ jid_nick:set(from, room, to);
+ pr.attr.from = to;
+ rooms:get(room, to).sessions[from] = pr;
+ broadcast_presence_stanza(room, pr);
+ else
+ --TODO malformed-jid
+ end
end
end
+ else -- possible rejoin
+ log("debug", "%s had connection replaced", current_nick);
+ handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('Replaced by new connection')); -- send unavailable
+ handle_to_occupant(origin, stanza); -- resend available
end
else -- enter room
local new_nick = to;
@@ -284,8 +244,10 @@ function handle_to_occupant(origin, stanza) -- PM, vCards, etc
new_nick = nil;
end
if not new_nick then
+ log("debug", "%s couldn't join due to nick conflict: %s", from, to);
origin.send(st.error_reply(stanza, "cancel", "conflict"));
else
+ log("debug", "%s joining as %s", from, to);
local data;
if not rooms:get(room) and not rooms_info:get(room) then -- new room
data = {affiliation='owner', role='moderator', jid=from, sessions={[from]=get_filtered_presence(stanza)}};
@@ -298,19 +260,17 @@ function handle_to_occupant(origin, stanza) -- PM, vCards, etc
local r = rooms:get(room);
if r then
for occupant, o_data in pairs(r) do
- if occupant ~= from then
+ if occupant ~= to then
local pres = get_filtered_presence(o_data.sessions[o_data.jid]);
pres.attr.to, pres.attr.from = from, occupant;
- pres
- --local pres = st.presence({to=from, from=occupant})
- :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
+ pres:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
:tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up();
core_route_stanza(component, pres);
end
end
end
+ pr.attr.from = to;
broadcast_presence_stanza(room, pr);
- --broadcast_presence(nil, to, room);
local history = rooms_info:get(room, 'history'); -- send discussion history
if history then
for _, msg in ipairs(history) do
@@ -327,24 +287,17 @@ function handle_to_occupant(origin, stanza) -- PM, vCards, etc
elseif type ~= 'result' then -- bad type
origin.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME correct error?
end
- elseif not current_nick then -- not in room
+ elseif not current_nick and type ~= "error" then -- not in room
origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
elseif stanza.name == "message" and type == "groupchat" then -- groupchat messages not allowed in PM
origin.send(st.error_reply(stanza, "modify", "bad-request"));
elseif stanza.name == "message" and type == "error" then
- if current_nick then
- local data = rooms:get(room, to);
- data.role = 'none';
- local pr = st.presence({type='unavailable', from=current_nick}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant'):up()
- :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
- :tag("item", {affiliation=data.affiliation, role=data.role}):up();
- broadcast_presence_stanza(room, pr);
- rooms:remove(room, to);
- jid_nick:remove(from, room);
- end
+ log("debug", "%s kicked from %s for sending an error message", current_nick, room);
+ handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant')); -- send unavailable
else -- private stanza
local o_data = rooms:get(room, to);
if o_data then
+ log("debug", "%s sent private stanza to %s (%s)", from, to, o_data.jid);
local jid = o_data.jid;
if stanza.name=='iq' and type=='get' and stanza.tags[1].attr.xmlns == 'vcard-temp' then jid = jid_bare(jid); end
stanza.attr.to, stanza.attr.from = jid, current_nick;
@@ -379,7 +332,6 @@ function handle_to_room(origin, stanza) -- presence changes and groupchat messag
if subject then
set_subject(current_nick, room, subject); -- TODO use broadcast_message_stanza
else
- --broadcast_message(current_nick, room, nil, getText(stanza, {"body"}));
broadcast_message_stanza(room, stanza, true);
end
end
diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua
index ba5bcf36..c1487228 100644
--- a/plugins/mod_register.lua
+++ b/plugins/mod_register.lua
@@ -44,7 +44,7 @@ module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
datamanager.store(session.username, session.host, "vcard", nil);
datamanager.store(session.username, session.host, "private", nil);
datamanager.store(session.username, session.host, "offline", nil);
- local bare = session.username.."@"..session.host;
+ --local bare = session.username.."@"..session.host;
for jid, item in pairs(roster) do
if jid ~= "pending" then
if item.subscription == "both" or item.subscription == "to" then
diff --git a/prosody b/prosody
index 197446e6..15c94c92 100755
--- a/prosody
+++ b/prosody
@@ -7,14 +7,14 @@
-- COPYING file in the source package for more information.
--
--- Config here --
+-- Will be modified by configure script if run --
CFG_SOURCEDIR=nil;
CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
CFG_PLUGINDIR=nil;
CFG_DATADIR=os.getenv("PROSODY_DATADIR");
--- -- -- -- -- --
+-- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- --
if CFG_SOURCEDIR then
package.path = CFG_SOURCEDIR.."/?.lua;"..package.path
@@ -84,7 +84,9 @@ require "core.usermanager"
require "core.sessionmanager"
require "core.stanza_router"
---[[
+-- Commented to protect us from
+-- the second kind of people
+--[[
pcall(require, "remdebug.engine");
if remdebug then remdebug.engine.start() end
]]
@@ -104,6 +106,7 @@ require "util.datamanager".set_data_path(data_path);
----------- End of out-of-place code --------------
+
eventmanager.fire_event("server-starting");
@@ -148,4 +151,21 @@ end
eventmanager.fire_event("server-started");
-server.loop();
+local quitting;
+while not quitting do
+ xpcall(server.loop, function (err)
+ if err:match("%d*: interrupted!$") then
+ quitting = true;
+ return;
+ end
+
+ log("error", "Top-level error, please report:\n%s", tostring(err));
+
+ local traceback = debug.traceback("", 2);
+ if traceback then
+ log("error", "%s", traceback);
+ end
+
+ eventmanager.fire_event("very-bad-error", "*", err, traceback);
+ end);
+end
diff --git a/util/dataforms.lua b/util/dataforms.lua
new file mode 100644
index 00000000..c10a0244
--- /dev/null
+++ b/util/dataforms.lua
@@ -0,0 +1,70 @@
+
+module "dataforms"
+
+local xmlns_forms = 'jabber:x:data';
+
+local form_t = {};
+local form_mt = { __index = form_t };
+
+function new(layout)
+ return setmetatable(layout, form_mt);
+end
+
+local form_x_attr = { xmlns = xmlns_forms };
+
+function form_t.form(layout, data)
+ local form = st.tag("x", form_x_attr);
+ for n, field in ipairs(layout) do
+ local field_type = field.type;
+ -- Add field tag
+ form:tag("field", { type = field_type, var = field.name });
+
+ local value = data[field.name];
+
+ -- Add value, depending on type
+ if field_type == "hidden" then
+ if type(value) == "table" then
+ -- Assume an XML snippet
+ form:add_child(value);
+ elseif value then
+ form:text(tostring(value));
+ end
+ elseif field_type == "boolean" then
+ form:text((value and "1") or "0");
+ elseif field_type == "fixed" then
+
+ elseif field_type == "jid-multi" then
+ for _, jid in ipairs(value) do
+ form:tag("value"):text(jid):up();
+ end
+ elseif field_type == "jid-single" then
+ form:tag("value"):text(value):up();
+
+ end
+
+ -- Jump back up to list of fields
+ form:up();
+ end
+ return form;
+end
+
+function form_t.data(layout, stanza)
+
+end
+
+
+
+--[[
+
+Layout:
+{
+
+ title = "MUC Configuration",
+ instructions = [[Use this form to configure options for this MUC room.]],
+
+ { name = "FORM_TYPE", type = "hidden", required = true };
+ { name = "field-name", type = "field-type", required = false };
+}
+
+
+--]]
diff --git a/util/dependencies.lua b/util/dependencies.lua
index 09b7b018..6ba5f670 100644
--- a/util/dependencies.lua
+++ b/util/dependencies.lua
@@ -9,7 +9,7 @@
local fatal;
-local function softreq(...) local ok, lib = pcall(require, ...); if ok then return lib; else return nil; end end
+local function softreq(...) local ok, lib = pcall(require, ...); if ok then return lib; else return nil, lib; end end
local function missingdep(name, sources, msg)
print("");
@@ -51,19 +51,37 @@ if not ssl then
end
end
-local encodings = softreq "util.encodings"
+local encodings, err = softreq "util.encodings"
if not encodings then
- missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
+ if err:match("not found") then
+ missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so";
});
+ else
+ print "***********************************"
+ print("util/encodings couldn't be loaded. Check that you have a recent version of libidn");
+ print ""
+ print("The full error was:");
+ print(err)
+ print "***********************************"
+ end
fatal = true;
end
-local encodings = softreq "util.hashes"
-if not encodings then
- missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
+local hashes, err = softreq "util.hashes"
+if not hashes then
+ if err:match("not found") then
+ missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so";
});
+ else
+ print "***********************************"
+ print("util/hashes couldn't be loaded. Check that you have a recent version of OpenSSL (libcrypto in particular)");
+ print ""
+ print("The full error was:");
+ print(err)
+ print "***********************************"
+ end
fatal = true;
end
diff --git a/util/multitable.lua b/util/multitable.lua
index e5e87521..397f9ebe 100644
--- a/util/multitable.lua
+++ b/util/multitable.lua
@@ -82,6 +82,53 @@ local function remove(self, ...)
end
+local function s(t, n, results, _end, ...)
+ if t == nil then return; end
+ local k = select(n, ...);
+ if n == _end then
+ if k == nil then
+ for _, v in pairs(t) do
+ t_insert(results, v);
+ end
+ else
+ t_insert(results, t[k]);
+ end
+ return;
+ end
+ if k then
+ v = t[k];
+ if v then
+ s(v, n+1, results, _end, ...);
+ end
+ else
+ for _,b in pairs(t) do
+ s(b, n+1, results, _end, ...);
+ end
+ end
+end
+
+-- Search for keys, nil == wildcard
+local function search(self, ...)
+ local _end = select('#', ...);
+ for n = _end,1 do
+ if select(n, ...) then _end = n; break; end
+ end
+ local results = {};
+ s(self.data, 1, results, _end, ...);
+ return results;
+end
+
+-- Append results to an existing list
+local function search_add(self, results, ...)
+ if not results then results = {}; end
+ local _end = select('#', ...);
+ for n = _end,1 do
+ if select(n, ...) then _end = n; break; end
+ end
+ s(self.data, 1, results, _end, ...);
+ return results;
+end
+
function new()
return {
data = {};
@@ -89,6 +136,8 @@ function new()
add = add;
set = set;
remove = remove;
+ search = search;
+ search_add = search_add;
};
end
diff --git a/util/pubsub.lua b/util/pubsub.lua
new file mode 100644
index 00000000..c1e35e3c
--- /dev/null
+++ b/util/pubsub.lua
@@ -0,0 +1,60 @@
+
+local ipairs, pairs, setmetatable, type =
+ ipairs, pairs, setmetatable, type;
+
+module "pubsub"
+
+local pubsub_node_mt = { __index = _M };
+
+function new_node(name)
+ return setmetatable({ name = name, subscribers = {} }, pubsub_node_mt);
+end
+
+function set_subscribers(node, subscribers_list, list_type)
+ local subscribers = node.subscribers;
+
+ if list_type == "array" then
+ for _, jid in ipairs(subscribers_list) do
+ if not subscribers[jid] then
+ node:add_subscriber(jid);
+ end
+ end
+ elseif (not list_type) or list_type == "set" then
+ for jid in pairs(subscribers_list) do
+ if type(jid) == "string" then
+ node:add_subscriber(jid);
+ end
+ end
+ end
+end
+
+function get_subscribers(node)
+ return node.subscribers;
+end
+
+function publish(node, item, dispatcher, data)
+ local subscribers = node.subscribers;
+ for i = 1,#subscribers do
+ item.attr.to = subscribers[i];
+ dispatcher(data, item);
+ end
+end
+
+function add_subscriber(node, jid)
+ local subscribers = node.subscribers;
+ if not subscribers[jid] then
+ local space = #subscribers;
+ subscribers[space] = jid;
+ subscribers[jid] = space;
+ end
+end
+
+function remove_subscriber(node, subscriber)
+ local subscribers = node.subscribers;
+ if subscribers[jid] then
+ subscribers[subscribers[jid]] = nil;
+ subscribers[jid] = nil;
+ end
+end
+
+return _M;
diff --git a/util/stanza.lua b/util/stanza.lua
index 1c1cab0e..5bc15609 100644
--- a/util/stanza.lua
+++ b/util/stanza.lua
@@ -12,9 +12,10 @@ local t_concat = table.concat;
local t_remove = table.remove;
local t_concat = table.concat;
local s_format = string.format;
-local s_match = string.match;
+local s_match = string.match;
local tostring = tostring;
local setmetatable = setmetatable;
+local getmetatable = getmetatable;
local pairs = pairs;
local ipairs = ipairs;
local type = type;
@@ -215,6 +216,24 @@ function deserialize(stanza)
return stanza;
end
+function clone(stanza)
+ local lookup_table = {};
+ local function _copy(object)
+ if type(object) ~= "table" then
+ return object;
+ elseif lookup_table[object] then
+ return lookup_table[object];
+ end
+ local new_table = {};
+ lookup_table[object] = new_table;
+ for index, value in pairs(object) do
+ new_table[_copy(index)] = _copy(value);
+ end
+ return setmetatable(new_table, getmetatable(object));
+ end
+ return _copy(stanza)
+end
+
function message(attr, body)
if not body then
return stanza("message", attr);
diff --git a/util/timer.lua b/util/timer.lua
new file mode 100644
index 00000000..3db66832
--- /dev/null
+++ b/util/timer.lua
@@ -0,0 +1,51 @@
+-- Prosody IM v0.3
+-- Copyright (C) 2008-2009 Matthew Wild
+-- Copyright (C) 2008-2009 Waqas Hussain
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+
+local ns_addtimer = require "net.server".addtimer;
+local get_time = os.time;
+local t_insert = table.insert;
+local t_remove = table.remove;
+local ipairs, pairs = ipairs, pairs;
+local type = type;
+
+local data = {};
+local new_data = {};
+
+module "timer"
+
+local function _add_task(delay, func)
+ local current_time = get_time();
+ delay = delay + current_time;
+ if delay >= current_time then
+ t_insert(new_data, {delay, func});
+ else func(); end
+end
+
+add_task = _add_task;
+
+ns_addtimer(function()
+ local current_time = get_time();
+ if #new_data > 0 then
+ for _, d in pairs(new_data) do
+ t_insert(data, d);
+ end
+ new_data = {};
+ end
+
+ for i, d in pairs(data) do
+ local t, func = d[1], d[2];
+ if t <= current_time then
+ data[i] = nil;
+ local r = func();
+ if type(r) == "number" then _add_task(r, func); end
+ end
+ end
+end);
+
+return _M;