Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Johan Oudinet 2016-11-23 11:45:13 +01:00
commit d364eab74b
126 changed files with 2389 additions and 49031 deletions

View File

@ -3,6 +3,7 @@ language: erlang
otp_release:
- 17.5
- 18.3
- 19.1
services:
- riak

View File

@ -108,14 +108,6 @@ edoc:
$(ERL) -noinput +B -eval \
'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.'
spec:
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
'case fxml_gen:compile("specs/xmpp_codec.spec", [{add_type_specs, xmpp_element}, {erl_dir, "src"}, {hrl_dir, "include"}]) of ok -> halt(0); _ -> halt(1) end.'
xdata:
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
'case xdata_codec:compile("specs", [{erl_dir, "src"}, {hrl_dir, "include"}]) of ok -> halt(0); _ -> halt(1) end.'
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))
VERSIONED_DEP=$(if $(DEP_$(1)_VERSION),$(DEP_$(1)_VERSION),$(1))
@ -347,5 +339,5 @@ quicktest:
$(REBAR) skip_deps=true ct suites=elixir
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
install uninstall uninstall-binary uninstall-all translations deps test spec \
install uninstall uninstall-binary uninstall-all translations deps test \
quicktest erlang_plt deps_plt ejabberd_plt

51
include/bosh.hrl Normal file
View File

@ -0,0 +1,51 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-define(CT_XML,
{<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
-define(CT_PLAIN,
{<<"Content-Type">>, <<"text/plain">>}).
-define(CT_JSON,
{<<"Content-Type">>, <<"application/json">>}).
-define(AC_ALLOW_ORIGIN,
{<<"Access-Control-Allow-Origin">>, <<"*">>}).
-define(AC_ALLOW_METHODS,
{<<"Access-Control-Allow-Methods">>,
<<"GET, POST, OPTIONS">>}).
-define(AC_ALLOW_HEADERS,
{<<"Access-Control-Allow-Headers">>,
<<"Content-Type">>}).
-define(AC_MAX_AGE,
{<<"Access-Control-Max-Age">>, <<"86400">>}).
-define(OPTIONS_HEADER,
[?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
-define(HEADER(CType),
[CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
-define(PROCNAME, ejabberd_mod_bosh).

View File

@ -1,10 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: flex_offline.xdata
%% Form type: http://jabber.org/protocol/offline
%% Document: XEP-0013
-type property() :: {'number_of_messages', non_neg_integer()}.
-type result() :: [property()].
-type form() :: [property() | xdata_field()].

View File

@ -1,17 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 10 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-record(jid, {user = <<"">> :: binary(),
server = <<"">> :: binary(),
resource = <<"">> :: binary(),
luser = <<"">> :: binary(),
lserver = <<"">> :: binary(),
lresource = <<"">> :: binary()}).
-type(jid() :: #jid{}).
-type(ljid() :: {binary(), binary(), binary()}).

View File

@ -1,13 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: mam_query.xdata
%% Form type: urn:xmpp:mam:1
%% Document: XEP-0313
-type property() :: {'with', jid:jid()} |
{'start', erlang:timestamp()} |
{'end', erlang:timestamp()} |
{'withtext', binary()}.
-type result() :: [property()].
-type form() :: [property() | xdata_field()].

View File

@ -1,7 +1,7 @@
-record(offline_msg,
{us = {<<"">>, <<"">>} :: {binary(), binary()},
timestamp = now() :: erlang:timestamp() | '_',
expire = now() :: erlang:timestamp() | never | '_',
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_',
expire = p1_time_compat:timestamp() :: erlang:timestamp() | never | '_',
from = #jid{} :: jid() | '_',
to = #jid{} :: jid() | '_',
packet = #xmlel{} :: xmlel() | '_'}).

View File

@ -1,16 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: muc_register.xdata
%% Form type: http://jabber.org/protocol/muc#register
%% Document: XEP-0045
-type property() :: {'allow', boolean()} |
{'email', binary()} |
{'faqentry', [binary()]} |
{'first', binary()} |
{'last', binary()} |
{'roomnick', binary()} |
{'url', binary()}.
-type result() :: [property()].
-type form() :: [property() | xdata_field()].

View File

@ -1,16 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: muc_request.xdata
%% Form type: http://jabber.org/protocol/muc#request
%% Document: XEP-0045
-type property() :: {'role', participant} |
{'jid', jid:jid()} |
{'roomnick', binary()} |
{'request_allow', boolean()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'role', participant, options(participant)}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,55 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: muc_roomconfig.xdata
%% Form type: http://jabber.org/protocol/muc#roomconfig
%% Document: XEP-0045
-type 'allow_private_messages_from_visitors'() :: nobody | moderators | anyone.
-type 'maxusers'() :: none | non_neg_integer().
-type 'presencebroadcast'() :: moderator | participant | visitor.
-type 'whois'() :: moderators | anyone.
-type property() :: {'maxhistoryfetch', binary()} |
{'allowpm', binary()} |
{'allow_private_messages', boolean()} |
{'allow_private_messages_from_visitors', 'allow_private_messages_from_visitors'()} |
{'allow_visitor_status', boolean()} |
{'allow_visitor_nickchange', boolean()} |
{'allow_voice_requests', boolean()} |
{'allow_subscription', boolean()} |
{'voice_request_min_interval', non_neg_integer()} |
{'captcha_protected', boolean()} |
{'captcha_whitelist', [jid:jid()]} |
{'allow_query_users', boolean()} |
{'allowinvites', boolean()} |
{'changesubject', boolean()} |
{'enablelogging', boolean()} |
{'getmemberlist', [binary()]} |
{'lang', binary()} |
{'pubsub', binary()} |
{'maxusers', 'maxusers'()} |
{'membersonly', boolean()} |
{'moderatedroom', boolean()} |
{'members_by_default', boolean()} |
{'passwordprotectedroom', boolean()} |
{'persistentroom', boolean()} |
{'presencebroadcast', ['presencebroadcast'()]} |
{'publicroom', boolean()} |
{'public_list', boolean()} |
{'roomadmins', [jid:jid()]} |
{'roomdesc', binary()} |
{'roomname', binary()} |
{'roomowners', [jid:jid()]} |
{'roomsecret', binary()} |
{'whois', 'whois'()} |
{'mam', boolean()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'allowpm', binary(), options(binary())} |
{'allow_private_messages_from_visitors', 'allow_private_messages_from_visitors'(), options('allow_private_messages_from_visitors'())} |
{'getmemberlist', [binary()], options(binary())} |
{'maxusers', 'maxusers'(), options('maxusers'())} |
{'presencebroadcast', ['presencebroadcast'()], options('presencebroadcast'())} |
{'whois', 'whois'(), options('whois'())}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,18 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: muc_roominfo.xdata
%% Form type: http://jabber.org/protocol/muc#roominfo
%% Document: XEP-0045
-type property() :: {'maxhistoryfetch', non_neg_integer()} |
{'contactjid', [jid:jid()]} |
{'description', binary()} |
{'lang', binary()} |
{'ldapgroup', binary()} |
{'logs', binary()} |
{'occupants', non_neg_integer()} |
{'subject', binary()} |
{'subjectmod', boolean()}.
-type result() :: [property()].
-type form() :: [property() | xdata_field()].

View File

@ -1,182 +0,0 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-define(NS_COMPONENT, <<"jabber:component:accept">>).
-define(NS_SERVER, <<"jabber:server">>).
-define(NS_SERVER_DIALBACK, <<"jabber:server:dialback">>).
-define(NS_CLIENT, <<"jabber:client">>).
-define(NS_DISCO_ITEMS,
<<"http://jabber.org/protocol/disco#items">>).
-define(NS_DISCO_INFO,
<<"http://jabber.org/protocol/disco#info">>).
-define(NS_VCARD, <<"vcard-temp">>).
-define(NS_VCARD_UPDATE, <<"vcard-temp:x:update">>).
-define(NS_AUTH, <<"jabber:iq:auth">>).
-define(NS_AUTH_ERROR, <<"jabber:iq:auth:error">>).
-define(NS_REGISTER, <<"jabber:iq:register">>).
-define(NS_SEARCH, <<"jabber:iq:search">>).
-define(NS_ROSTER, <<"jabber:iq:roster">>).
-define(NS_ROSTER_VER,
<<"urn:xmpp:features:rosterver">>).
-define(NS_PRIVACY, <<"jabber:iq:privacy">>).
-define(NS_BLOCKING, <<"urn:xmpp:blocking">>).
-define(NS_PRIVATE, <<"jabber:iq:private">>).
-define(NS_VERSION, <<"jabber:iq:version">>).
-define(NS_TIME, <<"urn:xmpp:time">>).
-define(NS_LAST, <<"jabber:iq:last">>).
-define(NS_XDATA, <<"jabber:x:data">>).
-define(NS_IQDATA, <<"jabber:iq:data">>).
-define(NS_DELAY, <<"urn:xmpp:delay">>).
-define(NS_HINTS, <<"urn:xmpp:hints">>).
-define(NS_EXPIRE, <<"jabber:x:expire">>).
-define(NS_EVENT, <<"jabber:x:event">>).
-define(NS_CHATSTATES,
<<"http://jabber.org/protocol/chatstates">>).
-define(NS_XCONFERENCE, <<"jabber:x:conference">>).
-define(NS_STATS,
<<"http://jabber.org/protocol/stats">>).
-define(NS_MUC, <<"http://jabber.org/protocol/muc">>).
-define(NS_MUC_USER,
<<"http://jabber.org/protocol/muc#user">>).
-define(NS_MUC_ADMIN,
<<"http://jabber.org/protocol/muc#admin">>).
-define(NS_MUC_OWNER,
<<"http://jabber.org/protocol/muc#owner">>).
-define(NS_MUC_UNIQUE,
<<"http://jabber.org/protocol/muc#unique">>).
-define(NS_PUBSUB,
<<"http://jabber.org/protocol/pubsub">>).
-define(NS_PUBSUB_EVENT,
<<"http://jabber.org/protocol/pubsub#event">>).
-define(NS_PUBSUB_META_DATA,
<<"http://jabber.org/protocol/pubsub#meta-data">>).
-define(NS_PUBSUB_OWNER,
<<"http://jabber.org/protocol/pubsub#owner">>).
-define(NS_PUBSUB_NMI,
<<"http://jabber.org/protocol/pubsub#node-meta-info">>).
-define(NS_PUBSUB_ERRORS,
<<"http://jabber.org/protocol/pubsub#errors">>).
-define(NS_PUBSUB_NODE_CONFIG,
<<"http://jabber.org/protocol/pubsub#node_config">>).
-define(NS_PUBSUB_SUB_OPTIONS,
<<"http://jabber.org/protocol/pubsub#subscribe_options">>).
-define(NS_PUBSUB_SUBSCRIBE_OPTIONS,
<<"http://jabber.org/protocol/pubsub#subscribe_options">>).
-define(NS_PUBSUB_PUBLISH_OPTIONS,
<<"http://jabber.org/protocol/pubsub#publish_options">>).
-define(NS_PUBSUB_SUB_AUTH,
<<"http://jabber.org/protocol/pubsub#subscribe_authorization">>).
-define(NS_PUBSUB_GET_PENDING,
<<"http://jabber.org/protocol/pubsub#get-pending">>).
-define(NS_COMMANDS,
<<"http://jabber.org/protocol/commands">>).
-define(NS_BYTESTREAMS,
<<"http://jabber.org/protocol/bytestreams">>).
-define(NS_ADMIN,
<<"http://jabber.org/protocol/admin">>).
-define(NS_ADMIN_ANNOUNCE,
<<"http://jabber.org/protocol/admin#announce">>).
-define(NS_ADMIN_ANNOUNCE_ALL,
<<"http://jabber.org/protocol/admin#announce-all">>).
-define(NS_ADMIN_SET_MOTD,
<<"http://jabber.org/protocol/admin#set-motd">>).
-define(NS_ADMIN_EDIT_MOTD,
<<"http://jabber.org/protocol/admin#edit-motd">>).
-define(NS_ADMIN_DELETE_MOTD,
<<"http://jabber.org/protocol/admin#delete-motd">>).
-define(NS_ADMIN_ANNOUNCE_ALLHOSTS,
<<"http://jabber.org/protocol/admin#announce-allhosts">>).
-define(NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS,
<<"http://jabber.org/protocol/admin#announce-all-allhosts">>).
-define(NS_ADMIN_SET_MOTD_ALLHOSTS,
<<"http://jabber.org/protocol/admin#set-motd-allhosts">>).
-define(NS_ADMIN_EDIT_MOTD_ALLHOSTS,
<<"http://jabber.org/protocol/admin#edit-motd-allhosts">>).
-define(NS_ADMIN_DELETE_MOTD_ALLHOSTS,
<<"http://jabber.org/protocol/admin#delete-motd-allhosts">>).
-define(NS_SERVERINFO,
<<"http://jabber.org/network/serverinfo">>).
-define(NS_RSM, <<"http://jabber.org/protocol/rsm">>).
-define(NS_EJABBERD_CONFIG, <<"ejabberd:config">>).
-define(NS_STREAM,
<<"http://etherx.jabber.org/streams">>).
-define(NS_STANZAS,
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>).
-define(NS_STREAMS,
<<"urn:ietf:params:xml:ns:xmpp-streams">>).
-define(NS_TLS, <<"urn:ietf:params:xml:ns:xmpp-tls">>).
-define(NS_SASL,
<<"urn:ietf:params:xml:ns:xmpp-sasl">>).
-define(NS_SESSION,
<<"urn:ietf:params:xml:ns:xmpp-session">>).
-define(NS_BIND,
<<"urn:ietf:params:xml:ns:xmpp-bind">>).
-define(NS_FEATURE_IQAUTH,
<<"http://jabber.org/features/iq-auth">>).
-define(NS_FEATURE_IQREGISTER,
<<"http://jabber.org/features/iq-register">>).
-define(NS_FEATURE_COMPRESS,
<<"http://jabber.org/features/compress">>).
-define(NS_FEATURE_MSGOFFLINE, <<"msgoffline">>).
-define(NS_FLEX_OFFLINE, <<"http://jabber.org/protocol/offline">>).
-define(NS_COMPRESS,
<<"http://jabber.org/protocol/compress">>).
-define(NS_CAPS, <<"http://jabber.org/protocol/caps">>).
-define(NS_SHIM, <<"http://jabber.org/protocol/shim">>).
-define(NS_ADDRESS,
<<"http://jabber.org/protocol/address">>).
-define(NS_OOB, <<"jabber:x:oob">>).
-define(NS_CAPTCHA, <<"urn:xmpp:captcha">>).
-define(NS_MEDIA, <<"urn:xmpp:media-element">>).
-define(NS_BOB, <<"urn:xmpp:bob">>).
-define(NS_MAM_TMP, <<"urn:xmpp:mam:tmp">>).
-define(NS_MAM_0, <<"urn:xmpp:mam:0">>).
-define(NS_MAM_1, <<"urn:xmpp:mam:1">>).
-define(NS_SID_0, <<"urn:xmpp:sid:0">>).
-define(NS_PING, <<"urn:xmpp:ping">>).
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
-define(NS_FORWARD, <<"urn:xmpp:forward:0">>).
-define(NS_CLIENT_STATE, <<"urn:xmpp:csi:0">>).
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
-define(NS_HTTP_UPLOAD, <<"urn:xmpp:http:upload">>).
-define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>).
-define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>).
-define(NS_NICK, <<"http://jabber.org/protocol/nick">>).
-define(NS_SIC_0, <<"urn:xmpp:sic:0">>).
-define(NS_SIC_1, <<"urn:xmpp:sic:1">>).
-define(NS_MIX_0, <<"urn:xmpp:mix:0">>).
-define(NS_MIX_SERVICEINFO_0, <<"urn:xmpp:mix:0#serviceinfo">>).
-define(NS_MIX_NODES_MESSAGES, <<"urn:xmpp:mix:nodes:messages">>).
-define(NS_MIX_NODES_PRESENCE, <<"urn:xmpp:mix:nodes:presence">>).
-define(NS_MIX_NODES_PARTICIPANTS, <<"urn:xmpp:mix:nodes:participants">>).
-define(NS_MIX_NODES_SUBJECT, <<"urn:xmpp:mix:nodes:subject">>).
-define(NS_MIX_NODES_CONFIG, <<"urn:xmpp:mix:nodes:config">>).
-define(NS_PRIVILEGE, <<"urn:xmpp:privilege:1">>).
-define(NS_DELEGATION, <<"urn:xmpp:delegation:1">>).
-define(NS_MUCSUB, <<"urn:xmpp:mucsub:0">>).
-define(NS_MUCSUB_NODES_PRESENCE, <<"urn:xmpp:mucsub:nodes:presence">>).
-define(NS_MUCSUB_NODES_MESSAGES, <<"urn:xmpp:mucsub:nodes:messages">>).
-define(NS_MUCSUB_NODES_PARTICIPANTS, <<"urn:xmpp:mucsub:nodes:participants">>).
-define(NS_MUCSUB_NODES_AFFILIATIONS, <<"urn:xmpp:mucsub:nodes:affiliations">>).
-define(NS_MUCSUB_NODES_SUBJECT, <<"urn:xmpp:mucsub:nodes:subject">>).
-define(NS_MUCSUB_NODES_CONFIG, <<"urn:xmpp:mucsub:nodes:config">>).
-define(NS_MUCSUB_NODES_SYSTEM, <<"urn:xmpp:mucsub:nodes:system">>).

View File

@ -1,13 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: pubsub_get_pending.xdata
%% Form type: http://jabber.org/protocol/pubsub#subscribe_authorization
%% Document: XEP-0060
-type property() :: {'node', binary()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'node', binary(), options(binary())}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,60 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: pubsub_node_config.xdata
%% Form type: http://jabber.org/protocol/pubsub#node_config
%% Document: XEP-0060
-type 'access_model'() :: authorize | open | presence | roster | whitelist.
-type 'children_association_policy'() :: all | owners | whitelist.
-type 'itemreply'() :: owner | publisher | none.
-type 'node_type'() :: leaf | collection.
-type 'notification_type'() :: normal | headline.
-type 'publish_model'() :: publishers | subscribers | open.
-type 'send_last_published_item'() :: never | on_sub | on_sub_and_presence.
-type property() :: {'access_model', 'access_model'()} |
{'body_xslt', binary()} |
{'children_association_policy', 'children_association_policy'()} |
{'children_association_whitelist', [jid:jid()]} |
{'children', [binary()]} |
{'children_max', binary()} |
{'collection', [binary()]} |
{'contact', [jid:jid()]} |
{'dataform_xslt', binary()} |
{'deliver_notifications', boolean()} |
{'deliver_payloads', boolean()} |
{'description', binary()} |
{'item_expire', binary()} |
{'itemreply', 'itemreply'()} |
{'language', binary()} |
{'max_items', non_neg_integer()} |
{'max_payload_size', non_neg_integer()} |
{'node_type', 'node_type'()} |
{'notification_type', 'notification_type'()} |
{'notify_config', boolean()} |
{'notify_delete', boolean()} |
{'notify_retract', boolean()} |
{'notify_sub', boolean()} |
{'persist_items', boolean()} |
{'presence_based_delivery', boolean()} |
{'publish_model', 'publish_model'()} |
{'purge_offline', boolean()} |
{'roster_groups_allowed', [binary()]} |
{'send_last_published_item', 'send_last_published_item'()} |
{'tempsub', boolean()} |
{'subscribe', boolean()} |
{'title', binary()} |
{'type', binary()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'access_model', 'access_model'(), options('access_model'())} |
{'children_association_policy', 'children_association_policy'(), options('children_association_policy'())} |
{'itemreply', 'itemreply'(), options('itemreply'())} |
{'language', binary(), options(binary())} |
{'node_type', 'node_type'(), options('node_type'())} |
{'notification_type', 'notification_type'(), options('notification_type'())} |
{'publish_model', 'publish_model'(), options('publish_model'())} |
{'roster_groups_allowed', [binary()], options(binary())} |
{'send_last_published_item', 'send_last_published_item'(), options('send_last_published_item'())}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,14 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: pubsub_publish_options.xdata
%% Form type: http://jabber.org/protocol/pubsub#publish-options
%% Document: XEP-0060
-type 'access_model'() :: authorize | open | presence | roster | whitelist.
-type property() :: {'access_model', 'access_model'()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'access_model', 'access_model'(), options('access_model'())}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,13 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: pubsub_subscribe_authorization.xdata
%% Form type: http://jabber.org/protocol/pubsub#subscribe_authorization
%% Document: XEP-0060
-type property() :: {'allow', boolean()} |
{'node', binary()} |
{'subscriber_jid', jid:jid()} |
{'subid', binary()}.
-type result() :: [property()].
-type form() :: [property() | xdata_field()].

View File

@ -1,25 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: pubsub_subscribe_options.xdata
%% Form type: http://jabber.org/protocol/pubsub#subscribe_options
%% Document: XEP-0060
-type 'show-values'() :: away | chat | dnd | online | xa.
-type 'subscription_type'() :: items | nodes.
-type 'subscription_depth'() :: 1 | all.
-type property() :: {'deliver', boolean()} |
{'digest', boolean()} |
{'digest_frequency', binary()} |
{'expire', binary()} |
{'include_body', boolean()} |
{'show-values', ['show-values'()]} |
{'subscription_type', 'subscription_type'()} |
{'subscription_depth', 'subscription_depth'()}.
-type result() :: [property()].
-type options(T) :: [{binary(), T}].
-type property_with_options() ::
{'show-values', ['show-values'()], options('show-values'())} |
{'subscription_type', 'subscription_type'(), options('subscription_type'())} |
{'subscription_depth', 'subscription_depth'(), options('subscription_depth'())}.
-type form() :: [property() | property_with_options() | xdata_field()].

View File

@ -1,25 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2015, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 10 Dec 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-include("ns.hrl").
-include("jid.hrl").
-include("xmpp_codec.hrl").
-include("fxml.hrl").
-type iq_type() :: get | set | result | error.
-type message_type() :: chat | error | groupchat | headline | normal.
-type presence_type() :: available | error | probe | subscribe |
subscribed | unavailable | unsubscribe |
unsubscribed.
-type stanza() :: iq() | presence() | message().
-define(is_stanza(Pkt),
(is_record(Pkt, iq) or
is_record(Pkt, message) or
is_record(Pkt, presence))).

File diff suppressed because it is too large Load Diff

View File

@ -27,14 +27,14 @@ defmodule Ejabberd.Mixfile do
[mod: {:ejabberd_app, []},
applications: [:ssl],
included_applications: [:lager, :mnesia, :p1_utils, :cache_tab,
:fast_tls, :stringprep, :fast_xml,
:fast_tls, :stringprep, :fast_xml, :xmpp,
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2]
++ cond_apps]
end
defp erlc_options do
# Use our own includes + includes from all dependencies
includes = ["include"] ++ Path.wildcard(Path.join("..", "/*/include"))
includes = ["include"] ++ Path.wildcard("deps/*/include")
[:debug_info] ++ Enum.map(includes, fn(path) -> {:i, path} end)
end
@ -42,10 +42,11 @@ defmodule Ejabberd.Mixfile do
[{:lager, "~> 3.2"},
{:p1_utils, "~> 1.0"},
{:cache_tab, "~> 1.0"},
{:stringprep, "~> 1.0"},
{:stringprep, "~> 1.0", override: true}, # override cause of :xmpp
{:fast_yaml, "~> 1.0"},
{:fast_tls, "~> 1.0"},
{:fast_xml, "~> 1.1"},
{:fast_xml, "~> 1.1", override: true}, # override cause of :xmpp
{:xmpp, github: "processone/xmpp", tag: "1.0.3"},
{:stun, "~> 1.0"},
{:esip, "~> 1.0"},
{:jiffy, "~> 0.14.7"},

View File

@ -1,7 +1,7 @@
%{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []},
"cache_tab": {:hex, :cache_tab, "1.0.4", "3fd2b1ab40c36e7830a4e09e836c6b0fa89191cd4e5fd471873e4eb42f5cd37c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
"cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []},
"earmark": {:hex, :earmark, "1.0.2", "a0b0904d74ecc14da8bd2e6e0248e1a409a2bc91aade75fcf428125603de3853", [:mix], []},
"earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []},
"erlware_commons": {:hex, :erlware_commons, "0.21.0", "a04433071ad7d112edefc75ac77719dd3e6753e697ac09428fc83d7564b80b15", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]},
"esip": {:hex, :esip, "1.0.8", "69885a6c07964aabc6c077fe1372aa810a848bd3d9a415b160dabdce9c7a79b5", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}, {:stun, "1.0.7", [hex: :stun, optional: false]}]},
"ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]},
@ -13,11 +13,12 @@
"getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []},
"goldrush": {:hex, :goldrush, "0.1.8", "2024ba375ceea47e27ea70e14d2c483b2d8610101b4e852ef7f89163cdb6e649", [:rebar3], []},
"iconv": {:hex, :iconv, "1.0.2", "a0792f06ab4b5ea1b5bb49789405739f1281a91c44cf3879cb70e4d777666217", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
"jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:rebar, :make], []},
"jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:make, :rebar], []},
"lager": {:hex, :lager, "3.2.1", "eef4e18b39e4195d37606d9088ea05bf1b745986cf8ec84f01d332456fe88d17", [:rebar3], [{:goldrush, "0.1.8", [hex: :goldrush, optional: false]}]},
"p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []},
"p1_utils": {:hex, :p1_utils, "1.0.5", "3e698354fdc1fea5491d991457b0cb986c0a00a47d224feb841dc3ec82b9f721", [:rebar3], []},
"providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]},
"relx": {:hex, :relx, "3.21.1", "f989dc520730efd9075e9f4debcb8ba1d7d1e86b018b0bcf45a2eb80270b4ad6", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.21.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]},
"stringprep": {:hex, :stringprep, "1.0.6", "1cf1c439eb038aa590da5456e019f86afbfbfeb5a2d37b6e5f873041624c6701", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
"stun": {:hex, :stun, "1.0.7", "904dc6f26a3c30c54881c4c3003699f2a4968067ee6b3aecdf9895aad02df75e", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}}
"stun": {:hex, :stun, "1.0.7", "904dc6f26a3c30c54881c4c3003699f2a4968067ee6b3aecdf9895aad02df75e", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
"xmpp": {:git, "https://github.com/processone/xmpp.git", "e4630667bc63de7ad2d236387f4631a0656a9355", [tag: "1.0.3"]}}

View File

@ -13,6 +13,7 @@
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.7"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.6"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.16"}}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.0.3"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.7"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.8"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.6"}}},
@ -55,6 +56,7 @@
luerl,
stun,
fast_yaml,
xmpp,
p1_utils,
p1_mysql,
p1_pgsql,
@ -65,7 +67,9 @@
{erl_first_files, ["src/ejabberd_config.erl", "src/gen_mod.erl"]}.
{erl_opts, [nowarn_deprecated_function,
{i, "include"}, {i, "deps/fast_xml/include"},
{i, "include"},
{i, "deps/fast_xml/include"},
{i, "deps/xmpp/include"},
{if_var_false, debug, no_debug_info},
{if_var_true, debug, debug_info},
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
@ -113,8 +117,10 @@
{if_var_false, elixir, "(\"Elixir.*\":_/_)"},
{if_var_false, redis, "(\"eredis\":_/_)"}]}.
{eunit_compile_opts, [{i, "tools"}, {i, "include"},
{i, "deps/fast_xml/include"}]}.
{eunit_compile_opts, [{i, "tools"},
{i, "include"},
{i, "deps/fast_xml/include"},
{i, "deps/xmpp/include"}]}.
{if_version_above, "17", {cover_enabled, true}}.
{cover_export_enabled, true}.

View File

@ -1 +0,0 @@
[{decode, [{<<"number_of_messages">>, {dec_int, [0, infinity]}}]}].

View File

@ -1,12 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/offline</name>
<doc>XEP-0013</doc>
<desc>
Service Discovery extension for number of messages
in an offline message queue.
</desc>
<field
var='number_of_messages'
type='text-single'
label='Number of Offline Messages'/>
</form_type>

View File

@ -1,9 +0,0 @@
[{decode, [{<<"start">>, {xmpp_util, decode_timestamp, []}},
{<<"end">>, {xmpp_util, decode_timestamp, []}}]},
{specs, [{<<"start">>, "erlang:timestamp()"},
{<<"end">>, "erlang:timestamp()"}]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,23 +0,0 @@
<form_type>
<name>urn:xmpp:mam:1</name>
<doc>XEP-0313</doc>
<desc>Form to query message archives</desc>
<field var='with'
type='jid-single'
label='User JID'/>
<field var='start'
type='text-single'
label='Search from the date'/>
<field var='end'
type='text-single'
label='Search until the date'/>
<field var='withtext'
type='text-single'
label='Search the text'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,2 +0,0 @@
[{prefix, <<"muc#register_">>},
{required, [<<"muc#register_roomnick">>]}].

View File

@ -1,37 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/muc#register</name>
<doc>XEP-0045</doc>
<desc>
Forms enabling user registration with a
Multi-User Chat (MUC) room or admin approval
of user registration requests.
</desc>
<field
var='muc#register_allow'
type='boolean'
label='Allow this person to register with the room?'/>
<field
var='muc#register_email'
type='text-single'
label='Email Address'/>
<field
var='muc#register_faqentry'
type='text-multi'
label='FAQ Entry'/>
<field
var='muc#register_first'
type='text-single'
label='Given Name'/>
<field
var='muc#register_last'
type='text-single'
label='Family Name'/>
<field
var='muc#register_roomnick'
type='text-single'
label='Nickname'/>
<field
var='muc#register_url'
type='text-single'
label='A Web Page'/>
</form_type>

View File

@ -1,2 +0,0 @@
[{prefix, <<"muc#">>},
{required, [<<"muc#role">>]}].

View File

@ -1,31 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/muc#request</name>
<doc>XEP-0045</doc>
<desc>
Forms enabling voice requests in a
Multi-User Chat (MUC) room or admin
approval of such requests.
</desc>
<field var='muc#role'
type='list-single'
label='Requested role'>
<option label='Participant'>
<value>participant</value>
</option>
</field>
<field var='muc#jid'
type='jid-single'
label='User JID'/>
<field var='muc#roomnick'
type='text-single'
label='Nickname'/>
<field var='muc#request_allow'
type='boolean'
label='Grant voice to this person?'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,11 +0,0 @@
[{prefix, <<"muc#roomconfig_">>},
{prefix, <<"muc#">>},
{decode, [{<<"muc#roomconfig_maxusers">>,
{dec_enum_int, [[none], 0, infinity]}},
{<<"voice_request_min_interval">>,
{dec_int, [0, infinity]}}]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,192 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/muc#roomconfig</name>
<doc>XEP-0045</doc>
<desc>
Forms enabling creation and configuration of
a Multi-User Chat (MUC) room.
</desc>
<field
var='muc#maxhistoryfetch'
type='text-single'
label='Maximum Number of History Messages Returned by Room'/>
<field
var='muc#roomconfig_allowpm'
type='list-single'
label='Roles that May Send Private Messages'/>
<field
var='allow_private_messages'
type='boolean'
label='Allow users to send private messages'/>
<field
var='allow_private_messages_from_visitors'
type='list-single'
label='Allow visitors to send private messages to'>
<option label='nobody'>
<value>nobody</value>
</option>
<option label='moderators only'>
<value>moderators</value>
</option>
<option label='anyone'>
<value>anyone</value>
</option>
</field>
<field
var='allow_visitor_status'
type='boolean'
label='Allow visitors to send status text in presence updates'/>
<field
var='allow_visitor_nickchange'
type='boolean'
label='Allow visitors to change nickname'/>
<field
var='allow_voice_requests'
type='boolean'
label='Allow visitors to send voice requests'/>
<field
var='allow_subscription'
type='boolean'
label='Allow subscription'/>
<field
var='voice_request_min_interval'
type='text-single'
label='Minimum interval between voice requests (in seconds)'/>
<field
var='captcha_protected'
type='boolean'
label='Make room CAPTCHA protected'/>
<field
var='captcha_whitelist'
type='jid-multi'
label='Exclude Jabber IDs from CAPTCHA challenge'/>
<field
var='allow_query_users'
type='boolean'
label='Allow users to query other users'/>
<field
var='muc#roomconfig_allowinvites'
type='boolean'
label='Allow users to send invites'/>
<field
var='muc#roomconfig_changesubject'
type='boolean'
label='Allow users to change the subject'/>
<field
var='muc#roomconfig_enablelogging'
type='boolean'
label='Enable logging'/>
<field
var='muc#roomconfig_getmemberlist'
type='list-multi'
label='Roles and Affiliations that May Retrieve Member List'/>
<field
var='muc#roomconfig_lang'
type='text-single'
label='Natural Language for Room Discussions'/>
<field
var='muc#roomconfig_pubsub'
type='text-single'
label='XMPP URI of Associated Publish-Subscribe Node'/>
<field
var='muc#roomconfig_maxusers'
type='list-single'
label='Maximum Number of Occupants'>
<option label='No limit'>
<value>none</value>
</option>
<option><value>5</value></option>
<option><value>10</value></option>
<option><value>20</value></option>
<option><value>30</value></option>
<option><value>50</value></option>
<option><value>100</value></option>
<option><value>200</value></option>
<option><value>500</value></option>
<option><value>1000</value></option>
<option><value>2000</value></option>
<option><value>5000</value></option>
</field>
<field
var='muc#roomconfig_membersonly'
type='boolean'
label='Make room members-only'/>
<field
var='muc#roomconfig_moderatedroom'
type='boolean'
label='Make room moderated'/>
<field
var='members_by_default'
type='boolean'
label='Default users as participants'/>
<field
var='muc#roomconfig_passwordprotectedroom'
type='boolean'
label='Make room password protected'/>
<field
var='muc#roomconfig_persistentroom'
type='boolean'
label='Make room persistent'/>
<field
var='muc#roomconfig_presencebroadcast'
type='list-multi'
label='Roles for which Presence is Broadcasted'>
<option label='Moderator'>
<value>moderator</value>
</option>
<option label='Participant'>
<value>participant</value>
</option>
<option label='Visitor'>
<value>visitor</value>
</option>
</field>
<field
var='muc#roomconfig_publicroom'
type='boolean'
label='Make room public searchable'/>
<field
var='public_list'
type='boolean'
label='Make participants list public'/>
<field
var='muc#roomconfig_roomadmins'
type='jid-multi'
label='Full List of Room Admins'/>
<field
var='muc#roomconfig_roomdesc'
type='text-single'
label='Room description'/>
<field
var='muc#roomconfig_roomname'
type='text-single'
label='Room title'/>
<field
var='muc#roomconfig_roomowners'
type='jid-multi'
label='Full List of Room Owners'/>
<field
var='muc#roomconfig_roomsecret'
type='text-private'
label='Password'/>
<field
var='muc#roomconfig_whois'
type='list-single'
label='Present real Jabber IDs to'>
<option label='moderators only'>
<value>moderators</value>
</option>
<option label='anyone'>
<value>anyone</value>
</option>
</field>
<field
var='mam'
type='boolean'
label='Enable message archiving'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,11 +0,0 @@
[{prefix, <<"muc#roominfo_">>},
{prefix, <<"muc#">>},
{decode, [{<<"muc#maxhistoryfetch">>,
{dec_int, [0, infinity]}},
{<<"muc#roominfo_occupants">>,
{dec_int, [0, infinity]}}]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,55 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/muc#roominfo</name>
<doc>XEP-0045</doc>
<desc>
Forms enabling the communication of extended service discovery
information about a Multi-User Chat (MUC) room.
</desc>
<field
var='muc#maxhistoryfetch'
type='text-single'
label='Maximum Number of History Messages Returned by Room'/>
<field
var='muc#roominfo_contactjid'
type='jid-multi'
label='Contact Addresses (normally, room owner or owners)'/>
<field
var='muc#roominfo_description'
type='text-single'
label='Room description'/>
<field
var='muc#roominfo_lang'
type='text-single'
label='Natural Language for Room Discussions'/>
<field
var='muc#roominfo_ldapgroup'
type='text-single'
label='An associated LDAP group that defines
room membership; this should be an LDAP
Distinguished Name according to an
implementation-specific or
deployment-specific definition of a
group.'/>
<field
var='muc#roominfo_logs'
type='text-single'
label='URL for Archived Discussion Logs'/>
<field
var='muc#roominfo_occupants'
type='text-single'
label='Number of occupants'/>
<field
var='muc#roominfo_subject'
type='text-single'
label='Current Discussion Topic'/>
<field
var='muc#roominfo_subjectmod'
type='boolean'
label='The room subject can be modified by participants'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,7 +0,0 @@
[{prefix, <<"pubsub#">>},
{required, [<<"pubsub#node">>]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,15 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/pubsub#subscribe_authorization</name>
<doc>XEP-0060</doc>
<desc>Forms enabling authorization of subscriptions to pubsub nodes</desc>
<field
var='pubsub#node'
type='list-single'
label='The NodeID of the relevant node'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,8 +0,0 @@
[{prefix, <<"pubsub#">>},
{decode, [{<<"pubsub#max_items">>, {dec_int, [0,infinity]}},
{<<"pubsub#max_payload_size">>, {dec_int, [0,infinity]}}]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,189 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/pubsub#node_config</name>
<doc>XEP-0060</doc>
<desc>Forms enabling configuration of pubsub nodes</desc>
<field var='pubsub#access_model'
type='list-single'
label='Specify the access model'>
<option label='Subscription requests must be approved and only subscribers may retrieve items'>
<value>authorize</value>
</option>
<option label='Anyone may subscribe and retrieve items'>
<value>open</value>
</option>
<option label='Anyone with a presence subscription of both or from may subscribe and retrieve items'>
<value>presence</value>
</option>
<option label='Anyone in the specified roster group(s) may subscribe and retrieve items'>
<value>roster</value>
</option>
<option label='Only those on a whitelist may subscribe and retrieve items'>
<value>whitelist</value>
</option>
</field>
<field var='pubsub#body_xslt'
type='text-single'
label='The URL of an XSL transformation which can be
applied to payloads in order to generate an
appropriate message body element.'/>
<field var='pubsub#children_association_policy'
type='list-single'
label='Who may associate leaf nodes with a collection'>
<option label='Anyone may associate leaf nodes with the collection'>
<value>all</value>
</option>
<option label='Only collection node owners may associate leaf nodes with the collection'>
<value>owners</value>
</option>
<option label='Only those on a whitelist may associate leaf nodes with the collection'>
<value>whitelist</value>
</option>
</field>
<field var='pubsub#children_association_whitelist'
type='jid-multi'
label='The list of JIDs that may associate leaf nodes with a collection'/>
<field var='pubsub#children'
type='text-multi'
label='The child nodes (leaf or collection) associated with a collection'/>
<field var='pubsub#children_max'
type='text-single'
label='The maximum number of child nodes that can be associated with a collection'/>
<field var='pubsub#collection'
type='text-multi'
label='The collections with which a node is affiliated'/>
<field var='pubsub#contact'
type='jid-multi'
label='The JIDs of those to contact with questions'/>
<field var='pubsub#dataform_xslt'
type='text-single'
label='The URL of an XSL transformation which can be
applied to the payload format in order to generate
a valid Data Forms result that the client could
display using a generic Data Forms rendering
engine'/>
<field var='pubsub#deliver_notifications' type='boolean'
label='Deliver event notifications'>
<value>true</value>
</field>
<field var='pubsub#deliver_payloads'
type='boolean'
label='Deliver payloads with event notifications'/>
<field var='pubsub#description'
type='text-single'
label='A description of the node'/>
<field var='pubsub#item_expire'
type='text-single'
label='Number of seconds after which to automatically purge items'/>
<field var='pubsub#itemreply'
type='list-single'
label='Whether owners or publisher should receive replies to items'>
<option label='Statically specify a replyto of the node owner(s)'>
<value>owner</value>
</option>
<option label='Dynamically specify a replyto of the item publisher'>
<value>publisher</value>
</option>
<option>
<value>none</value>
</option>
</field>
<field var='pubsub#language'
type='list-single'
label='The default language of the node'/>
<field var='pubsub#max_items'
type='text-single'
label='Max # of items to persist'>
</field>
<field var='pubsub#max_payload_size'
type='text-single'
label='Max payload size in bytes'/>
<field var='pubsub#node_type'
type='list-single'
label='Whether the node is a leaf (default) or a collection'>
<option label='The node is a leaf node (default)'>
<value>leaf</value>
</option>
<option label='The node is a collection node'>
<value>collection</value>
</option>
</field>
<field var='pubsub#notification_type' type='list-single'
label='Specify the event message type'>
<option label='Messages of type normal'>
<value>normal</value>
</option>
<option label='Messages of type headline'>
<value>headline</value>
</option>
</field>
<field var='pubsub#notify_config'
type='boolean'
label='Notify subscribers when the node configuration changes'/>
<field var='pubsub#notify_delete'
type='boolean'
label='Notify subscribers when the node is deleted'/>
<field var='pubsub#notify_retract'
type='boolean'
label='Notify subscribers when items are removed from the node'/>
<field var='pubsub#notify_sub'
type='boolean'
label='Whether to notify owners about new subscribers and unsubscribes'/>
<field var='pubsub#persist_items'
type='boolean'
label='Persist items to storage'/>
<field var='pubsub#presence_based_delivery'
type='boolean'
label='Only deliver notifications to available users'/>
<field var='pubsub#publish_model'
type='list-single'
label='Specify the publisher model'>
<option label='Only publishers may publish'>
<value>publishers</value>
</option>
<option label='Subscribers may publish'>
<value>subscribers</value>
</option>
<option label='Anyone may publish'>
<value>open</value>
</option>
</field>
<field var='pubsub#purge_offline'
type='boolean'
label='Purge all items when the relevant publisher goes offline'/>
<field var='pubsub#roster_groups_allowed'
type='list-multi'
label='Roster groups allowed to subscribe'/>
<field var='pubsub#send_last_published_item'
type='list-single'
label='When to send the last published item'>
<option label='Never'>
<value>never</value>
</option>
<option label='When a new subscription is processed'>
<value>on_sub</value>
</option>
<option label='When a new subscription is processed and whenever a subscriber comes online'>
<value>on_sub_and_presence</value>
</option>
</field>
<field var='pubsub#tempsub'
type='boolean'
label='Whether to make all subscriptions temporary, based on subscriber presence'/>
<field var='pubsub#subscribe' type='boolean'
label='Whether to allow subscriptions'>
<value>1</value>
</field>
<field var='pubsub#title'
type='text-single'
label='A friendly name for the node'/>
<field var='pubsub#type'
type='text-single'
label='The type of node data, usually specified by
the namespace of the payload (if any)'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,6 +0,0 @@
[{prefix, <<"pubsub#">>}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,34 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/pubsub#publish-options</name>
<doc>XEP-0060</doc>
<desc>
Forms enabling publication with options; each field must specify whether it
defines METADATA to be attached to the item, a per-item OVERRIDE of the node
configuration, or a PRECONDITION to be checked against the node configuration.
</desc>
<field var='pubsub#access_model'
type='list-single'
label='Specify the access model'>
<option label='Access model of authorize'>
<value>authorize</value>
</option>
<option label='Access model of open'>
<value>open</value>
</option>
<option label='Access model of presence'>
<value>presence</value>
</option>
<option label='Access model of roster'>
<value>roster</value>
</option>
<option label='Access model of whitelist'>
<value>whitelist</value>
</option>
</field>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1,7 +0,0 @@
[{prefix, <<"pubsub#">>},
{required, [<<"pubsub#node">>, <<"pubsub#subscriber_jid">>, <<"pubsub#allow">>]}].
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:

View File

@ -1,27 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/pubsub#subscribe_authorization</name>
<doc>XEP-0060</doc>
<desc>Forms enabling authorization of subscriptions to pubsub nodes</desc>
<field
var='pubsub#allow'
type='boolean'
label='Allow this Jabber ID to subscribe to this pubsub node?'/>
<field
var='pubsub#node'
type='text-single'
label='Node ID'/>
<field
var='pubsub#subscriber_jid'
type='jid-single'
label='Subscriber Address'/>
<field
var='pubsub#subid'
type='text-single'
label='The subscription identifier associated with the subscription request'/>
</form_type>
<!--
Local Variables:
mode: xml
End:
-->

View File

@ -1 +0,0 @@
[{prefix, <<"pubsub#">>}].

View File

@ -1,70 +0,0 @@
<form_type>
<name>http://jabber.org/protocol/pubsub#subscribe_options</name>
<doc>XEP-0060</doc>
<desc>Forms enabling configuration of subscription options for pubsub nodes</desc>
<field
var='pubsub#deliver'
type='boolean'
label='Whether an entity wants to receive
or disable notifications'/>
<field
var='pubsub#digest'
type='boolean'
label='Whether an entity wants to receive digests
(aggregations) of notifications or all
notifications individually'/>
<field var='pubsub#digest_frequency'
type='text-single'
label='The minimum number of milliseconds between
sending any two notification digests'/>
<field
var='pubsub#expire'
type='text-single'
label='The DateTime at which a leased subscription
will end or has ended'/>
<field
var='pubsub#include_body'
type='boolean'
label='Whether an entity wants to receive an XMPP
message body in addition to the payload
format'/>
<field
var='pubsub#show-values'
type='list-multi'
label='The presence states for which an entity
wants to receive notifications'>
<option label='XMPP Show Value of Away'>
<value>away</value>
</option>
<option label='XMPP Show Value of Chat'>
<value>chat</value>
</option>
<option label='XMPP Show Value of DND (Do Not Disturb)'>
<value>dnd</value>
</option>
<option label='Mere Availability in XMPP (No Show Value)'>
<value>online</value>
</option>
<option label='XMPP Show Value of XA (Extended Away)'>
<value>xa</value>
</option>
</field>
<field var='pubsub#subscription_type'
type='list-single'>
<option label='Receive notification of new items only'>
<value>items</value>
</option>
<option label='Receive notification of new nodes only'>
<value>nodes</value>
</option>
</field>
<field var='pubsub#subscription_depth'
type='list-single'>
<option label='Receive notification from direct child nodes only'>
<value>1</value>
</option>
<option label='Receive notification from all descendent nodes'>
<value>all</value>
</option>
</field>
</form_type>

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,6 @@
start(normal, _Args) ->
ejabberd_logger:start(),
write_pid_file(),
jid:start(),
start_apps(),
start_elixir_application(),
ejabberd:check_app(ejabberd),
@ -223,8 +222,7 @@ start_apps() ->
ejabberd:start_app(ssl),
ejabberd:start_app(fast_yaml),
ejabberd:start_app(fast_tls),
ejabberd:start_app(fast_xml),
ejabberd:start_app(stringprep),
ejabberd:start_app(xmpp),
ejabberd:start_app(cache_tab).
opt_type(net_ticktime) ->

View File

@ -36,8 +36,8 @@
check_password/6, check_password_with_authmodule/4,
check_password_with_authmodule/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2, export/1, import/1,
get_vh_registered_users_number/1, import/3,
get_vh_registered_users/2, export/1, import_info/0,
get_vh_registered_users_number/1, import/5, import_start/2,
get_vh_registered_users_number/2, get_password/2,
get_password_s/2, get_password_with_authmodule/2,
is_user_exists/2, is_user_exists_in_other_modules/3,
@ -438,15 +438,20 @@ auth_modules(Server) ->
export(Server) ->
ejabberd_auth_mnesia:export(Server).
import(Server) ->
ejabberd_auth_mnesia:import(Server).
import_info() ->
[{<<"users">>, 3}].
import(Server, mnesia, Passwd) ->
ejabberd_auth_mnesia:import(Server, mnesia, Passwd);
import(Server, riak, Passwd) ->
ejabberd_auth_riak:import(Server, riak, Passwd);
import(_, _, _) ->
pass.
import_start(_LServer, mnesia) ->
ejabberd_auth_mnesia:init_db();
import_start(_LServer, _) ->
ok.
import(Server, {sql, _}, mnesia, <<"users">>, Fields) ->
ejabberd_auth_mnesia:import(Server, Fields);
import(Server, {sql, _}, riak, <<"users">>, Fields) ->
ejabberd_auth_riak:import(Server, Fields);
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
ok.
opt_type(auth_method) ->
fun (V) when is_list(V) ->

View File

@ -36,12 +36,12 @@
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users/2, init_db/0,
get_vh_registered_users_number/1,
get_vh_registered_users_number/2, get_password/2,
get_password_s/2, is_user_exists/2, remove_user/2,
remove_user/3, store_type/0, export/1, import/1,
import/3, plain_password_required/0, opt_type/1]).
remove_user/3, store_type/0, export/1, import/2,
plain_password_required/0, opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -59,16 +59,19 @@
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
init_db(),
update_table(),
update_reg_users_counter_table(Host),
maybe_alert_password_scrammed_without_option(),
ok.
init_db() ->
mnesia:create_table(passwd,
[{disc_copies, [node()]},
{attributes, record_info(fields, passwd)}]),
mnesia:create_table(reg_users_counter,
[{ram_copies, [node()]},
{attributes, record_info(fields, reg_users_counter)}]),
update_table(),
update_reg_users_counter_table(Host),
maybe_alert_password_scrammed_without_option(),
ok.
{attributes, record_info(fields, reg_users_counter)}]).
update_reg_users_counter_table(Server) ->
Set = get_vh_registered_users(Server),
@ -493,16 +496,9 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select username, password from users;">>,
fun([LUser, Password]) ->
#passwd{us = {LUser, LServer}, password = Password}
end}].
import(_LServer, mnesia, #passwd{} = P) ->
mnesia:dirty_write(P);
import(_, _, _) ->
pass.
import(LServer, [LUser, Password, _TimeStamp]) ->
mnesia:dirty_write(
#passwd{us = {LUser, LServer}, password = Password}).
opt_type(auth_password_format) -> fun (V) -> V end;
opt_type(_) -> [auth_password_format].

View File

@ -27,6 +27,8 @@
-compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(ejabberd_config).
-author('alexey@process-one.net').
-behaviour(ejabberd_auth).
@ -39,8 +41,8 @@
get_vh_registered_users_number/1,
get_vh_registered_users_number/2, get_password/2,
get_password_s/2, is_user_exists/2, remove_user/2,
remove_user/3, store_type/0, export/1, import/3,
plain_password_required/0]).
remove_user/3, store_type/0, export/1, import/2,
plain_password_required/0, opt_type/1]).
-export([passwd_schema/0]).
-include("ejabberd.hrl").
@ -301,7 +303,9 @@ export(_Server) ->
[]
end}].
import(LServer, riak, #passwd{} = Passwd) ->
ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]);
import(_, _, _) ->
pass.
import(LServer, [LUser, Password, _TimeStamp]) ->
Passwd = #passwd{us = {LUser, LServer}, password = Password},
ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]).
opt_type(auth_password_format) -> fun (V) -> V end;
opt_type(_) -> [auth_password_format].

1095
src/ejabberd_bosh.erl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -277,6 +277,10 @@ get_commands_spec() ->
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
result_example = ok}].
init() ->
try mnesia:transform_table(ejabberd_commands, ignore,
record_info(fields, ejabberd_commands))
catch exit:{aborted, {no_exists, _}} -> ok
end,
mnesia:create_table(ejabberd_commands,
[{ram_copies, [node()]},
{local_content, true},

View File

@ -231,10 +231,10 @@ open_socket(init, StateData) ->
send_header(NewStateData, Version),
{next_state, wait_for_stream, NewStateData,
?FSMTIMEOUT};
{error, _Reason} ->
{error, Reason} ->
?INFO_MSG("s2s connection: ~s -> ~s (remote server "
"not found)",
[StateData#state.myname, StateData#state.server]),
"not found: ~p)",
[StateData#state.myname, StateData#state.server, Reason]),
case ejabberd_hooks:run_fold(find_s2s_bridge, undefined,
[StateData#state.myname,
StateData#state.server])

View File

@ -52,12 +52,14 @@
-include("logger.hrl").
-type sockmod() :: ejabberd_http_bind |
ejabberd_bosh |
ejabberd_http_ws |
gen_tcp | fast_tls | ezlib.
-type receiver() :: pid () | atom().
-type socket() :: pid() | inet:socket() |
fast_tls:tls_socket() |
ezlib:zlib_socket() |
ezlib:zlib_socket() |
ejabberd_bosh:bind_socket() |
ejabberd_http_bind:bind_socket().
-record(socket_state, {sockmod = gen_tcp :: sockmod(),
@ -228,6 +230,7 @@ get_transport(#socket_state{sockmod = SockMod,
tcp -> tcp_zlib;
tls -> tls_zlib
end;
ejabberd_bosh -> http_bind;
ejabberd_http_bind -> http_bind;
ejabberd_http_ws -> websocket
end.
@ -254,4 +257,3 @@ peername(#socket_state{sockmod = SockMod,
gen_tcp -> inet:peername(Socket);
_ -> SockMod:peername(Socket)
end.

View File

@ -61,10 +61,6 @@ start_link(Host) ->
?MODULE, [Host]).
init([Host]) ->
PoolSize = ejabberd_config:get_option(
{sql_pool_size, Host},
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_POOL_SIZE),
StartInterval = ejabberd_config:get_option(
{sql_start_interval, Host},
fun(I) when is_integer(I), I>0 -> I end,
@ -76,6 +72,7 @@ init([Host]) ->
(mssql) -> mssql;
(odbc) -> odbc
end, odbc),
PoolSize = get_pool_size(Type, Host),
case Type of
sqlite ->
check_sqlite_db(Host);
@ -117,6 +114,23 @@ remove_pid(Host, Pid) ->
end,
mnesia:ets(F).
-spec get_pool_size(atom(), binary()) -> pos_integer().
get_pool_size(SQLType, Host) ->
PoolSize = ejabberd_config:get_option(
{sql_pool_size, Host},
fun(I) when is_integer(I), I>0 -> I end,
case SQLType of
sqlite -> 1;
_ -> ?DEFAULT_POOL_SIZE
end),
if PoolSize > 1 andalso SQLType == sqlite ->
?WARNING_MSG("it's not recommended to set sql_pool_size > 1 for "
"sqlite, because it may cause race conditions", []);
true ->
ok
end,
PoolSize.
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).

View File

@ -30,12 +30,12 @@
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
-export([export/2, export/3, import_file/2, import/2,
import/3, delete/1]).
-export([export/2, export/3, import/3, import/4, delete/1, import_info/1]).
-define(MAX_RECORDS_PER_TRANSACTION, 100).
-record(dump, {fd, cont = start}).
-record(sql_dump, {fd, type}).
%%%----------------------------------------------------------------------
%%% API
@ -50,13 +50,14 @@
modules() ->
[ejabberd_auth,
mod_announce,
mod_caps,
mod_irc,
mod_last,
mod_muc,
mod_offline,
mod_privacy,
mod_private,
%% mod_pubsub,
mod_pubsub,
mod_roster,
mod_shared_roster,
mod_vcard,
@ -100,49 +101,44 @@ delete(Server, Module) ->
delete(LServer, Table, ConvertFun)
end, Module:export(Server)).
import_file(Server, FileName) when is_binary(FileName) ->
import(Server, binary_to_list(FileName));
import_file(Server, FileName) ->
case disk_log:open([{name, make_ref()},
{file, FileName},
{mode, read_only}]) of
{ok, Fd} ->
LServer = jid:nameprep(Server),
Mods = [{Mod, gen_mod:db_type(LServer, Mod)}
|| Mod <- modules(), gen_mod:is_loaded(LServer, Mod)],
AuthMods = case lists:member(ejabberd_auth_mnesia,
ejabberd_auth:auth_modules(LServer)) of
true ->
[{ejabberd_auth, mnesia}];
false ->
[]
end,
import_dump(LServer, AuthMods ++ Mods, #dump{fd = Fd});
Err ->
exit(Err)
end.
import(Server, Output) ->
import(Server, Output, [{fast, true}]).
import(Server, Output, Opts) ->
LServer = jid:nameprep(iolist_to_binary(Server)),
Modules = modules(),
IO = prepare_output(Output, disk_log),
import(Server, Dir, ToType) ->
lists:foreach(
fun(Module) ->
import(LServer, IO, Opts, Module)
end, Modules),
close_output(Output, IO).
fun(Mod) ->
?INFO_MSG("importing ~p...", [Mod]),
import(Mod, Server, Dir, ToType)
end, modules()).
import(Server, Output, Opts, Module) ->
import(Mod, Server, Dir, ToType) ->
LServer = jid:nameprep(iolist_to_binary(Server)),
IO = prepare_output(Output, disk_log),
try Mod:import_start(LServer, ToType)
catch error:undef -> ok end,
lists:foreach(
fun({SelectQuery, ConvertFun}) ->
import(LServer, SelectQuery, IO, ConvertFun, Opts)
end, Module:import(Server)),
close_output(Output, IO).
fun({File, Tab, _Mod, FieldsNumber}) ->
FileName = filename:join([Dir, File]),
case open_sql_dump(FileName) of
{ok, #sql_dump{type = FromType} = Dump} ->
import_rows(LServer, {sql, FromType}, ToType,
Tab, Mod, Dump, FieldsNumber),
close_sql_dump(Dump);
{error, enoent} ->
ok;
eof ->
?INFO_MSG("It seems like SQL dump ~s is empty", [FileName]);
Err ->
?ERROR_MSG("Failed to open SQL dump ~s: ~s",
[FileName, format_error(Err)])
end
end, import_info(Mod)),
try Mod:import_stop(LServer, ToType)
catch error:undef -> ok end.
import_info(Mod) ->
Info = Mod:import_info(),
lists:map(
fun({Tab, FieldsNum}) ->
FileName = <<Tab/binary, ".txt">>,
{FileName, Tab, Mod, FieldsNum}
end, Info).
%%%----------------------------------------------------------------------
%%% Internal functions
@ -200,79 +196,6 @@ delete(LServer, Table, ConvertFun) ->
end,
mnesia:transaction(F).
import(LServer, SelectQuery, IO, ConvertFun, Opts) ->
F = case proplists:get_bool(fast, Opts) of
true ->
fun() ->
case ejabberd_sql:sql_query_t(SelectQuery) of
{selected, _, Rows} ->
lists:foldl(fun process_sql_row/2,
{IO, ConvertFun, undefined}, Rows);
Err ->
erlang:error(Err)
end
end;
false ->
fun() ->
ejabberd_sql:sql_query_t(
[iolist_to_binary(
[<<"declare c cursor for ">>, SelectQuery])]),
fetch(IO, ConvertFun, undefined)
end
end,
ejabberd_sql:sql_transaction(LServer, F).
fetch(IO, ConvertFun, PrevRow) ->
case ejabberd_sql:sql_query_t([<<"fetch c;">>]) of
{selected, _, [Row]} ->
process_sql_row(Row, {IO, ConvertFun, PrevRow}),
fetch(IO, ConvertFun, Row);
{selected, _, []} ->
ok;
Err ->
erlang:error(Err)
end.
process_sql_row(Row, {IO, ConvertFun, PrevRow}) when Row == PrevRow ->
%% Avoid calling ConvertFun with the same input
{IO, ConvertFun, Row};
process_sql_row(Row, {IO, ConvertFun, _PrevRow}) ->
case catch ConvertFun(Row) of
{'EXIT', _} = Err ->
?ERROR_MSG("failed to convert ~p: ~p", [Row, Err]);
Term ->
ok = disk_log:log(IO#dump.fd, Term)
end,
{IO, ConvertFun, Row}.
import_dump(LServer, Mods, #dump{fd = Fd, cont = Cont}) ->
case disk_log:chunk(Fd, Cont) of
{NewCont, Terms} ->
import_terms(LServer, Mods, Terms),
import_dump(LServer, Mods, #dump{fd = Fd, cont = NewCont});
eof ->
ok;
Err ->
exit(Err)
end.
import_terms(LServer, Mods, [Term|Terms]) ->
import_term(LServer, Mods, Term),
import_terms(LServer, Mods, Terms);
import_terms(_LServer, _Mods, []) ->
ok.
import_term(LServer, [{Mod, DBType}|Mods], Term) ->
case catch Mod:import(LServer, DBType, Term) of
pass -> import_term(LServer, Mods, Term);
ok -> ok;
Err ->
?ERROR_MSG("failed to import ~p for module ~p: ~p",
[Term, Mod, Err])
end;
import_term(_LServer, [], _Term) ->
ok.
prepare_output(FileName) ->
prepare_output(FileName, normal).
@ -285,25 +208,11 @@ prepare_output(FileName, normal) when is_list(FileName) ->
Err ->
exit(Err)
end;
prepare_output(FileName, disk_log) when is_list(FileName) ->
case disk_log:open([{name, make_ref()},
{repair, truncate},
{file, FileName}]) of
{ok, Fd} ->
#dump{fd = Fd};
Err ->
exit(Err)
end;
prepare_output(Output, _Type) ->
Output.
close_output(FileName, Fd) when FileName /= Fd ->
case Fd of
#dump{} ->
disk_log:close(Fd#dump.fd);
_ ->
file:close(Fd)
end,
file:close(Fd),
ok;
close_output(_, _) ->
ok.
@ -321,6 +230,129 @@ flatten1([H|T], Acc) ->
flatten1([], Acc) ->
Acc.
import_rows(LServer, FromType, ToType, Tab, Mod, Dump, FieldsNumber) ->
case read_row_from_sql_dump(Dump, FieldsNumber) of
{ok, Fields} ->
case catch Mod:import(LServer, FromType, ToType, Tab, Fields) of
ok ->
ok;
Err ->
?ERROR_MSG("Failed to import fields ~p for tab ~p: ~p",
[Fields, Tab, Err])
end,
import_rows(LServer, FromType, ToType,
Tab, Mod, Dump, FieldsNumber);
eof ->
ok;
Err ->
?ERROR_MSG("Failed to read row from SQL dump: ~s",
[format_error(Err)])
end.
open_sql_dump(FileName) ->
case file:open(FileName, [raw, read, binary, read_ahead]) of
{ok, Fd} ->
case file:read(Fd, 11) of
{ok, <<"PGCOPY\n", 16#ff, "\r\n", 0>>} ->
case skip_pgcopy_header(Fd) of
ok ->
{ok, #sql_dump{fd = Fd, type = pgsql}};
Err ->
Err
end;
{ok, _} ->
file:position(Fd, 0),
{ok, #sql_dump{fd = Fd, type = mysql}};
Err ->
Err
end;
Err ->
Err
end.
close_sql_dump(#sql_dump{fd = Fd}) ->
file:close(Fd).
read_row_from_sql_dump(#sql_dump{fd = Fd, type = pgsql}, _) ->
case file:read(Fd, 2) of
{ok, <<(-1):16/signed>>} ->
eof;
{ok, <<FieldsNum:16>>} ->
read_fields(Fd, FieldsNum, []);
{ok, _} ->
{error, eof};
eof ->
{error, eof};
{error, _} = Err ->
Err
end;
read_row_from_sql_dump(#sql_dump{fd = Fd, type = mysql}, FieldsNum) ->
read_lines(Fd, FieldsNum, <<"">>, []).
skip_pgcopy_header(Fd) ->
try
{ok, <<_:4/binary, ExtSize:32>>} = file:read(Fd, 8),
{ok, <<_:ExtSize/binary>>} = file:read(Fd, ExtSize),
ok
catch error:{badmatch, {error, _} = Err} ->
Err;
error:{badmatch, _} ->
{error, eof}
end.
read_fields(_Fd, 0, Acc) ->
{ok, lists:reverse(Acc)};
read_fields(Fd, N, Acc) ->
case file:read(Fd, 4) of
{ok, <<(-1):32/signed>>} ->
read_fields(Fd, N-1, [null|Acc]);
{ok, <<ValSize:32>>} ->
case file:read(Fd, ValSize) of
{ok, <<Val:ValSize/binary>>} ->
read_fields(Fd, N-1, [Val|Acc]);
{ok, _} ->
{error, eof};
Err ->
Err
end;
{ok, _} ->
{error, eof};
eof ->
{error, eof};
{error, _} = Err ->
Err
end.
read_lines(_Fd, 0, <<"">>, Acc) ->
{ok, lists:reverse(Acc)};
read_lines(Fd, N, Buf, Acc) ->
case file:read_line(Fd) of
{ok, Data} when size(Data) >= 2 ->
Size = size(Data) - 2,
case Data of
<<Val:Size/binary, 0, $\n>> ->
NewBuf = <<Buf/binary, Val/binary>>,
read_lines(Fd, N-1, <<"">>, [NewBuf|Acc]);
_ ->
NewBuf = <<Buf/binary, Data/binary>>,
read_lines(Fd, N, NewBuf, Acc)
end;
{ok, Data} ->
NewBuf = <<Buf/binary, Data/binary>>,
read_lines(Fd, N, NewBuf, Acc);
eof when Buf == <<"">>, Acc == [] ->
eof;
eof ->
{error, eof};
{error, _} = Err ->
Err
end.
format_error({error, eof}) ->
"unexpected end of file";
format_error({error, Posix}) ->
file:format_error(Posix).
format_queries(SQLs) ->
lists:map(
fun(#sql_query{} = SQL) ->

View File

@ -1,128 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: flex_offline.xdata
%% Form type: http://jabber.org/protocol/offline
%% Document: XEP-0013
-module(flex_offline).
-export([decode/1, decode/2, encode/1, encode/2,
format_error/1]).
-include("xmpp_codec.hrl").
-include("flex_offline.hrl").
-export_type([property/0, result/0, form/0]).
dec_int(Val, Min, Max) ->
case list_to_integer(binary_to_list(Val)) of
Int when Int =< Max, Min == infinity -> Int;
Int when Int =< Max, Int >= Min -> Int
end.
enc_int(Int) -> integer_to_binary(Int).
format_error({form_type_mismatch, Type}) ->
<<"FORM_TYPE doesn't match '", Type/binary, "'">>;
format_error({bad_var_value, Var, Type}) ->
<<"Bad value of field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({missing_value, Var, Type}) ->
<<"Missing value of field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({too_many_values, Var, Type}) ->
<<"Too many values for field '", Var/binary,
"' of type '", Type/binary, "'">>;
format_error({unknown_var, Var, Type}) ->
<<"Unknown field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({missing_required_var, Var, Type}) ->
<<"Missing required field '", Var/binary, "' of type '",
Type/binary, "'">>.
decode(Fs) -> decode(Fs, []).
decode(Fs, Acc) ->
case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var,
Fs)
of
false -> decode(Fs, Acc, []);
#xdata_field{values =
[<<"http://jabber.org/protocol/offline">>]} ->
decode(Fs, Acc, []);
_ ->
erlang:error({?MODULE,
{form_type_mismatch,
<<"http://jabber.org/protocol/offline">>}})
end.
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
encode(List, Translate) when is_list(List) ->
Fs = [case Opt of
{number_of_messages, Val} ->
[encode_number_of_messages(Val, Translate)];
{number_of_messages, _, _} ->
erlang:error({badarg, Opt});
#xdata_field{} -> [Opt];
_ -> []
end
|| Opt <- List],
FormType = #xdata_field{var = <<"FORM_TYPE">>,
type = hidden,
values =
[<<"http://jabber.org/protocol/offline">>]},
[FormType | lists:flatten(Fs)].
decode([#xdata_field{var = <<"number_of_messages">>,
values = [Value]}
| Fs],
Acc, Required) ->
try dec_int(Value, 0, infinity) of
Result ->
decode(Fs, [{number_of_messages, Result} | Acc],
lists:delete(<<"number_of_messages">>, Required))
catch
_:_ ->
erlang:error({?MODULE,
{bad_var_value, <<"number_of_messages">>,
<<"http://jabber.org/protocol/offline">>}})
end;
decode([#xdata_field{var = <<"number_of_messages">>,
values = []} =
F
| Fs],
Acc, Required) ->
decode([F#xdata_field{var = <<"number_of_messages">>,
values = [<<>>]}
| Fs],
Acc, Required);
decode([#xdata_field{var = <<"number_of_messages">>}
| _],
_, _) ->
erlang:error({?MODULE,
{too_many_values, <<"number_of_messages">>,
<<"http://jabber.org/protocol/offline">>}});
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
if Var /= <<"FORM_TYPE">> ->
erlang:error({?MODULE,
{unknown_var, Var,
<<"http://jabber.org/protocol/offline">>}});
true -> decode(Fs, Acc, Required)
end;
decode([], _, [Var | _]) ->
erlang:error({?MODULE,
{missing_required_var, Var,
<<"http://jabber.org/protocol/offline">>}});
decode([], Acc, []) -> Acc.
encode_number_of_messages(Value, Translate) ->
Values = case Value of
undefined -> [];
Value -> [enc_int(Value)]
end,
Opts = [],
#xdata_field{var = <<"number_of_messages">>,
values = Values, required = false, type = 'text-single',
options = Opts, desc = <<>>,
label = Translate(<<"Number of Offline Messages">>)}.

View File

@ -1,266 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @doc
%%% JID processing library
%%% @end
%%% Created : 24 Nov 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-module(jid).
%% API
-export([start/0,
make/1,
make/2,
make/3,
split/1,
from_string/1,
to_string/1,
is_nodename/1,
nodeprep/1,
nameprep/1,
resourceprep/1,
tolower/1,
remove_resource/1,
replace_resource/2]).
-include("jid.hrl").
-export_type([jid/0]).
-export_type([ljid/0]).
%%%===================================================================
%%% API
%%%===================================================================
-spec start() -> ok.
start() ->
{ok, Owner} = ets_owner(),
SplitPattern = binary:compile_pattern([<<"@">>, <<"/">>]),
%% Table is public to allow ETS insert to fix / update the table even if table already exist
%% with another owner.
catch ets:new(jlib, [named_table, public, set, {keypos, 1}, {heir, Owner, undefined}]),
ets:insert(jlib, {string_to_jid_pattern, SplitPattern}),
ok.
ets_owner() ->
case whereis(jlib_ets) of
undefined ->
Pid = spawn(fun() -> ets_keepalive() end),
case catch register(jlib_ets, Pid) of
true ->
{ok, Pid};
Error -> Error
end;
Pid ->
{ok,Pid}
end.
%% Process used to keep jlib ETS table alive in case the original owner dies.
%% The table need to be public, otherwise subsequent inserts would fail.
ets_keepalive() ->
receive
_ ->
ets_keepalive()
end.
-spec make(binary(), binary(), binary()) -> jid() | error.
make(User, Server, Resource) ->
case nodeprep(User) of
error -> error;
LUser ->
case nameprep(Server) of
error -> error;
LServer ->
case resourceprep(Resource) of
error -> error;
LResource ->
#jid{user = User, server = Server, resource = Resource,
luser = LUser, lserver = LServer,
lresource = LResource}
end
end
end.
-spec make(binary(), binary()) -> jid() | error.
make(User, Server) ->
make(User, Server, <<"">>).
-spec make({binary(), binary(), binary()} | binary()) -> jid() | error.
make({User, Server, Resource}) ->
make(User, Server, Resource);
make(Server) ->
make(<<"">>, Server, <<"">>).
%% This is the reverse of make_jid/1
-spec split(jid()) -> {binary(), binary(), binary()} | error.
split(#jid{user = U, server = S, resource = R}) ->
{U, S, R};
split(_) ->
error.
-spec from_string(binary() | string()) -> jid() | error.
from_string(S) when is_list(S) ->
%% We do not accept list because we want to enforce good practice of
%% using binaries for string. However, we do not let it crash to avoid
%% losing associated ets table.
{error, need_jid_as_binary};
from_string(<<>>) ->
error;
from_string(S) when is_binary(S) ->
SplitPattern = ets:lookup_element(jlib, string_to_jid_pattern, 2),
Size = size(S),
End = Size-1,
case binary:match(S, SplitPattern) of
{0, _} ->
error;
{End, _} ->
error;
{Pos1, _} ->
case binary:at(S, Pos1) of
$/ ->
make(<<>>,
binary:part(S, 0, Pos1),
binary:part(S, Pos1+1, Size-Pos1-1));
_ ->
Pos1N = Pos1+1,
case binary:match(S, SplitPattern, [{scope, {Pos1+1, Size-Pos1-1}}]) of
{End, _} ->
error;
{Pos1N, _} ->
error;
{Pos2, _} ->
case binary:at(S, Pos2) of
$/ ->
make(binary:part(S, 0, Pos1),
binary:part(S, Pos1+1, Pos2-Pos1-1),
binary:part(S, Pos2+1, Size-Pos2-1));
_ -> error
end;
_ ->
make(binary:part(S, 0, Pos1),
binary:part(S, Pos1+1, Size-Pos1-1),
<<>>)
end
end;
_ ->
make(<<>>, S, <<>>)
end.
-spec to_string(jid() | ljid()) -> binary().
to_string(#jid{user = User, server = Server,
resource = Resource}) ->
to_string({User, Server, Resource});
to_string({N, S, R}) ->
Node = iolist_to_binary(N),
Server = iolist_to_binary(S),
Resource = iolist_to_binary(R),
S1 = case Node of
<<"">> -> <<"">>;
_ -> <<Node/binary, "@">>
end,
S2 = <<S1/binary, Server/binary>>,
S3 = case Resource of
<<"">> -> S2;
_ -> <<S2/binary, "/", Resource/binary>>
end,
S3.
-spec is_nodename(binary()) -> boolean().
is_nodename(Node) ->
N = nodeprep(Node),
(N /= error) and (N /= <<>>).
-define(LOWER(Char),
if Char >= $A, Char =< $Z -> Char + 32;
true -> Char
end).
-spec nodeprep(binary()) -> binary() | error.
nodeprep("") -> <<>>;
nodeprep(S) when byte_size(S) < 1024 ->
R = stringprep:nodeprep(S),
if byte_size(R) < 1024 -> R;
true -> error
end;
nodeprep(_) -> error.
-spec nameprep(binary()) -> binary() | error.
nameprep(S) when byte_size(S) < 1024 ->
R = stringprep:nameprep(S),
if byte_size(R) < 1024 -> R;
true -> error
end;
nameprep(_) -> error.
-spec resourceprep(binary()) -> binary() | error.
resourceprep(S) when byte_size(S) < 1024 ->
R = stringprep:resourceprep(S),
if byte_size(R) < 1024 -> R;
true -> error
end;
resourceprep(_) -> error.
-spec tolower(jid() | ljid()) -> error | ljid().
tolower(#jid{luser = U, lserver = S,
lresource = R}) ->
{U, S, R};
tolower({U, S, R}) ->
case nodeprep(U) of
error -> error;
LUser ->
case nameprep(S) of
error -> error;
LServer ->
case resourceprep(R) of
error -> error;
LResource -> {LUser, LServer, LResource}
end
end
end.
-spec remove_resource(jid()) -> jid();
(ljid()) -> ljid().
remove_resource(#jid{} = JID) ->
JID#jid{resource = <<"">>, lresource = <<"">>};
remove_resource({U, S, _R}) -> {U, S, <<"">>}.
-spec replace_resource(jid(), binary()) -> error | jid().
replace_resource(JID, Resource) ->
case resourceprep(Resource) of
error -> error;
LResource ->
JID#jid{resource = Resource, lresource = LResource}
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -1,220 +0,0 @@
%% Created automatically by xdata generator (xdata_codec.erl)
%% Source: mam_query.xdata
%% Form type: urn:xmpp:mam:1
%% Document: XEP-0313
-module(mam_query).
-export([decode/1, decode/2, encode/1, encode/2,
format_error/1]).
-include("xmpp_codec.hrl").
-include("mam_query.hrl").
-export_type([property/0, result/0, form/0]).
enc_jid(J) -> jid:to_string(J).
dec_jid(Val) ->
case jid:from_string(Val) of
error -> erlang:error(badarg);
J -> J
end.
format_error({form_type_mismatch, Type}) ->
<<"FORM_TYPE doesn't match '", Type/binary, "'">>;
format_error({bad_var_value, Var, Type}) ->
<<"Bad value of field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({missing_value, Var, Type}) ->
<<"Missing value of field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({too_many_values, Var, Type}) ->
<<"Too many values for field '", Var/binary,
"' of type '", Type/binary, "'">>;
format_error({unknown_var, Var, Type}) ->
<<"Unknown field '", Var/binary, "' of type '",
Type/binary, "'">>;
format_error({missing_required_var, Var, Type}) ->
<<"Missing required field '", Var/binary, "' of type '",
Type/binary, "'">>.
decode(Fs) -> decode(Fs, []).
decode(Fs, Acc) ->
case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var,
Fs)
of
false -> decode(Fs, Acc, []);
#xdata_field{values = [<<"urn:xmpp:mam:1">>]} ->
decode(Fs, Acc, []);
_ ->
erlang:error({?MODULE,
{form_type_mismatch, <<"urn:xmpp:mam:1">>}})
end.
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
encode(List, Translate) when is_list(List) ->
Fs = [case Opt of
{with, Val} -> [encode_with(Val, Translate)];
{with, _, _} -> erlang:error({badarg, Opt});
{start, Val} -> [encode_start(Val, Translate)];
{start, _, _} -> erlang:error({badarg, Opt});
{'end', Val} -> [encode_end(Val, Translate)];
{'end', _, _} -> erlang:error({badarg, Opt});
{withtext, Val} -> [encode_withtext(Val, Translate)];
{withtext, _, _} -> erlang:error({badarg, Opt});
#xdata_field{} -> [Opt];
_ -> []
end
|| Opt <- List],
FormType = #xdata_field{var = <<"FORM_TYPE">>,
type = hidden, values = [<<"urn:xmpp:mam:1">>]},
[FormType | lists:flatten(Fs)].
decode([#xdata_field{var = <<"with">>, values = [Value]}
| Fs],
Acc, Required) ->
try dec_jid(Value) of
Result ->
decode(Fs, [{with, Result} | Acc],
lists:delete(<<"with">>, Required))
catch
_:_ ->
erlang:error({?MODULE,
{bad_var_value, <<"with">>, <<"urn:xmpp:mam:1">>}})
end;
decode([#xdata_field{var = <<"with">>, values = []} = F
| Fs],
Acc, Required) ->
decode([F#xdata_field{var = <<"with">>, values = [<<>>]}
| Fs],
Acc, Required);
decode([#xdata_field{var = <<"with">>} | _], _, _) ->
erlang:error({?MODULE,
{too_many_values, <<"with">>, <<"urn:xmpp:mam:1">>}});
decode([#xdata_field{var = <<"start">>,
values = [Value]}
| Fs],
Acc, Required) ->
try xmpp_util:decode_timestamp(Value) of
Result ->
decode(Fs, [{start, Result} | Acc],
lists:delete(<<"start">>, Required))
catch
_:_ ->
erlang:error({?MODULE,
{bad_var_value, <<"start">>, <<"urn:xmpp:mam:1">>}})
end;
decode([#xdata_field{var = <<"start">>, values = []} = F
| Fs],
Acc, Required) ->
decode([F#xdata_field{var = <<"start">>,
values = [<<>>]}
| Fs],
Acc, Required);
decode([#xdata_field{var = <<"start">>} | _], _, _) ->
erlang:error({?MODULE,
{too_many_values, <<"start">>, <<"urn:xmpp:mam:1">>}});
decode([#xdata_field{var = <<"end">>, values = [Value]}
| Fs],
Acc, Required) ->
try xmpp_util:decode_timestamp(Value) of
Result ->
decode(Fs, [{'end', Result} | Acc],
lists:delete(<<"end">>, Required))
catch
_:_ ->
erlang:error({?MODULE,
{bad_var_value, <<"end">>, <<"urn:xmpp:mam:1">>}})
end;
decode([#xdata_field{var = <<"end">>, values = []} = F
| Fs],
Acc, Required) ->
decode([F#xdata_field{var = <<"end">>, values = [<<>>]}
| Fs],
Acc, Required);
decode([#xdata_field{var = <<"end">>} | _], _, _) ->
erlang:error({?MODULE,
{too_many_values, <<"end">>, <<"urn:xmpp:mam:1">>}});
decode([#xdata_field{var = <<"withtext">>,
values = [Value]}
| Fs],
Acc, Required) ->
try Value of
Result ->
decode(Fs, [{withtext, Result} | Acc],
lists:delete(<<"withtext">>, Required))
catch
_:_ ->
erlang:error({?MODULE,
{bad_var_value, <<"withtext">>, <<"urn:xmpp:mam:1">>}})
end;
decode([#xdata_field{var = <<"withtext">>,
values = []} =
F
| Fs],
Acc, Required) ->
decode([F#xdata_field{var = <<"withtext">>,
values = [<<>>]}
| Fs],
Acc, Required);
decode([#xdata_field{var = <<"withtext">>} | _], _,
_) ->
erlang:error({?MODULE,
{too_many_values, <<"withtext">>,
<<"urn:xmpp:mam:1">>}});
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
if Var /= <<"FORM_TYPE">> ->
erlang:error({?MODULE,
{unknown_var, Var, <<"urn:xmpp:mam:1">>}});
true -> decode(Fs, Acc, Required)
end;
decode([], _, [Var | _]) ->
erlang:error({?MODULE,
{missing_required_var, Var, <<"urn:xmpp:mam:1">>}});
decode([], Acc, []) -> Acc.
encode_with(Value, Translate) ->
Values = case Value of
undefined -> [];
Value -> [enc_jid(Value)]
end,
Opts = [],
#xdata_field{var = <<"with">>, values = Values,
required = false, type = 'jid-single', options = Opts,
desc = <<>>, label = Translate(<<"User JID">>)}.
encode_start(Value, Translate) ->
Values = case Value of
undefined -> [];
Value -> [Value]
end,
Opts = [],
#xdata_field{var = <<"start">>, values = Values,
required = false, type = 'text-single', options = Opts,
desc = <<>>,
label = Translate(<<"Search from the date">>)}.
encode_end(Value, Translate) ->
Values = case Value of
undefined -> [];
Value -> [Value]
end,
Opts = [],
#xdata_field{var = <<"end">>, values = Values,
required = false, type = 'text-single', options = Opts,
desc = <<>>,
label = Translate(<<"Search until the date">>)}.
encode_withtext(Value, Translate) ->
Values = case Value of
<<>> -> [];
Value -> [Value]
end,
Opts = [],
#xdata_field{var = <<"withtext">>, values = Values,
required = false, type = 'text-single', options = Opts,
desc = <<>>, label = Translate(<<"Search the text">>)}.

View File

@ -31,8 +31,8 @@
-behaviour(gen_mod).
-export([start/2, init/0, stop/1, export/1, import/1,
import/3, announce/3, send_motd/1, disco_identity/5,
-export([start/2, init/0, stop/1, export/1, import_info/0,
import_start/2, import/5, announce/3, send_motd/1, disco_identity/5,
disco_features/5, disco_items/5, depends/2,
send_announcement_to_all/3, announce_commands/4,
announce_items/4, mod_opt_type/1]).
@ -43,7 +43,7 @@
-include("mod_announce.hrl").
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #motd{} | #motd_users{}) -> ok | pass.
-callback import(binary(), binary(), [binary()]) -> ok.
-callback set_motd_users(binary(), [{binary(), binary(), binary()}]) -> {atomic, any()}.
-callback set_motd(binary(), xmlel()) -> {atomic, any()}.
-callback delete_motd(binary()) -> {atomic, any()}.
@ -832,15 +832,17 @@ export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import_info() ->
[{<<"motd">>, 3}].
import(LServer, DBType, LA) ->
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, LA).
Mod:init(LServer, []).
mod_opt_type(access) ->
fun acl:access_rules_validator/1;
import(LServer, {sql, _}, DBType, Tab, List) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, List).
mod_opt_type(access) -> fun acl:access_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [access, db_type].

View File

@ -11,7 +11,7 @@
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
get_motd/1, is_motd_user/2, set_motd_user/2, import/3]).
-include("xmpp.hrl").
-include("mod_announce.hrl").
@ -81,10 +81,11 @@ set_motd_user(LUser, LServer) ->
end,
mnesia:transaction(F).
import(_LServer, #motd{} = Motd) ->
mnesia:dirty_write(Motd);
import(_LServer, #motd_users{} = Users) ->
mnesia:dirty_write(Users).
import(LServer, <<"motd">>, [<<>>, XML, _TimeStamp]) ->
El = fxml_stream:parse_element(XML),
mnesia:dirty_write(#motd{server = LServer, packet = El});
import(LServer, <<"motd">>, [LUser, <<>>, _TimeStamp]) ->
mnesia:dirty_write(#motd_users{us = {LUser, LServer}}).
%%%===================================================================
%%% Internal functions

View File

@ -11,7 +11,7 @@
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
get_motd/1, is_motd_user/2, set_motd_user/2, import/3]).
-include("xmpp.hrl").
-include("mod_announce.hrl").
@ -71,11 +71,13 @@ set_motd_user(LUser, LServer) ->
#motd_users{us = {LUser, LServer}}, motd_users_schema(),
[{'2i', [{<<"server">>, LServer}]}])}.
import(_LServer, #motd{} = Motd) ->
ejabberd_riak:put(Motd, motd_schema());
import(_LServer, #motd_users{us = {_, S}} = Users) ->
import(LServer, <<"motd">>, [<<>>, XML, _TimeStamp]) ->
El = fxml_stream:parse_element(XML),
ejabberd_riak:put(#motd{server = LServer, packet = El}, motd_schema());
import(LServer, <<"motd">>, [LUser, <<>>, _TimeStamp]) ->
Users = #motd_users{us = {LUser, LServer}},
ejabberd_riak:put(Users, motd_users_schema(),
[{'2i', [{<<"server">>, S}]}]).
[{'2i', [{<<"server">>, LServer}]}]).
%%%===================================================================
%%% Internal functions

View File

@ -13,8 +13,8 @@
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/1,
import/2, export/1]).
get_motd/1, is_motd_user/2, set_motd_user/2, import/3,
export/1]).
-include("xmpp.hrl").
-include("mod_announce.hrl").
@ -108,19 +108,8 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select xml from motd where username='';">>,
fun([XML]) ->
El = fxml_stream:parse_element(XML),
#motd{server = LServer, packet = El}
end},
{<<"select username from motd where xml='';">>,
fun([LUser]) ->
#motd_users{us = {LUser, LServer}}
end}].
import(_, _) ->
pass.
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions

296
src/mod_bosh.erl Normal file
View File

@ -0,0 +1,296 @@
%%%-------------------------------------------------------------------
%%% File : mod_bosh.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Purpose : This module acts as a bridge to ejabberd_bosh which implements
%%% the real stuff, this is to handle the new pluggable architecture
%%% for extending ejabberd's http service.
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-module(mod_bosh).
-author('steve@zeank.in-berlin.de').
%%-define(ejabberd_debug, true).
-behaviour(gen_server).
-behaviour(gen_mod).
-export([start_link/0]).
-export([start/2, stop/1, process/2, open_session/2,
close_session/1, find_session/1]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
depends/2, mod_opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include("jlib.hrl").
-include("ejabberd_http.hrl").
-include("bosh.hrl").
-record(bosh, {sid = <<"">> :: binary() | '_',
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_',
pid = self() :: pid() | '$1'}).
-record(state, {}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
process([], #request{method = 'POST', data = <<>>}) ->
?DEBUG("Bad Request: no data", []),
{400, ?HEADER(?CT_XML),
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"400 Bad Request">>}]}};
process([],
#request{method = 'POST', data = Data, ip = IP, headers = Hdrs}) ->
?DEBUG("Incoming data: ~p", [Data]),
Type = get_type(Hdrs),
ejabberd_bosh:process_request(Data, IP, Type);
process([], #request{method = 'GET', data = <<>>}) ->
{200, ?HEADER(?CT_XML), get_human_html_xmlel()};
process([], #request{method = 'OPTIONS', data = <<>>}) ->
{200, ?OPTIONS_HEADER, []};
process(_Path, _Request) ->
?DEBUG("Bad Request: ~p", [_Request]),
{400, ?HEADER(?CT_XML),
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"400 Bad Request">>}]}}.
get_human_html_xmlel() ->
Heading = <<"ejabberd ", (jlib:atom_to_binary(?MODULE))/binary>>,
#xmlel{name = <<"html">>,
attrs =
[{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}],
children =
[#xmlel{name = <<"head">>, attrs = [],
children =
[#xmlel{name = <<"title">>, attrs = [],
children = [{xmlcdata, Heading}]}]},
#xmlel{name = <<"body">>, attrs = [],
children =
[#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, Heading}]},
#xmlel{name = <<"p">>, attrs = [],
children =
[{xmlcdata, <<"An implementation of ">>},
#xmlel{name = <<"a">>,
attrs =
[{<<"href">>,
<<"http://xmpp.org/extensions/xep-0206.html">>}],
children =
[{xmlcdata,
<<"XMPP over BOSH (XEP-0206)">>}]}]},
#xmlel{name = <<"p">>, attrs = [],
children =
[{xmlcdata,
<<"This web page is only informative. To "
"use HTTP-Bind you need a Jabber/XMPP "
"client that supports it.">>}]}]}]}.
open_session(SID, Pid) ->
Session = #bosh{sid = SID, timestamp = p1_time_compat:timestamp(), pid = Pid},
lists:foreach(
fun(Node) when Node == node() ->
gen_server:call(?MODULE, {write, Session});
(Node) ->
cluster_send({?MODULE, Node}, {write, Session})
end, ejabberd_cluster:get_nodes()).
close_session(SID) ->
case mnesia:dirty_read(bosh, SID) of
[Session] ->
lists:foreach(
fun(Node) when Node == node() ->
gen_server:call(?MODULE, {delete, Session});
(Node) ->
cluster_send({?MODULE, Node}, {delete, Session})
end, ejabberd_cluster:get_nodes());
[] ->
ok
end.
write_session(#bosh{pid = Pid1, sid = SID, timestamp = T1} = S1) ->
case mnesia:dirty_read(bosh, SID) of
[#bosh{pid = Pid2, timestamp = T2} = S2] ->
if Pid1 == Pid2 ->
mnesia:dirty_write(S1);
T1 < T2 ->
cluster_send(Pid2, replaced),
mnesia:dirty_write(S1);
true ->
cluster_send(Pid1, replaced),
mnesia:dirty_write(S2)
end;
[] ->
mnesia:dirty_write(S1)
end.
delete_session(#bosh{sid = SID, pid = Pid1}) ->
case mnesia:dirty_read(bosh, SID) of
[#bosh{pid = Pid2}] ->
if Pid1 == Pid2 ->
mnesia:dirty_delete(bosh, SID);
true ->
ok
end;
[] ->
ok
end.
find_session(SID) ->
case mnesia:dirty_read(bosh, SID) of
[#bosh{pid = Pid}] ->
{ok, Pid};
[] ->
error
end.
start(Host, Opts) ->
setup_database(),
start_jiffy(Opts),
TmpSup = gen_mod:get_module_proc(Host, ?PROCNAME),
TmpSupSpec = {TmpSup,
{ejabberd_tmp_sup, start_link, [TmpSup, ejabberd_bosh]},
permanent, infinity, supervisor, [ejabberd_tmp_sup]},
ProcSpec = {?MODULE,
{?MODULE, start_link, []},
transient, 2000, worker, [?MODULE]},
case supervisor:start_child(ejabberd_sup, ProcSpec) of
{ok, _} ->
supervisor:start_child(ejabberd_sup, TmpSupSpec);
{error, {already_started, _}} ->
supervisor:start_child(ejabberd_sup, TmpSupSpec);
Err ->
Err
end.
stop(Host) ->
TmpSup = gen_mod:get_module_proc(Host, ?PROCNAME),
supervisor:terminate_child(ejabberd_sup, TmpSup),
supervisor:delete_child(ejabberd_sup, TmpSup).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
{ok, #state{}}.
handle_call({write, Session}, _From, State) ->
Res = write_session(Session),
{reply, Res, State};
handle_call({delete, Session}, _From, State) ->
Res = delete_session(Session),
{reply, Res, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({write, Session}, State) ->
write_session(Session),
{noreply, State};
handle_info({delete, Session}, State) ->
delete_session(Session),
{noreply, State};
handle_info(_Info, State) ->
?ERROR_MSG("got unexpected info: ~p", [_Info]),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
setup_database() ->
case catch mnesia:table_info(bosh, attributes) of
[sid, pid] ->
mnesia:delete_table(bosh);
_ ->
ok
end,
mnesia:create_table(bosh,
[{ram_copies, [node()]}, {local_content, true},
{attributes, record_info(fields, bosh)}]),
mnesia:add_table_copy(bosh, node(), ram_copies).
start_jiffy(Opts) ->
case gen_mod:get_opt(json, Opts,
fun(false) -> false;
(true) -> true
end, false) of
false ->
ok;
true ->
case catch ejabberd:start_app(jiffy) of
ok ->
ok;
Err ->
?WARNING_MSG("Failed to start JSON codec (jiffy): ~p. "
"JSON support will be disabled", [Err])
end
end.
get_type(Hdrs) ->
try
{_, S} = lists:keyfind('Content-Type', 1, Hdrs),
[T|_] = str:tokens(S, <<";">>),
[_, <<"json">>] = str:tokens(T, <<"/">>),
json
catch _:_ ->
xml
end.
cluster_send(NodePid, Msg) ->
erlang:send(NodePid, Msg, [noconnect, nosuspend]).
depends(_Host, _Opts) ->
[].
mod_opt_type(json) ->
fun (false) -> false;
(true) -> true
end;
mod_opt_type(max_concat) ->
fun (unlimited) -> unlimited;
(N) when is_integer(N), N > 0 -> N
end;
mod_opt_type(max_inactivity) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(max_pause) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(prebind) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[json, max_concat, max_inactivity, max_pause, prebind].

View File

@ -55,20 +55,16 @@
-include("logger.hrl").
-include("xmpp.hrl").
-include("mod_caps.hrl").
-define(PROCNAME, ejabberd_mod_caps).
-define(BAD_HASH_LIFETIME, 600).
-record(caps_features,
{
node_pair = {<<"">>, <<"">>} :: {binary(), binary()},
features = [] :: [binary()] | pos_integer()
}).
-record(state, {host = <<"">> :: binary()}).
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), {binary(), binary()}, [binary() | pos_integer()]) -> ok.
-callback caps_read(binary(), {binary(), binary()}) ->
{ok, non_neg_integer() | [binary()]} | error.
-callback caps_write(binary(), {binary(), binary()},
@ -525,9 +521,6 @@ is_valid_node(Node) ->
false
end.
caps_features_schema() ->
{record_info(fields, caps_features), #caps_features{}}.
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
@ -559,24 +552,8 @@ import_next(_LServer, _DBType, '$end_of_table') ->
ok;
import_next(LServer, DBType, NodePair) ->
Features = [F || {_, F} <- ets:lookup(caps_features_tmp, NodePair)],
case Features of
[I] when is_integer(I), DBType == mnesia ->
mnesia:dirty_write(
#caps_features{node_pair = NodePair, features = I});
[I] when is_integer(I), DBType == riak ->
ejabberd_riak:put(
#caps_features{node_pair = NodePair, features = I},
caps_features_schema());
_ when DBType == mnesia ->
mnesia:dirty_write(
#caps_features{node_pair = NodePair, features = Features});
_ when DBType == riak ->
ejabberd_riak:put(
#caps_features{node_pair = NodePair, features = Features},
caps_features_schema());
_ when DBType == sql ->
ok
end,
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, NodePair, Features),
import_next(LServer, DBType, ets:next(caps_features_tmp, NodePair)).
mod_opt_type(cache_life_time) ->

View File

@ -10,7 +10,7 @@
-behaviour(mod_caps).
%% API
-export([init/2, caps_read/2, caps_write/3]).
-export([init/2, caps_read/2, caps_write/3, import/3]).
-include("mod_caps.hrl").
-include("logger.hrl").
@ -46,6 +46,13 @@ caps_write(_LServer, Node, Features) ->
mnesia:dirty_write(#caps_features{node_pair = Node,
features = Features}).
import(_LServer, NodePair, [I]) when is_integer(I) ->
mnesia:dirty_write(
#caps_features{node_pair = NodePair, features = I});
import(_LServer, NodePair, Features) ->
mnesia:dirty_write(
#caps_features{node_pair = NodePair, features = Features}).
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -10,7 +10,7 @@
-behaviour(mod_caps).
%% API
-export([init/2, caps_read/2, caps_write/3]).
-export([init/2, caps_read/2, caps_write/3, import/3]).
-include("mod_caps.hrl").
@ -31,6 +31,15 @@ caps_write(_LServer, Node, Features) ->
features = Features},
caps_features_schema()).
import(_LServer, NodePair, [I]) when is_integer(I) ->
ejabberd_riak:put(
#caps_features{node_pair = NodePair, features = I},
caps_features_schema());
import(_LServer, NodePair, Features) ->
ejabberd_riak:put(
#caps_features{node_pair = NodePair, features = Features},
caps_features_schema()).
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -12,7 +12,7 @@
-compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, caps_read/2, caps_write/3, export/1]).
-export([init/2, caps_read/2, caps_write/3, export/1, import/3]).
-include("mod_caps.hrl").
-include("ejabberd_sql_pt.hrl").
@ -53,6 +53,9 @@ export(_Server) ->
[]
end}].
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -344,7 +344,9 @@ disco_identity(Acc, _From, To, <<"">>, _Lang, Type) ->
empty when Identities /= [] -> {result, Identities};
{result, Ids} -> {result, Ids ++ Identities};
Acc -> Acc
end.
end;
disco_identity(Acc, _From, _To, _Node, _Lang, _Type) ->
Acc.
my_features(ejabberd_local) -> [?NS_DELEGATION];
my_features(ejabberd_sm) -> [].

View File

@ -360,7 +360,8 @@ handle_cast(Request, State) ->
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
handle_info({route, From, To, #iq{} = IQ}, State) ->
handle_info({route, From, To, #iq{} = Packet}, State) ->
IQ = xmpp:decode_els(Packet),
{Reply, NewState} = case process_iq(From, IQ, State) of
R when is_record(R, iq) ->
{R, State};

View File

@ -34,8 +34,8 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/1, export/1,
process_sm_iq/1, on_presence_update/4, import/1,
import/3, store_last_info/4, get_last_info/2,
process_sm_iq/1, on_presence_update/4, import_info/0,
import/5, import_start/2, store_last_info/4, get_last_info/2,
remove_user/2, transform_options/1, mod_opt_type/1,
opt_type/1, register_user/2, depends/2]).
@ -207,18 +207,28 @@ remove_user(User, Server) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_user(LUser, LServer).
import_info() ->
[{<<"last">>, 3}].
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []).
import(LServer, {sql, _}, DBType, <<"last">>, [LUser, TimeStamp, State]) ->
TS = case TimeStamp of
<<"">> -> 0;
_ -> binary_to_integer(TimeStamp)
end,
LA = #last_activity{us = {LUser, LServer},
timestamp = TS,
status = State},
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, LA).
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import(LServer, DBType, LA) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, LA).
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).

View File

@ -13,7 +13,7 @@
%% API
-export([init/2, get_last/2, store_last_info/4, remove_user/2,
import/1, import/2, export/1]).
import/2, export/1]).
-include("mod_last.hrl").
-include("logger.hrl").
@ -43,9 +43,6 @@ store_last_info(LUser, LServer, TimeStamp, Status) ->
remove_user(LUser, LServer) ->
sql_queries:del_last(LServer, LUser).
import(_LServer, _LA) ->
pass.
export(_Server) ->
[{last_activity,
fun(Host, #last_activity{us = {LUser, LServer},
@ -58,15 +55,5 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select username, seconds, state from last">>,
fun([LUser, TimeStamp, State]) ->
#last_activity{us = {LUser, LServer},
timestamp = binary_to_integer(
TimeStamp),
status = State}
end}].
%%%===================================================================
%%% Internal functions
%%%===================================================================
import(_LServer, _LA) ->
pass.

View File

@ -51,8 +51,9 @@
process_mucsub/1,
broadcast_service_message/2,
export/1,
import/1,
import/3,
import_info/0,
import/5,
import_start/2,
opts_to_binary/1,
can_use_nick/4]).
@ -62,7 +63,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
-include("mod_muc.hrl").
@ -79,7 +80,7 @@
-type muc_room_opts() :: [{atom(), any()}].
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #muc_room{} | #muc_registered{}) -> ok | pass.
-callback import(binary(), binary(), [binary()]) -> ok.
-callback store_room(binary(), binary(), binary(), list()) -> {atomic, any()}.
-callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error.
-callback forget_room(binary(), binary(), binary()) -> {atomic, any()}.
@ -175,8 +176,10 @@ init([Host, Opts]) ->
<<"conference.@HOST@">>),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, [{host, MyHost}|Opts]),
update_tables(),
mnesia:create_table(muc_online_room,
[{ram_copies, [node()]},
{type, ordered_set},
{attributes, record_info(fields, muc_online_room)}]),
mnesia:add_table_copy(muc_online_room, node(), ram_copies),
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
@ -497,8 +500,12 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
ServerHost, ?MODULE, max_rooms_discoitems,
fun(I) when is_integer(I), I>=0 -> I end,
100),
Items = iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM),
xmpp:make_iq_result(IQ, #disco_items{node = Node, items = Items});
case iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM) of
{error, Err} ->
xmpp:make_error(IQ, Err);
{result, Result} ->
xmpp:make_iq_result(IQ, Result)
end;
process_disco_items(#iq{lang = Lang} = IQ) ->
Txt = <<"No module is handling this query">>,
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
@ -597,76 +604,112 @@ register_room(Host, Room, Pid) ->
end,
mnesia:transaction(F).
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"">>, undefined) ->
Rooms = get_vh_rooms(Host),
case erlang:length(Rooms) < MaxRoomsDiscoItems of
true ->
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang});
false ->
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined)
end;
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined) ->
Empty = #disco_item{jid = jid:make(<<"conference.localhost">>),
node = <<"emptyrooms">>,
name = translate:translate(Lang, <<"Empty Rooms">>)},
Query = {get_disco_item, only_non_empty, From, Lang},
[Empty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"emptyrooms">>, undefined) ->
iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang});
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, _DiscoNode, Rsm) ->
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
RsmOut = jlib:rsm_encode(RsmO),
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut.
-spec iq_disco_items(binary(), jid(), binary(), integer(), binary(),
rsm_set() | undefined) ->
{result, disco_items()} | {error, stanza_error()}.
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM)
when Node == <<"">>; Node == <<"nonemptyrooms">>; Node == <<"emptyrooms">> ->
Count = get_vh_rooms_count(Host),
Query = if Node == <<"">>, RSM == undefined, Count > MaxRoomsDiscoItems ->
{get_disco_item, only_non_empty, From, Lang};
Node == <<"nonemptyrooms">> ->
{get_disco_item, only_non_empty, From, Lang};
Node == <<"emptyrooms">> ->
{get_disco_item, 0, From, Lang};
true ->
{get_disco_item, all, From, Lang}
end,
Items = get_vh_rooms(Host, Query, RSM),
ResRSM = case Items of
[_|_] when RSM /= undefined ->
#disco_item{jid = #jid{luser = First}} = hd(Items),
#disco_item{jid = #jid{luser = Last}} = lists:last(Items),
#rsm_set{first = #rsm_first{data = First},
last = Last,
count = Count};
[] when RSM /= undefined ->
#rsm_set{count = Count};
_ ->
undefined
end,
{result, #disco_items{node = Node, items = Items, rsm = ResRSM}};
iq_disco_items(_Host, _From, Lang, _MaxRoomsDiscoItems, _Node, _RSM) ->
{error, xmpp:err_item_not_found(<<"Node not found">>, Lang)}.
iq_disco_items_list(Host, Rooms, Query) ->
lists:zf(
fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
case catch gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
{item, Desc} ->
flush(),
{true, #disco_item{jid = jid:make(Name, Host),
name = Desc}};
_ ->
false
end
end, Rooms).
-spec get_vh_rooms(binary, term(), rsm_set() | undefined) -> [disco_item()].
get_vh_rooms(Host, Query,
#rsm_set{max = Max, 'after' = After, before = undefined})
when is_binary(After), After /= <<"">> ->
lists:reverse(get_vh_rooms(next, {After, Host}, Host, Query, 0, Max, []));
get_vh_rooms(Host, Query,
#rsm_set{max = Max, 'after' = undefined, before = Before})
when is_binary(Before), Before /= <<"">> ->
get_vh_rooms(prev, {Before, Host}, Host, Query, 0, Max, []);
get_vh_rooms(Host, Query,
#rsm_set{max = Max, 'after' = undefined, before = <<"">>}) ->
get_vh_rooms(last, {<<"">>, Host}, Host, Query, 0, Max, []);
get_vh_rooms(Host, Query, #rsm_set{max = Max}) ->
lists:reverse(get_vh_rooms(first, {<<"">>, Host}, Host, Query, 0, Max, []));
get_vh_rooms(Host, Query, undefined) ->
lists:reverse(get_vh_rooms(first, {<<"">>, Host}, Host, Query, 0, undefined, [])).
get_vh_rooms(_, _) ->
todo.
%% AllRooms = lists:sort(get_vh_rooms(Host)),
%% Count = erlang:length(AllRooms),
%% Guard = case Direction of
%% _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
%% aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
%% before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
%% _ -> [{'==', {element, 2, '$1'}, Host}]
%% end,
%% L = lists:sort(
%% mnesia:dirty_select(muc_online_room,
%% [{#muc_online_room{name_host = '$1', _ = '_'},
%% Guard,
%% ['$_']}])),
%% L2 = if
%% Index == undefined andalso Direction == before ->
%% lists:reverse(lists:sublist(lists:reverse(L), 1, M));
%% Index == undefined ->
%% lists:sublist(L, 1, M);
%% Index > Count orelse Index < 0 ->
%% [];
%% true ->
%% lists:sublist(L, Index+1, M)
%% end,
%% if L2 == [] -> {L2, #rsm_out{count = Count}};
%% true ->
%% H = hd(L2),
%% NewIndex = get_room_pos(H, AllRooms),
%% T = lists:last(L2),
%% {F, _} = H#muc_online_room.name_host,
%% {Last, _} = T#muc_online_room.name_host,
%% {L2,
%% #rsm_out{first = F, last = Last, count = Count,
%% index = NewIndex}}
%% end.
-spec get_vh_rooms(prev | next | last | first,
{binary(), binary()}, binary(), term(),
non_neg_integer(), non_neg_integer() | undefined,
[disco_item()]) -> [disco_item()].
get_vh_rooms(_Action, _Key, _Host, _Query, Count, Max, Items) when Count >= Max ->
Items;
get_vh_rooms(Action, Key, Host, Query, Count, Max, Items) ->
Call = fun() ->
case Action of
prev -> mnesia:dirty_prev(muc_online_room, Key);
next -> mnesia:dirty_next(muc_online_room, Key);
last -> mnesia:dirty_last(muc_online_room);
first -> mnesia:dirty_first(muc_online_room)
end
end,
NewAction = case Action of
last -> prev;
first -> next;
_ -> Action
end,
try Call() of
'$end_of_table' ->
Items;
{_, Host} = NewKey ->
case get_room_disco_item(NewKey, Query) of
{ok, Item} ->
get_vh_rooms(NewAction, NewKey, Host, Query,
Count + 1, Max, [Item|Items]);
{error, _} ->
get_vh_rooms(NewAction, NewKey, Host, Query,
Count, Max, Items)
end;
NewKey ->
get_vh_rooms(NewAction, NewKey, Host, Query, Count, Max, Items)
catch _:{aborted, {badarg, _}} ->
Items
end.
-spec get_room_disco_item({binary(), binary()}, term()) -> {ok, disco_item()} |
{error, timeout | notfound}.
get_room_disco_item({Name, Host}, Query) ->
case mnesia:dirty_read(muc_online_room, {Name, Host}) of
[#muc_online_room{pid = Pid}|_] ->
RoomJID = jid:make(Name, Host),
try gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
{item, Desc} ->
{ok, #disco_item{jid = RoomJID, name = Desc}};
false ->
{error, notfound}
catch _:{timeout, _} ->
{error, timeout};
_:{noproc, _} ->
{error, notfound}
end;
_ ->
{error, notfound}
end.
get_subscribed_rooms(_ServerHost, Host, From) ->
Rooms = get_vh_rooms(Host),
@ -681,21 +724,6 @@ get_subscribed_rooms(_ServerHost, Host, From) ->
[]
end, Rooms).
%% @doc Return the position of desired room in the list of rooms.
%% The room must exist in the list. The count starts in 0.
%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
get_room_pos(Desired, Rooms) ->
get_room_pos(Desired, Rooms, 0).
get_room_pos(Desired, [HeadRoom | _], HeadPosition)
when Desired#muc_online_room.name_host ==
HeadRoom#muc_online_room.name_host ->
HeadPosition;
get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
get_room_pos(Desired, Rooms, HeadPosition + 1).
flush() -> receive _ -> flush() after 0 -> ok end.
get_nick(ServerHost, Host, From) ->
LServer = jid:nameprep(ServerHost),
Mod = gen_mod:db_mod(LServer, ?MODULE),
@ -782,6 +810,13 @@ get_vh_rooms(Host) ->
[{'==', {element, 2, '$1'}, Host}],
['$_']}]).
-spec get_vh_rooms_count(binary()) -> non_neg_integer().
get_vh_rooms_count(Host) ->
ets:select_count(muc_online_room,
ets:fun2ms(
fun(#muc_online_room{name_host = {_, H}}) ->
H == Host
end)).
clean_table_from_bad_node(Node) ->
F = fun() ->
@ -811,6 +846,23 @@ clean_table_from_bad_node(Node, Host) ->
end,
mnesia:async_dirty(F).
update_tables() ->
try
case mnesia:table_info(muc_online_room, type) of
ordered_set -> ok;
_ ->
case mnesia:delete_table(muc_online_room) of
{atomic, ok} -> ok;
Err -> erlang:error(Err)
end
end
catch _:{aborted, {no_exists, muc_online_room}} -> ok;
_:{aborted, {no_exists, muc_online_room, type}} -> ok;
E:R ->
?ERROR_MSG("failed to update mnesia table '~s': ~p",
[muc_online_room, {E, R}])
end.
opts_to_binary(Opts) ->
lists:map(
fun({title, Title}) ->
@ -853,13 +905,16 @@ export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import_info() ->
[{<<"muc_room">>, 4}, {<<"muc_registered">>, 4}].
import(LServer, DBType, Data) ->
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Data).
Mod:init(LServer, []).
import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, L).
mod_opt_type(access) ->
fun acl:access_rules_validator/1;

View File

@ -9,11 +9,15 @@
-module(mod_muc_mnesia).
-behaviour(mod_muc).
-behaviour(mod_muc_room).
%% API
-export([init/2, import/2, store_room/4, restore_room/3, forget_room/3,
-export([init/2, import/3, store_room/4, restore_room/3, forget_room/3,
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
-export([set_affiliation/6, set_affiliations/4, get_affiliation/5,
get_affiliations/3, search_affiliation/4]).
-include("jlib.hrl").
-include("mod_muc.hrl").
-include("logger.hrl").
@ -113,10 +117,33 @@ set_nick(_LServer, Host, From, Nick) ->
end,
mnesia:transaction(F).
import(_LServer, #muc_room{} = R) ->
mnesia:dirty_write(R);
import(_LServer, #muc_registered{} = R) ->
mnesia:dirty_write(R).
set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) ->
{error, not_implemented}.
set_affiliations(_ServerHost, _Room, _Host, _Affiliations) ->
{error, not_implemented}.
get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) ->
{error, not_implemented}.
get_affiliations(_ServerHost, _Room, _Host) ->
{error, not_implemented}.
search_affiliation(_ServerHost, _Room, _Host, _Affiliation) ->
{error, not_implemented}.
import(_LServer, <<"muc_room">>,
[Name, RoomHost, SOpts, _TimeStamp]) ->
Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)),
mnesia:dirty_write(
#muc_room{name_host = {Name, RoomHost},
opts = Opts});
import(_LServer, <<"muc_registered">>,
[J, RoomHost, Nick, _TimeStamp]) ->
#jid{user = U, server = S} = jid:from_string(J),
mnesia:dirty_write(
#muc_registered{us_host = {{U, S}, RoomHost},
nick = Nick}).
%%%===================================================================
%%% Internal functions

View File

@ -9,11 +9,15 @@
-module(mod_muc_riak).
-behaviour(mod_muc).
-behaviour(mod_muc_room).
%% API
-export([init/2, import/2, store_room/4, restore_room/3, forget_room/3,
-export([init/2, import/3, store_room/4, restore_room/3, forget_room/3,
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
-export([set_affiliation/6, set_affiliations/4, get_affiliation/5,
get_affiliations/3, search_affiliation/4]).
-include("jlib.hrl").
-include("mod_muc.hrl").
%%%===================================================================
@ -101,11 +105,33 @@ set_nick(LServer, Host, From, Nick) ->
end
end}.
import(_LServer, #muc_room{} = R) ->
ejabberd_riak:put(R, muc_room_schema());
import(_LServer, #muc_registered{us_host = {_, Host}, nick = Nick} = R) ->
set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) ->
{error, not_implemented}.
set_affiliations(_ServerHost, _Room, _Host, _Affiliations) ->
{error, not_implemented}.
get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) ->
{error, not_implemented}.
get_affiliations(_ServerHost, _Room, _Host) ->
{error, not_implemented}.
search_affiliation(_ServerHost, _Room, _Host, _Affiliation) ->
{error, not_implemented}.
import(_LServer, <<"muc_room">>,
[Name, RoomHost, SOpts, _TimeStamp]) ->
Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)),
ejabberd_riak:put(
#muc_room{name_host = {Name, RoomHost}, opts = Opts},
muc_room_schema());
import(_LServer, <<"muc_registered">>,
[J, RoomHost, Nick, _TimeStamp]) ->
#jid{user = U, server = S} = jid:from_string(J),
R = #muc_registered{us_host = {{U, S}, RoomHost}, nick = Nick},
ejabberd_riak:put(R, muc_registered_schema(),
[{'2i', [{<<"nick_host">>, {Nick, Host}}]}]).
[{'2i', [{<<"nick_host">>, {Nick, RoomHost}}]}]).
%%%===================================================================
%%% Internal functions

View File

@ -504,8 +504,7 @@ handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
Len = ?DICT:fold(fun(_, _, Acc) -> Acc + 1 end, 0,
StateData#state.users),
Len = ?DICT:size(StateData#state.users),
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
true ->
get_roomdesc_reply(JID, StateData,

View File

@ -11,11 +11,14 @@
-compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_muc).
-behaviour(mod_muc_room).
%% API
-export([init/2, store_room/4, restore_room/3, forget_room/3,
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4,
import/1, import/2, export/1]).
import/3, export/1]).
-export([set_affiliation/6, set_affiliations/4, get_affiliation/5,
get_affiliations/3, search_affiliation/4]).
-include("jid.hrl").
-include("mod_muc.hrl").
@ -127,6 +130,21 @@ set_nick(LServer, Host, From, Nick) ->
end,
ejabberd_sql:sql_transaction(LServer, F).
set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) ->
{error, not_implemented}.
set_affiliations(_ServerHost, _Room, _Host, _Affiliations) ->
{error, not_implemented}.
get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) ->
{error, not_implemented}.
get_affiliations(_ServerHost, _Room, _Host) ->
{error, not_implemented}.
search_affiliation(_ServerHost, _Room, _Host, _Affiliation) ->
{error, not_implemented}.
export(_Server) ->
[{muc_room,
fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) ->
@ -158,21 +176,8 @@ export(_Server) ->
end
end}].
import(_LServer) ->
[{<<"select name, host, opts from muc_room;">>,
fun([Name, RoomHost, SOpts]) ->
Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)),
#muc_room{name_host = {Name, RoomHost}, opts = Opts}
end},
{<<"select jid, host, nick from muc_registered;">>,
fun([J, RoomHost, Nick]) ->
#jid{user = U, server = S} = jid:from_string(J),
#muc_registered{us_host = {{U, S}, RoomHost},
nick = Nick}
end}].
import(_, _) ->
pass.
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -53,8 +53,9 @@
remove_expired_messages/1,
remove_old_messages/2,
remove_user/2,
import/1,
import/3,
import_info/0,
import_start/2,
import/5,
export/1,
get_queue_length/2,
count_offline_messages/2,
@ -90,7 +91,7 @@
-type us() :: {binary(), binary()}.
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #offline_msg{}) -> ok | pass.
-callback import(#offline_msg{}) -> ok.
-callback store_messages(binary(), us(), [#offline_msg{}],
non_neg_integer(), non_neg_integer()) ->
{atomic, any()}.
@ -851,13 +852,35 @@ export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import_info() ->
[{<<"spool">>, 4}].
import(LServer, DBType, Data) ->
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Data).
Mod:import(LServer, []).
import(LServer, {sql, _}, DBType, <<"spool">>,
[LUser, XML, _Seq, _TimeStamp]) ->
El = fxml_stream:parse_element(XML),
From = #jid{} = jid:from_string(
fxml:get_attr_s(<<"from">>, El#xmlel.attrs)),
To = #jid{} = jid:from_string(
fxml:get_attr_s(<<"to">>, El#xmlel.attrs)),
Stamp = fxml:get_path_s(El, [{elem, <<"delay">>},
{attr, <<"stamp">>}]),
TS = try xmpp_util:decode_timestamp(Stamp) of
{MegaSecs, Secs, _} ->
{MegaSecs, Secs, 0}
catch _:_ ->
p1_time_compat:timestamp()
end,
US = {LUser, LServer},
Expire = find_x_expire(TS, El#xmlel.children),
Msg = #offline_msg{us = US, packet = El,
from = From, to = To,
timestamp = TS, expire = Expire},
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(Msg).
mod_opt_type(access_max_user_messages) ->
fun acl:shaper_rules_validator/1;

View File

@ -13,7 +13,7 @@
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
remove_old_messages/2, remove_user/2, read_message_headers/2,
read_message/3, remove_message/3, read_all_messages/2,
remove_all_messages/2, count_messages/2, import/2]).
remove_all_messages/2, count_messages/2, import/1]).
-include("xmpp.hrl").
-include("mod_offline.hrl").
@ -164,7 +164,7 @@ count_messages(LUser, LServer) ->
_ -> 0
end.
import(_LServer, #offline_msg{} = Msg) ->
import(#offline_msg{} = Msg) ->
mnesia:dirty_write(Msg).
%%%===================================================================

View File

@ -13,7 +13,7 @@
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
remove_old_messages/2, remove_user/2, read_message_headers/2,
read_message/3, remove_message/3, read_all_messages/2,
remove_all_messages/2, count_messages/2, import/2]).
remove_all_messages/2, count_messages/2, import/1]).
-include("xmpp.hrl").
-include("mod_offline.hrl").
@ -133,7 +133,7 @@ count_messages(LUser, LServer) ->
0
end.
import(_LServer, #offline_msg{us = US, timestamp = TS} = M) ->
import(#offline_msg{us = US, timestamp = TS} = M) ->
ejabberd_riak:put(M, offline_msg_schema(),
[{i, TS}, {'2i', [{<<"us">>, US}]}]).

View File

@ -15,8 +15,7 @@
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
remove_old_messages/2, remove_user/2, read_message_headers/2,
read_message/3, remove_message/3, read_all_messages/2,
remove_all_messages/2, count_messages/2, import/1, import/2,
export/1]).
remove_all_messages/2, count_messages/2, import/1, export/1]).
-include("xmpp.hrl").
-include("mod_offline.hrl").
@ -193,29 +192,8 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select username, xml from spool;">>,
fun([LUser, XML]) ->
El = #xmlel{} = fxml_stream:parse_element(XML),
#message{} = Pkt = xmpp:decode(El, ?NS_CLIENT, [ignore_els]),
From = Pkt#message.from,
To = case Pkt#message.to of
undefined -> jid:make(LUser, LServer);
JID -> JID
end,
TS = case xmpp:get_subtag(Pkt, #delay{}) of
#delay{stamp = Stamp} -> Stamp;
false -> p1_time_compat:timestamp()
end,
Expire = mod_offline:find_x_expire(TS, Pkt),
#offline_msg{us = {LUser, LServer},
from = From, to = To,
packet = El,
timestamp = TS, expire = Expire}
end}].
import(_, _) ->
pass.
import(_) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -31,11 +31,12 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_iq/1, export/1, import/1,
-export([start/2, stop/1, process_iq/1, export/1, import_info/0,
process_iq_set/3, process_iq_get/3, get_user_list/3,
check_packet/6, remove_user/2, encode_list_item/1,
is_list_needdb/1, updated_list/3,
item_to_xml/1, get_user_lists/2, import/3,
import_start/2, import_stop/2,
item_to_xml/1, get_user_lists/2, import/5,
set_privacy_list/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
@ -46,7 +47,7 @@
-include("mod_privacy.hrl").
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #privacy{}) -> ok | pass.
-callback import(#privacy{}) -> ok.
-callback process_lists_get(binary(), binary()) -> {none | binary(), [binary()]} | error.
-callback process_list_get(binary(), binary(), binary()) -> [listitem()] | error | not_found.
-callback process_default_set(binary(), binary(), binary() | none) -> {atomic, any()}.
@ -544,18 +545,108 @@ updated_list(_, #userlist{name = OldName} = Old,
true -> Old
end.
numeric_to_binary(<<0, 0, _/binary>>) ->
<<"0">>;
numeric_to_binary(<<0, _, _:6/binary, T/binary>>) ->
Res = lists:foldl(
fun(X, Sum) ->
Sum*10000 + X
end, 0, [X || <<X:16>> <= T]),
integer_to_binary(Res).
bool_to_binary(<<0>>) -> <<"0">>;
bool_to_binary(<<1>>) -> <<"1">>.
prepare_list_data(mysql, [ID|Row]) ->
[binary_to_integer(ID)|Row];
prepare_list_data(pgsql, [<<ID:64>>,
SType, SValue, SAction, SOrder, SMatchAll,
SMatchIQ, SMatchMessage, SMatchPresenceIn,
SMatchPresenceOut]) ->
[ID, SType, SValue, SAction,
numeric_to_binary(SOrder),
bool_to_binary(SMatchAll),
bool_to_binary(SMatchIQ),
bool_to_binary(SMatchMessage),
bool_to_binary(SMatchPresenceIn),
bool_to_binary(SMatchPresenceOut)].
prepare_id(mysql, ID) ->
binary_to_integer(ID);
prepare_id(pgsql, <<ID:32>>) ->
ID.
import_info() ->
[{<<"privacy_default_list">>, 2},
{<<"privacy_list_data">>, 10},
{<<"privacy_list">>, 4}].
import_start(LServer, DBType) ->
ets:new(privacy_default_list_tmp, [private, named_table]),
ets:new(privacy_list_data_tmp, [private, named_table, bag]),
ets:new(privacy_list_tmp, [private, named_table, bag,
{keypos, #privacy.us}]),
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []).
import(LServer, {sql, _}, _DBType, <<"privacy_default_list">>, [LUser, Name]) ->
US = {LUser, LServer},
ets:insert(privacy_default_list_tmp, {US, Name}),
ok;
import(LServer, {sql, SQLType}, _DBType, <<"privacy_list_data">>, Row1) ->
[ID|Row] = prepare_list_data(SQLType, Row1),
case mod_privacy_sql:raw_to_item(Row) of
[Item] ->
IS = {ID, LServer},
ets:insert(privacy_list_data_tmp, {IS, Item}),
ok;
[] ->
ok
end;
import(LServer, {sql, SQLType}, _DBType, <<"privacy_list">>,
[LUser, Name, ID, _TimeStamp]) ->
US = {LUser, LServer},
IS = {prepare_id(SQLType, ID), LServer},
Default = case ets:lookup(privacy_default_list_tmp, US) of
[{_, Name}] -> Name;
_ -> none
end,
case [Item || {_, Item} <- ets:lookup(privacy_list_data_tmp, IS)] of
[_|_] = Items ->
Privacy = #privacy{us = {LUser, LServer},
default = Default,
lists = [{Name, Items}]},
ets:insert(privacy_list_tmp, Privacy),
ets:delete(privacy_list_data_tmp, IS),
ok;
_ ->
ok
end.
import_stop(_LServer, DBType) ->
import_next(DBType, ets:first(privacy_list_tmp)),
ets:delete(privacy_default_list_tmp),
ets:delete(privacy_list_data_tmp),
ets:delete(privacy_list_tmp),
ok.
import_next(_DBType, '$end_of_table') ->
ok;
import_next(DBType, US) ->
[P|_] = Ps = ets:lookup(privacy_list_tmp, US),
Lists = lists:flatmap(
fun(#privacy{lists = Lists}) ->
Lists
end, Ps),
Privacy = P#privacy{lists = Lists},
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(Privacy),
import_next(DBType, ets:next(privacy_list_tmp, US)).
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import(LServer, DBType, Data) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Data).
depends(_Host, _Opts) ->
[].

View File

@ -15,7 +15,7 @@
process_default_set/3, process_active_set/3,
remove_privacy_list/3, set_privacy_list/1,
set_privacy_list/4, get_user_list/2, get_user_lists/2,
remove_user/2, import/2]).
remove_user/2, import/1]).
-include("xmpp.hrl").
-include("mod_privacy.hrl").
@ -144,7 +144,7 @@ remove_user(LUser, LServer) ->
F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end,
mnesia:transaction(F).
import(_LServer, #privacy{} = P) ->
import(#privacy{} = P) ->
mnesia:dirty_write(P).
%%%===================================================================

View File

@ -15,7 +15,7 @@
process_default_set/3, process_active_set/3,
remove_privacy_list/3, set_privacy_list/1,
set_privacy_list/4, get_user_list/2, get_user_lists/2,
remove_user/2, import/2]).
remove_user/2, import/1]).
-export([privacy_schema/0]).
@ -145,7 +145,7 @@ get_user_lists(LUser, LServer) ->
remove_user(LUser, LServer) ->
{atomic, ejabberd_riak:delete(privacy, {LUser, LServer})}.
import(_LServer, #privacy{} = P) ->
import(#privacy{} = P) ->
ejabberd_riak:put(P, privacy_schema()).
%%%===================================================================

View File

@ -17,7 +17,7 @@
process_default_set/3, process_active_set/3,
remove_privacy_list/3, set_privacy_list/1,
set_privacy_list/4, get_user_list/2, get_user_lists/2,
remove_user/2, import/1, import/2, export/1]).
remove_user/2, import/1, export/1]).
-export([item_to_raw/1, raw_to_item/1,
sql_add_privacy_list/2,
@ -249,37 +249,8 @@ get_id() ->
put(id, ID + 1),
ID + 1.
import(LServer) ->
[{<<"select username from privacy_list;">>,
fun([LUser]) ->
Default = case sql_get_default_privacy_list_t(LUser) of
{selected, [<<"name">>], []} ->
none;
{selected, [<<"name">>], [[DefName]]} ->
DefName;
_ ->
none
end,
{selected, [<<"name">>], Names} =
sql_get_privacy_list_names_t(LUser),
Lists = lists:flatmap(
fun([Name]) ->
case sql_get_privacy_list_data_t(LUser, Name) of
{selected, _, RItems} ->
[{Name,
lists:map(fun raw_to_item/1,
RItems)}];
_ ->
[]
end
end, Names),
#privacy{default = Default,
us = {LUser, LServer},
lists = Lists}
end}].
import(_, _) ->
pass.
import(_) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -31,9 +31,9 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_sm_iq/1, import/3,
remove_user/2, get_data/2, get_data/3, export/1, import/1,
mod_opt_type/1, set_data/3, depends/2]).
-export([start/2, stop/1, process_sm_iq/1, import_info/0,
remove_user/2, get_data/2, get_data/3, export/1,
import/5, import_start/2, mod_opt_type/1, set_data/3, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -42,7 +42,7 @@
-include("mod_private.hrl").
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #private_storage{}) -> ok | pass.
-callback import(binary(), binary(), [binary()]) -> ok.
-callback set_data(binary(), binary(), [{binary(), xmlel()}]) -> {atomic, any()}.
-callback get_data(binary(), binary(), binary()) -> {ok, xmlel()} | error.
-callback get_all_data(binary(), binary()) -> [xmlel()].
@ -124,17 +124,20 @@ remove_user(User, Server) ->
Mod = gen_mod:db_mod(Server, ?MODULE),
Mod:remove_user(LUser, LServer).
import_info() ->
[{<<"private_storage">>, 4}].
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []).
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import(LServer, DBType, PD) ->
import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, PD).
Mod:import(LServer, Tab, L).
depends(_Host, _Opts) ->
[].

View File

@ -11,7 +11,7 @@
%% API
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
import/2]).
import/3]).
-include("xmpp.hrl").
-include("mod_private.hrl").
@ -72,7 +72,10 @@ remove_user(LUser, LServer) ->
end,
mnesia:transaction(F).
import(_LServer, #private_storage{} = PS) ->
import(LServer, <<"private_storage">>,
[LUser, XMLNS, XML, _TimeStamp]) ->
El = #xmlel{} = fxml_stream:parse_element(XML),
PS = #private_storage{usns = {LUser, LServer, XMLNS}, xml = El},
mnesia:dirty_write(PS).
%%%===================================================================

View File

@ -12,7 +12,7 @@
%% API
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
import/2]).
import/3]).
-include("xmpp.hrl").
-include("mod_private.hrl").
@ -56,7 +56,10 @@ remove_user(LUser, LServer) ->
{atomic, ejabberd_riak:delete_by_index(private_storage,
<<"us">>, {LUser, LServer})}.
import(_LServer, #private_storage{usns = {LUser, LServer, _}} = PS) ->
import(LServer, <<"private_storage">>,
[LUser, XMLNS, XML, _TimeStamp]) ->
El = #xmlel{} = fxml_stream:parse_element(XML),
PS = #private_storage{usns = {LUser, LServer, XMLNS}, xml = El},
ejabberd_riak:put(PS, private_storage_schema(),
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).

View File

@ -12,7 +12,7 @@
%% API
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
import/1, import/2, export/1]).
import/3, export/1]).
-include("xmpp.hrl").
-include("mod_private.hrl").
@ -77,16 +77,8 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select username, namespace, data from private_storage;">>,
fun([LUser, XMLNS, XML]) ->
El = #xmlel{} = fxml_stream:parse_element(XML),
#private_storage{usns = {LUser, LServer, XMLNS},
xml = El}
end}].
import(_, _) ->
pass.
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -42,8 +42,9 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_iq/1, export/1,
import/1, process_local_iq/1, get_user_roster/2,
import/3, get_subscription_lists/3, get_roster/2,
import_info/0, process_local_iq/1, get_user_roster/2,
import/5, get_subscription_lists/3, get_roster/2,
import_start/2, import_stop/2,
get_in_pending_subscriptions/3, in_subscription/6,
out_subscription/4, set_items/3, remove_user/2,
get_jid_info/4, encode_item/1, webadmin_page/3,
@ -65,7 +66,7 @@
-export_type([subscription/0]).
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #roster{} | #roster_version{}) -> ok | pass.
-callback import(binary(), binary(), #roster{} | [binary()]) -> ok.
-callback read_roster_version(binary(), binary()) -> binary() | error.
-callback write_roster_version(binary(), binary(), boolean(), binary()) -> any().
-callback get_roster(binary(), binary()) -> [#roster{}].
@ -1022,13 +1023,34 @@ export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import_info() ->
[{<<"roster_version">>, 2},
{<<"rostergroups">>, 3},
{<<"rosterusers">>, 10}].
import(LServer, DBType, R) ->
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, R).
ets:new(rostergroups_tmp, [private, named_table, bag]),
Mod:init(LServer, []),
ok.
import_stop(_LServer, _DBType) ->
ets:delete(rostergroups_tmp),
ok.
import(LServer, {sql, _}, _DBType, <<"rostergroups">>, [LUser, SJID, Group]) ->
LJID = jid:tolower(jid:from_string(SJID)),
ets:insert(rostergroups_tmp, {{LUser, LServer, LJID}, Group}),
ok;
import(LServer, {sql, _}, DBType, <<"rosterusers">>, Row) ->
I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, 9)),
Groups = [G || {_, G} <- ets:lookup(rostergroups_tmp, I#roster.usj)],
RosterItem = I#roster{groups = Groups},
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, <<"rosterusers">>, RosterItem);
import(LServer, {sql, _}, DBType, <<"roster_version">>, [LUser, Ver]) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, <<"roster_version">>, [LUser, Ver]).
mod_opt_type(access) ->
fun acl:access_rules_validator/1;

View File

@ -15,7 +15,7 @@
get_roster/2, get_roster_by_jid/3, get_only_items/2,
roster_subscribe/4, get_roster_by_jid_with_groups/3,
remove_user/2, update_roster/4, del_roster/3, transaction/2,
read_subscription_and_groups/3, import/2]).
read_subscription_and_groups/3, import/3, create_roster/1]).
-include("mod_roster.hrl").
-include("logger.hrl").
@ -103,9 +103,13 @@ read_subscription_and_groups(LUser, LServer, LJID) ->
transaction(_LServer, F) ->
mnesia:transaction(F).
import(_LServer, #roster{} = R) ->
create_roster(RItem) ->
mnesia:dirty_write(RItem).
import(_LServer, <<"rosterusers">>, #roster{} = R) ->
mnesia:dirty_write(R);
import(_LServer, #roster_version{} = RV) ->
import(LServer, <<"roster_version">>, [LUser, Ver]) ->
RV = #roster_version{us = {LUser, LServer}, version = Ver},
mnesia:dirty_write(RV).
%%%===================================================================

View File

@ -13,10 +13,10 @@
%% API
-export([init/2, read_roster_version/2, write_roster_version/4,
get_roster/2, get_roster_by_jid/3,
get_roster/2, get_roster_by_jid/3, create_roster/1,
roster_subscribe/4, get_roster_by_jid_with_groups/3,
remove_user/2, update_roster/4, del_roster/3, transaction/2,
read_subscription_and_groups/3, get_only_items/2, import/2]).
read_subscription_and_groups/3, get_only_items/2, import/3]).
-include("mod_roster.hrl").
@ -96,10 +96,17 @@ read_subscription_and_groups(LUser, LServer, LJID) ->
error
end.
import(_LServer, #roster{us = {LUser, LServer}} = R) ->
ejabberd_riak:put(R, roster_schema(),
create_roster(#roster{us = {LUser, LServer}} = RItem) ->
ejabberd_riak:put(
RItem, roster_schema(),
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
import(_LServer, <<"rosterusers">>, RosterItem) ->
{LUser, LServer} = RosterItem#roster.us,
ejabberd_riak:put(RosterItem, roster_schema(),
[{'2i', [{<<"us">>, {LUser, LServer}}]}]);
import(_LServer, #roster_version{} = RV) ->
import(LServer, <<"roster_version">>, [LUser, Ver]) ->
RV = #roster_version{us = {LUser, LServer}, version = Ver},
ejabberd_riak:put(RV, roster_version_schema()).
%%%===================================================================

View File

@ -18,7 +18,7 @@
roster_subscribe/4, get_roster_by_jid_with_groups/3,
remove_user/2, update_roster/4, del_roster/3, transaction/2,
read_subscription_and_groups/3, get_only_items/2,
import/1, import/2, export/1]).
import/3, export/1, raw_to_record/2]).
-include("mod_roster.hrl").
-include("ejabberd_sql_pt.hrl").
@ -185,27 +185,8 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, type from rosterusers;">>,
fun([LUser, JID|_] = Row) ->
Item = raw_to_record(LServer, Row),
Username = ejabberd_sql:escape(LUser),
SJID = ejabberd_sql:escape(JID),
{selected, _, Rows} =
ejabberd_sql:sql_query_t(
[<<"select grp from rostergroups where username='">>,
Username, <<"' and jid='">>, SJID, <<"'">>]),
Groups = [Grp || [Grp] <- Rows],
Item#roster{groups = Groups}
end},
{<<"select username, version from roster_version;">>,
fun([LUser, Ver]) ->
#roster_version{us = {LUser, LServer}, version = Ver}
end}].
import(_, _) ->
pass.
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -30,9 +30,9 @@
-behaviour(gen_mod).
-export([start/2, stop/1, export/1,
import/1, webadmin_menu/3, webadmin_page/3,
import_info/0, webadmin_menu/3, webadmin_page/3,
get_user_roster/2, get_subscription_lists/3,
get_jid_info/4, import/3, process_item/2,
get_jid_info/4, import/5, process_item/2, import_start/2,
in_subscription/6, out_subscription/4, user_available/1,
unset_presence/4, register_user/2, remove_user/2,
list_groups/1, create_group/2, create_group/3,
@ -56,7 +56,7 @@
-type group_options() :: [{atom(), any()}].
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #sr_user{} | #sr_group{}) -> ok | pass.
-callback import(binary(), binary(), [binary()]) -> ok.
-callback list_groups(binary()) -> [binary()].
-callback groups_with_opts(binary()) -> [{binary(), group_options()}].
-callback create_group(binary(), binary(), group_options()) -> {atomic, any()}.
@ -1072,13 +1072,16 @@ export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import_info() ->
[{<<"sr_group">>, 3}, {<<"sr_user">>, 3}].
import(LServer, DBType, Data) ->
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Data).
Mod:init(LServer, []).
import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, L).
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [db_type].

View File

@ -15,11 +15,12 @@
delete_group/2, get_group_opts/2, set_group_opts/3,
get_user_groups/2, get_group_explicit_users/2,
get_user_displayed_groups/3, is_user_in_group/3,
add_user_to_group/3, remove_user_from_group/3, import/2]).
add_user_to_group/3, remove_user_from_group/3, import/3]).
-include("mod_roster.hrl").
-include("mod_shared_roster.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
%%%===================================================================
%%% API
@ -118,10 +119,14 @@ remove_user_from_group(Host, US, Group) ->
F = fun () -> mnesia:delete_object(R) end,
mnesia:transaction(F).
import(_LServer, #sr_group{} = G) ->
import(LServer, <<"sr_group">>, [Group, SOpts, _TimeStamp]) ->
G = #sr_group{group_host = {Group, LServer},
opts = ejabberd_sql:decode_term(SOpts)},
mnesia:dirty_write(G);
import(_LServer, #sr_user{} = U) ->
mnesia:dirty_write(U).
import(LServer, <<"sr_user">>, [SJID, Group, _TimeStamp]) ->
#jid{luser = U, lserver = S} = jid:from_string(SJID),
User = #sr_user{us = {U, S}, group_host = {Group, LServer}},
mnesia:dirty_write(User).
%%%===================================================================
%%% Internal functions

View File

@ -15,10 +15,11 @@
delete_group/2, get_group_opts/2, set_group_opts/3,
get_user_groups/2, get_group_explicit_users/2,
get_user_displayed_groups/3, is_user_in_group/3,
add_user_to_group/3, remove_user_from_group/3, import/2]).
add_user_to_group/3, remove_user_from_group/3, import/3]).
-include("mod_roster.hrl").
-include("mod_shared_roster.hrl").
-include("xmpp.hrl").
%%%===================================================================
%%% API
@ -120,13 +121,17 @@ add_user_to_group(Host, US, Group) ->
remove_user_from_group(Host, US, Group) ->
{atomic, ejabberd_riak:delete(sr_group, {US, {Group, Host}})}.
import(_LServer, #sr_group{group_host = {_, Host}} = G) ->
ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}]);
import(_LServer, #sr_user{us = US, group_host = {Group, Host}} = User) ->
import(LServer, <<"sr_group">>, [Group, SOpts, _TimeStamp]) ->
G = #sr_group{group_host = {Group, LServer},
opts = ejabberd_sql:decode_term(SOpts)},
ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, LServer}]}]);
import(LServer, <<"sr_user">>, [SJID, Group|_]) ->
#jid{luser = U, lserver = S} = jid:from_string(SJID),
User = #sr_user{us = {U, S}, group_host = {Group, LServer}},
ejabberd_riak:put(User, sr_user_schema(),
[{i, {US, {Group, Host}}},
{'2i', [{<<"us">>, US},
{<<"group_host">>, {Group, Host}}]}]).
[{i, {{U, S}, {Group, LServer}}},
{'2i', [{<<"us">>, {U, S}},
{<<"group_host">>, {Group, LServer}}]}]).
%%%===================================================================
%%% Internal functions

View File

@ -17,8 +17,8 @@
delete_group/2, get_group_opts/2, set_group_opts/3,
get_user_groups/2, get_group_explicit_users/2,
get_user_displayed_groups/3, is_user_in_group/3,
add_user_to_group/3, remove_user_from_group/3, import/1,
import/2, export/1]).
add_user_to_group/3, remove_user_from_group/3, import/3,
export/1]).
-include("jid.hrl").
-include("mod_roster.hrl").
@ -177,20 +177,8 @@ export(_Server) ->
[]
end}].
import(LServer) ->
[{<<"select name, opts from sr_group;">>,
fun([Group, SOpts]) ->
#sr_group{group_host = {Group, LServer},
opts = ejabberd_sql:decode_term(SOpts)}
end},
{<<"select jid, grp from sr_user;">>,
fun([SJID, Group]) ->
#jid{luser = U, lserver = S} = jid:from_string(SJID),
#sr_user{us = {U, S}, group_host = {Group, LServer}}
end}].
import(_, _) ->
pass.
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions

View File

@ -34,8 +34,8 @@
-export([start/2, init/3, stop/1, get_sm_features/5,
process_local_iq/1, process_sm_iq/1, string2lower/1,
remove_user/2, export/1, import/1, import/3, depends/2,
process_search/1, process_vcard/1, get_vcard/2,
remove_user/2, export/1, import_info/0, import/5, import_start/2,
depends/2, process_search/1, process_vcard/1, get_vcard/2,
disco_items/5, disco_features/5, disco_identity/5,
decode_iq_subel/1, mod_opt_type/1, set_vcard/3, make_vcard_search/4]).
@ -50,7 +50,7 @@
-callback init(binary(), gen_mod:opts()) -> any().
-callback stop(binary()) -> any().
-callback import(binary(), #vcard{} | #vcard_search{}) -> ok | pass.
-callback import(binary(), binary(), [binary()]) -> ok.
-callback get_vcard(binary(), binary()) -> [xmlel()] | error.
-callback set_vcard(binary(), binary(),
xmlel(), #vcard_search{}) -> {atomic, any()}.
@ -59,6 +59,7 @@
-callback search(binary(), [{binary(), [binary()]}], boolean(),
infinity | pos_integer()) -> [{binary(), binary()}].
-callback remove_user(binary(), binary()) -> {atomic, any()}.
-callback is_search_supported(binary()) -> boolean().
start(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
@ -105,6 +106,15 @@ init(Host, ServerHost, Search) ->
false -> loop(Host, ServerHost);
_ ->
ejabberd_router:register_route(Host, ServerHost),
Mod = gen_mod:db_mod(ServerHost, ?MODULE),
case Mod:is_search_supported(ServerHost) of
false ->
?WARNING_MSG("vcard search functionality is "
"not implemented for ~s backend",
[gen_mod:db_type(ServerHost, ?MODULE)]);
true ->
ejabberd_router:register_route(Host, ServerHost)
end,
loop(Host, ServerHost)
end.
@ -438,18 +448,21 @@ remove_user(User, Server) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_user(LUser, LServer).
import_info() ->
[{<<"vcard">>, 3}, {<<"vcard_search">>, 24}].
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []).
import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, L).
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import(LServer, DBType, VCard) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, VCard).
depends(_Host, _Opts) ->
[].

View File

@ -8,14 +8,17 @@
%%%-------------------------------------------------------------------
-module(mod_vcard_ldap).
-behaviour(ejabberd_config).
-behaviour(gen_server).
-behaviour(mod_vcard).
%% API
-export([start_link/2]).
-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4,
remove_user/2, import/2, search_fields/1, search_reported/1,
remove_user/2, import/3, search_fields/1, search_reported/1,
mod_opt_type/1, opt_type/1]).
-export([is_search_supported/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@ -70,6 +73,9 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
is_search_supported(_LServer) ->
true.
get_vcard(LUser, LServer) ->
{ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
VCardMap = State#state.vcard_map,
@ -157,7 +163,7 @@ search_items(Entries, State) ->
remove_user(_User, _Server) ->
{atomic, not_implemented}.
import(_, _) ->
import(_, _, _) ->
pass.
%%%===================================================================

View File

@ -11,8 +11,9 @@
-behaviour(mod_vcard).
%% API
-export([init/2, stop/1, import/2, get_vcard/2, set_vcard/4, search/4,
-export([init/2, stop/1, import/3, get_vcard/2, set_vcard/4, search/4,
search_fields/1, search_reported/1, remove_user/2]).
-export([is_search_supported/1]).
-include("ejabberd.hrl").
-include("xmpp.hrl").
@ -47,6 +48,9 @@ init(_Host, _Opts) ->
stop(_Host) ->
ok.
is_search_supported(_ServerHost) ->
true.
get_vcard(LUser, LServer) ->
US = {LUser, LServer},
F = fun () -> mnesia:read({vcard, US}) end,
@ -121,10 +125,29 @@ remove_user(LUser, LServer) ->
end,
mnesia:transaction(F).
import(_LServer, #vcard{} = VCard) ->
import(LServer, <<"vcard">>, [LUser, XML, _TimeStamp]) ->
#xmlel{} = El = fxml_stream:parse_element(XML),
VCard = #vcard{us = {LUser, LServer}, vcard = El},
mnesia:dirty_write(VCard);
import(_LServer, #vcard_search{} = S) ->
mnesia:dirty_write(S).
import(LServer, <<"vcard_search">>,
[User, LUser, FN, LFN,
Family, LFamily, Given, LGiven,
Middle, LMiddle, Nickname, LNickname,
BDay, LBDay, CTRY, LCTRY, Locality, LLocality,
EMail, LEMail, OrgName, LOrgName, OrgUnit, LOrgUnit]) ->
mnesia:dirty_write(
#vcard_search{us = {LUser, LServer},
user = {User, LServer}, luser = LUser,
fn = FN, lfn = LFN, family = Family,
lfamily = LFamily, given = Given,
lgiven = LGiven, middle = Middle,
lmiddle = LMiddle, nickname = Nickname,
lnickname = LNickname, bday = BDay,
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
locality = Locality, llocality = LLocality,
email = EMail, lemail = LEMail,
orgname = OrgName, lorgname = LOrgName,
orgunit = OrgUnit, lorgunit = LOrgUnit}).
%%%===================================================================
%%% Internal functions

View File

@ -12,7 +12,8 @@
%% API
-export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2,
search_fields/1, search_reported/1, import/2, stop/1]).
search_fields/1, search_reported/1, import/3, stop/1]).
-export([is_search_supported/1]).
-include("xmpp.hrl").
-include("mod_vcard.hrl").
@ -26,6 +27,9 @@ init(_Host, _Opts) ->
stop(_Host) ->
ok.
is_search_supported(_LServer) ->
false.
get_vcard(LUser, LServer) ->
case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of
{ok, R} ->
@ -101,7 +105,9 @@ search_reported(_LServer) ->
remove_user(LUser, LServer) ->
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
import(_LServer, #vcard{us = {LUser, LServer}, vcard = El} = VCard) ->
import(LServer, <<"vcard">>, [LUser, XML, _TimeStamp]) ->
El = fxml_stream:parse_element(XML),
VCard = #vcard{us = {LUser, LServer}, vcard = El},
#vcard_search{fn = FN,
lfn = LFN,
family = Family,
@ -150,7 +156,7 @@ import(_LServer, #vcard{us = {LUser, LServer}, vcard = El} = VCard) ->
{<<"lorgname">>, LOrgName},
{<<"orgunit">>, OrgUnit},
{<<"lorgunit">>, LOrgUnit}]}]);
import(_LServer, #vcard_search{}) ->
import(_LServer, <<"vcard_search">>, _) ->
ok.
%%%===================================================================

Some files were not shown because too many files have changed in this diff Show More