mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-20 16:15:59 +01:00
Add xdata generator and make some code using it
This commit is contained in:
parent
1de0bb83a0
commit
6a3691ef7c
@ -110,7 +110,11 @@ edoc:
|
|||||||
|
|
||||||
spec:
|
spec:
|
||||||
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
|
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
|
||||||
'case fxml_gen:compile("tools/xmpp_codec.spec", [{add_type_specs, xmpp_element}, {erl_dir, "src"}, {hrl_dir, "include"}]) of ok -> halt(0); _ -> halt(1) end.'
|
'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))
|
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))
|
||||||
|
|
||||||
|
10
include/flex_offline.hrl
Normal file
10
include/flex_offline.hrl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
%% 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()].
|
16
include/muc_register.hrl
Normal file
16
include/muc_register.hrl
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
%% 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()].
|
16
include/muc_request.hrl
Normal file
16
include/muc_request.hrl
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
%% 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()].
|
55
include/muc_roomconfig.hrl
Normal file
55
include/muc_roomconfig.hrl
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
%% 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()].
|
18
include/muc_roominfo.hrl
Normal file
18
include/muc_roominfo.hrl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
%% 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()].
|
13
include/pubsub_get_pending.hrl
Normal file
13
include/pubsub_get_pending.hrl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
%% 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()].
|
60
include/pubsub_node_config.hrl
Normal file
60
include/pubsub_node_config.hrl
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
%% 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()].
|
14
include/pubsub_publish_options.hrl
Normal file
14
include/pubsub_publish_options.hrl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
%% 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()].
|
13
include/pubsub_subscribe_authorization.hrl
Normal file
13
include/pubsub_subscribe_authorization.hrl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
%% 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()].
|
25
include/pubsub_subscribe_options.hrl
Normal file
25
include/pubsub_subscribe_options.hrl
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
%% 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()].
|
@ -8,7 +8,7 @@
|
|||||||
-record(ps_affiliation, {xmlns = <<>> :: binary(),
|
-record(ps_affiliation, {xmlns = <<>> :: binary(),
|
||||||
node = <<>> :: binary(),
|
node = <<>> :: binary(),
|
||||||
type :: member | none | outcast |
|
type :: member | none | outcast |
|
||||||
owner | publisher | 'publish-only',
|
owner | publisher | publish_only,
|
||||||
jid :: jid:jid()}).
|
jid :: jid:jid()}).
|
||||||
-type ps_affiliation() :: #ps_affiliation{}.
|
-type ps_affiliation() :: #ps_affiliation{}.
|
||||||
|
|
||||||
@ -510,7 +510,7 @@
|
|||||||
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
||||||
var = <<>> :: binary(),
|
var = <<>> :: binary(),
|
||||||
required = false :: boolean(),
|
required = false :: boolean(),
|
||||||
desc :: binary(),
|
desc = <<>> :: binary(),
|
||||||
values = [] :: [binary()],
|
values = [] :: [binary()],
|
||||||
options = [] :: [#xdata_option{}],
|
options = [] :: [#xdata_option{}],
|
||||||
sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
|
sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
|
||||||
|
1
specs/flex_offline.cfg
Normal file
1
specs/flex_offline.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{decode, [{<<"number_of_messages">>, {dec_int, [0, infinity]}}]}].
|
12
specs/flex_offline.xdata
Normal file
12
specs/flex_offline.xdata
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<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>
|
2
specs/muc_register.cfg
Normal file
2
specs/muc_register.cfg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[{prefix, <<"muc#register_">>},
|
||||||
|
{required, [<<"muc#register_roomnick">>]}].
|
37
specs/muc_register.xdata
Normal file
37
specs/muc_register.xdata
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<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>
|
2
specs/muc_request.cfg
Normal file
2
specs/muc_request.cfg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[{prefix, <<"muc#">>},
|
||||||
|
{required, [<<"muc#role">>]}].
|
31
specs/muc_request.xdata
Normal file
31
specs/muc_request.xdata
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
11
specs/muc_roomconfig.cfg
Normal file
11
specs/muc_roomconfig.cfg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[{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:
|
192
specs/muc_roomconfig.xdata
Normal file
192
specs/muc_roomconfig.xdata
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
11
specs/muc_roominfo.cfg
Normal file
11
specs/muc_roominfo.cfg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[{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:
|
55
specs/muc_roominfo.xdata
Normal file
55
specs/muc_roominfo.xdata
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
7
specs/pubsub_get_pending.cfg
Normal file
7
specs/pubsub_get_pending.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[{prefix, <<"pubsub#">>},
|
||||||
|
{required, [<<"pubsub#node">>]}].
|
||||||
|
|
||||||
|
%% Local Variables:
|
||||||
|
%% mode: erlang
|
||||||
|
%% End:
|
||||||
|
%% vim: set filetype=erlang tabstop=8:
|
15
specs/pubsub_get_pending.xdata
Normal file
15
specs/pubsub_get_pending.xdata
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
8
specs/pubsub_node_config.cfg
Normal file
8
specs/pubsub_node_config.cfg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[{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:
|
189
specs/pubsub_node_config.xdata
Normal file
189
specs/pubsub_node_config.xdata
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
6
specs/pubsub_publish_options.cfg
Normal file
6
specs/pubsub_publish_options.cfg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[{prefix, <<"pubsub#">>}].
|
||||||
|
|
||||||
|
%% Local Variables:
|
||||||
|
%% mode: erlang
|
||||||
|
%% End:
|
||||||
|
%% vim: set filetype=erlang tabstop=8:
|
34
specs/pubsub_publish_options.xdata
Normal file
34
specs/pubsub_publish_options.xdata
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
7
specs/pubsub_subscribe_authorization.cfg
Normal file
7
specs/pubsub_subscribe_authorization.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[{prefix, <<"pubsub#">>},
|
||||||
|
{required, [<<"pubsub#node">>, <<"pubsub#subscriber_jid">>, <<"pubsub#allow">>]}].
|
||||||
|
|
||||||
|
%% Local Variables:
|
||||||
|
%% mode: erlang
|
||||||
|
%% End:
|
||||||
|
%% vim: set filetype=erlang tabstop=8:
|
27
specs/pubsub_subscribe_authorization.xdata
Normal file
27
specs/pubsub_subscribe_authorization.xdata
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<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:
|
||||||
|
-->
|
1
specs/pubsub_subscribe_options.cfg
Normal file
1
specs/pubsub_subscribe_options.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{prefix, <<"pubsub#">>}].
|
70
specs/pubsub_subscribe_options.xdata
Normal file
70
specs/pubsub_subscribe_options.xdata
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<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>
|
@ -1610,6 +1610,7 @@
|
|||||||
default = false,
|
default = false,
|
||||||
min = 0, max = 1},
|
min = 0, max = 1},
|
||||||
#ref{name = xdata_field_desc,
|
#ref{name = xdata_field_desc,
|
||||||
|
default = <<"">>,
|
||||||
label = '$desc',
|
label = '$desc',
|
||||||
min = 0, max = 1},
|
min = 0, max = 1},
|
||||||
#ref{name = xdata_field_value,
|
#ref{name = xdata_field_value,
|
||||||
@ -1682,7 +1683,7 @@
|
|||||||
-record(ps_affiliation, {xmlns = <<>> :: binary(),
|
-record(ps_affiliation, {xmlns = <<>> :: binary(),
|
||||||
node = <<>> :: binary(),
|
node = <<>> :: binary(),
|
||||||
type :: member | none | outcast |
|
type :: member | none | outcast |
|
||||||
owner | publisher | 'publish-only',
|
owner | publisher | publish_only,
|
||||||
jid :: jid:jid()}).
|
jid :: jid:jid()}).
|
||||||
-type ps_affiliation() :: #ps_affiliation{}.
|
-type ps_affiliation() :: #ps_affiliation{}.
|
||||||
|
|
||||||
@ -1695,9 +1696,8 @@
|
|||||||
#attr{name = <<"affiliation">>,
|
#attr{name = <<"affiliation">>,
|
||||||
label = '$type',
|
label = '$type',
|
||||||
required = true,
|
required = true,
|
||||||
dec = {dec_enum, [[member, none, outcast, owner,
|
dec = {dec_ps_aff, []},
|
||||||
publisher, 'publish-only']]},
|
enc = {enc_ps_aff, []}}]}).
|
||||||
enc = {enc_enum, []}}]}).
|
|
||||||
|
|
||||||
-xml(pubsub_owner_affiliation,
|
-xml(pubsub_owner_affiliation,
|
||||||
#elem{name = <<"affiliation">>,
|
#elem{name = <<"affiliation">>,
|
||||||
@ -1711,9 +1711,8 @@
|
|||||||
#attr{name = <<"affiliation">>,
|
#attr{name = <<"affiliation">>,
|
||||||
label = '$type',
|
label = '$type',
|
||||||
required = true,
|
required = true,
|
||||||
dec = {dec_enum, [[member, none, outcast, owner,
|
dec = {dec_ps_aff, []},
|
||||||
publisher, 'publish-only']]},
|
enc = {enc_ps_aff, []}}]}).
|
||||||
enc = {enc_enum, []}}]}).
|
|
||||||
|
|
||||||
-xml(pubsub_event_configuration,
|
-xml(pubsub_event_configuration,
|
||||||
#elem{name = <<"configuration">>,
|
#elem{name = <<"configuration">>,
|
||||||
@ -3501,6 +3500,22 @@ dec_version(S) ->
|
|||||||
enc_version({Maj, Min}) ->
|
enc_version({Maj, Min}) ->
|
||||||
<<(integer_to_binary(Maj))/binary, $., (integer_to_binary(Min))/binary>>.
|
<<(integer_to_binary(Maj))/binary, $., (integer_to_binary(Min))/binary>>.
|
||||||
|
|
||||||
|
-spec dec_ps_aff(_) -> member | none | outcast |
|
||||||
|
owner | publisher | publish_only.
|
||||||
|
dec_ps_aff(<<"member">>) -> member;
|
||||||
|
dec_ps_aff(<<"none">>) -> none;
|
||||||
|
dec_ps_aff(<<"outcast">>) -> outcast;
|
||||||
|
dec_ps_aff(<<"owner">>) -> owner;
|
||||||
|
dec_ps_aff(<<"publisher">>) -> publisher;
|
||||||
|
dec_ps_aff(<<"publish-only">>) -> publish_only.
|
||||||
|
|
||||||
|
enc_ps_aff(member) -> <<"member">>;
|
||||||
|
enc_ps_aff(none) -> <<"none">>;
|
||||||
|
enc_ps_aff(outcast) -> <<"outcast">>;
|
||||||
|
enc_ps_aff(owner) -> <<"owner">>;
|
||||||
|
enc_ps_aff(publisher) -> <<"publisher">>;
|
||||||
|
enc_ps_aff(publish_only) -> <<"publish-only">>.
|
||||||
|
|
||||||
%% Local Variables:
|
%% Local Variables:
|
||||||
%% mode: erlang
|
%% mode: erlang
|
||||||
%% End:
|
%% End:
|
@ -73,6 +73,7 @@ captcha_text(Lang) ->
|
|||||||
mk_ocr_field(Lang, CID, Type) ->
|
mk_ocr_field(Lang, CID, Type) ->
|
||||||
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
|
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
|
||||||
#xdata_field{var = <<"ocr">>,
|
#xdata_field{var = <<"ocr">>,
|
||||||
|
type = 'text-single',
|
||||||
label = captcha_text(Lang),
|
label = captcha_text(Lang),
|
||||||
required = true,
|
required = true,
|
||||||
sub_els = [#media{uri = [URI]}]}.
|
sub_els = [#media{uri = [URI]}]}.
|
||||||
|
128
src/flex_offline.erl
Normal file
128
src/flex_offline.erl
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
%% 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">>)}.
|
@ -488,14 +488,11 @@ concat_identities(#disco_info{identities = Identities}) ->
|
|||||||
-spec concat_info(disco_info()) -> iolist().
|
-spec concat_info(disco_info()) -> iolist().
|
||||||
concat_info(#disco_info{xdata = Xs}) ->
|
concat_info(#disco_info{xdata = Xs}) ->
|
||||||
lists:sort(
|
lists:sort(
|
||||||
[concat_xdata_fields(Fs) || #xdata{type = result, fields = Fs} <- Xs]).
|
[concat_xdata_fields(X) || #xdata{type = result} = X <- Xs]).
|
||||||
|
|
||||||
-spec concat_xdata_fields([xdata_field()]) -> iolist().
|
-spec concat_xdata_fields(xdata()) -> iolist().
|
||||||
concat_xdata_fields(Fields) ->
|
concat_xdata_fields(#xdata{fields = Fields} = X) ->
|
||||||
Form = case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var, Fields) of
|
Form = xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X),
|
||||||
#xdata_field{values = Values} -> Values;
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
Res = [[Var, $<, lists:sort([[Val, $<] || Val <- Values])]
|
Res = [[Var, $<, lists:sort([[Val, $<] || Val <- Values])]
|
||||||
|| #xdata_field{var = Var, values = Values} <- Fields,
|
|| #xdata_field{var = Var, values = Values} <- Fields,
|
||||||
is_binary(Var), Var /= <<"FORM_TYPE">>],
|
is_binary(Var), Var /= <<"FORM_TYPE">>],
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5,
|
process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5,
|
||||||
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2,
|
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2,
|
||||||
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
||||||
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/4]).
|
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/3]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -191,39 +191,17 @@ remove_room(LServer, Name, Host) ->
|
|||||||
Mod:remove_room(LServer, LName, LHost),
|
Mod:remove_room(LServer, LName, LHost),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec get_room_config([xdata_field()], mod_muc_room:state(), jid(), binary())
|
-spec get_room_config([muc_roomconfig:property()], mod_muc_room:state(),
|
||||||
-> [xdata_field()].
|
jid(), binary()) -> [muc_roomconfig:property()].
|
||||||
get_room_config(XFields, RoomState, _From, Lang) ->
|
get_room_config(Fields, RoomState, _From, _Lang) ->
|
||||||
Config = RoomState#state.config,
|
Config = RoomState#state.config,
|
||||||
Label = <<"Enable message archiving">>,
|
Fields ++ [{mam, Config#config.mam}].
|
||||||
Var = <<"muc#roomconfig_mam">>,
|
|
||||||
Val = case Config#config.mam of
|
|
||||||
true -> <<"1">>;
|
|
||||||
_ -> <<"0">>
|
|
||||||
end,
|
|
||||||
XField = #xdata_field{type = boolean,
|
|
||||||
label = translate:translate(Lang, Label),
|
|
||||||
var = Var,
|
|
||||||
values = [Val]},
|
|
||||||
XFields ++ [XField].
|
|
||||||
|
|
||||||
-spec set_room_option({pos_integer(), _}, binary(), [binary()], binary())
|
-spec set_room_option({pos_integer(), _}, muc_roomconfig:property(), binary())
|
||||||
-> {pos_integer(), _}.
|
-> {pos_integer(), _}.
|
||||||
set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
|
set_room_option(_Acc, {mam, Val}, _Lang) ->
|
||||||
try
|
{#config.mam, Val};
|
||||||
Val = case Vals of
|
set_room_option(Acc, _Property, _Lang) ->
|
||||||
[<<"0">>|_] -> false;
|
|
||||||
[<<"false">>|_] -> false;
|
|
||||||
[<<"1">>|_] -> true;
|
|
||||||
[<<"true">>|_] -> true
|
|
||||||
end,
|
|
||||||
{#config.mam, Val}
|
|
||||||
catch _:{case_clause, _} ->
|
|
||||||
Txt = <<"Value of '~s' should be boolean">>,
|
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
|
||||||
{error, xmpp:err_bad_request(ErrTxt, Lang)}
|
|
||||||
end;
|
|
||||||
set_room_option(Acc, _Opt, _Vals, _Lang) ->
|
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
-spec user_receive_packet(stanza(), ejabberd_c2s:state(), jid(), jid(), jid()) -> stanza().
|
-spec user_receive_packet(stanza(), ejabberd_c2s:state(), jid(), jid(), jid()) -> stanza().
|
||||||
|
@ -668,19 +668,18 @@ get_nick(ServerHost, Host, From) ->
|
|||||||
Mod:get_nick(LServer, Host, From).
|
Mod:get_nick(LServer, Host, From).
|
||||||
|
|
||||||
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||||
{Nick, NickVals, Registered} = case get_nick(ServerHost, Host, From) of
|
{Nick, Registered} = case get_nick(ServerHost, Host, From) of
|
||||||
error -> {<<"">>, [], false};
|
error -> {<<"">>, false};
|
||||||
N -> {N, [N], true}
|
N -> {N, true}
|
||||||
end,
|
end,
|
||||||
Title = <<(translate:translate(
|
Title = <<(translate:translate(
|
||||||
Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>,
|
Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>,
|
||||||
Inst = translate:translate(Lang, <<"Enter nickname you want to register">>),
|
Inst = translate:translate(Lang, <<"Enter nickname you want to register">>),
|
||||||
Field = #xdata_field{type = 'text-single',
|
Fields = muc_register:encode(
|
||||||
label = translate:translate(Lang, <<"Nickname">>),
|
[{roomnick, Nick}],
|
||||||
var = <<"nick">>,
|
fun(T) -> translate:translate(Lang, T) end),
|
||||||
values = NickVals},
|
|
||||||
X = #xdata{type = form, title = Title,
|
X = #xdata{type = form, title = Title,
|
||||||
instructions = [Inst], fields = [Field]},
|
instructions = [Inst], fields = Fields},
|
||||||
#register{nick = Nick,
|
#register{nick = Nick,
|
||||||
registered = Registered,
|
registered = Registered,
|
||||||
instructions =
|
instructions =
|
||||||
@ -717,12 +716,13 @@ process_iq_register_set(ServerHost, Host, From,
|
|||||||
#register{nick = Nick, xdata = XData}, Lang) ->
|
#register{nick = Nick, xdata = XData}, Lang) ->
|
||||||
case XData of
|
case XData of
|
||||||
#xdata{type = submit, fields = Fs} ->
|
#xdata{type = submit, fields = Fs} ->
|
||||||
case lists:keyfind(<<"nick">>, #xdata_field.var, Fs) of
|
try
|
||||||
#xdata_field{values = [N]} ->
|
Options = muc_register:decode(Fs),
|
||||||
iq_set_register_info(ServerHost, Host, From, N, Lang);
|
N = proplists:get_value(roomnick, Options),
|
||||||
_ ->
|
iq_set_register_info(ServerHost, Host, From, N, Lang)
|
||||||
ErrText = <<"You must fill in field \"Nickname\" in the form">>,
|
catch _:{muc_register, Why} ->
|
||||||
{error, xmpp:err_not_acceptable(ErrText, Lang)}
|
ErrText = muc_register:format_error(Why),
|
||||||
|
{error, xmpp:err_bad_request(ErrText, Lang)}
|
||||||
end;
|
end;
|
||||||
#xdata{} ->
|
#xdata{} ->
|
||||||
Txt = <<"Incorrect data form">>,
|
Txt = <<"Incorrect data form">>,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -300,8 +300,7 @@ get_sm_items(Acc, _From, _To, _Node, _Lang) ->
|
|||||||
-spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()];
|
-spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()];
|
||||||
([xdata()], jid(), jid(), binary(), binary()) -> [xdata()].
|
([xdata()], jid(), jid(), binary(), binary()) -> [xdata()].
|
||||||
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
|
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
|
||||||
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
|
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, Lang) ->
|
||||||
N = integer_to_binary(count_offline_messages(U, S)),
|
|
||||||
case ejabberd_sm:get_session_pid(U, S, R) of
|
case ejabberd_sm:get_session_pid(U, S, R) of
|
||||||
Pid when is_pid(Pid) ->
|
Pid when is_pid(Pid) ->
|
||||||
Pid ! dont_ask_offline;
|
Pid ! dont_ask_offline;
|
||||||
@ -309,11 +308,9 @@ get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
|
|||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
[#xdata{type = result,
|
[#xdata{type = result,
|
||||||
fields = [#xdata_field{var = <<"FORM_TYPE">>,
|
fields = flex_offline:encode(
|
||||||
type = hidden,
|
[{number_of_messages, count_offline_messages(U, S)}],
|
||||||
values = [?NS_FLEX_OFFLINE]},
|
fun(T) -> translate:translate(Lang, T) end)}];
|
||||||
#xdata_field{var = <<"number_of_messages">>,
|
|
||||||
values = [N]}]}];
|
|
||||||
get_info(Acc, _From, _To, _Node, _Lang) ->
|
get_info(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
|
@ -1028,8 +1028,11 @@ do_route(Host, From, To, Packet) ->
|
|||||||
case find_authorization_response(Packet) of
|
case find_authorization_response(Packet) of
|
||||||
undefined ->
|
undefined ->
|
||||||
ok;
|
ok;
|
||||||
XData ->
|
{error, Err} ->
|
||||||
handle_authorization_response(Host, From, To, Packet, XData)
|
ejabberd_router:route_error(To, From, Packet, Err);
|
||||||
|
AuthResponse ->
|
||||||
|
handle_authorization_response(
|
||||||
|
Host, From, To, Packet, AuthResponse)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
Err = xmpp:err_service_unavailable(),
|
Err = xmpp:err_service_unavailable(),
|
||||||
@ -1200,19 +1203,28 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
|||||||
ServerHost = serverhost(Host),
|
ServerHost = serverhost(Host),
|
||||||
Plugins = config(ServerHost, plugins),
|
Plugins = config(ServerHost, plugins),
|
||||||
Config = case Configure of
|
Config = case Configure of
|
||||||
{_, XData} -> get_xdata_fields(XData);
|
{_, XData} -> decode_node_config(XData, Host, Lang);
|
||||||
undefined -> []
|
undefined -> []
|
||||||
end,
|
end,
|
||||||
Type = hd(Plugins),
|
Type = hd(Plugins),
|
||||||
create_node(Host, ServerHost, Node, From, Type, Access, Config);
|
case Config of
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err;
|
||||||
|
_ ->
|
||||||
|
create_node(Host, ServerHost, Node, From, Type, Access, Config)
|
||||||
|
end;
|
||||||
{set, #pubsub{publish = #ps_publish{node = Node, items = Items},
|
{set, #pubsub{publish = #ps_publish{node = Node, items = Items},
|
||||||
publish_options = XData, _ = undefined}} ->
|
publish_options = XData, _ = undefined}} ->
|
||||||
ServerHost = serverhost(Host),
|
ServerHost = serverhost(Host),
|
||||||
case Items of
|
case Items of
|
||||||
[#ps_item{id = ItemId, xml_els = Payload}] ->
|
[#ps_item{id = ItemId, xml_els = Payload}] ->
|
||||||
PubOpts = get_xdata_fields(XData),
|
case decode_publish_options(XData, Lang) of
|
||||||
publish_item(Host, ServerHost, Node, From, ItemId,
|
{error, _} = Err ->
|
||||||
Payload, PubOpts, Access);
|
Err;
|
||||||
|
PubOpts ->
|
||||||
|
publish_item(Host, ServerHost, Node, From, ItemId,
|
||||||
|
Payload, PubOpts, Access)
|
||||||
|
end;
|
||||||
[] ->
|
[] ->
|
||||||
{error, extended_error(xmpp:err_bad_request(), err_item_required())};
|
{error, extended_error(xmpp:err_bad_request(), err_item_required())};
|
||||||
_ ->
|
_ ->
|
||||||
@ -1236,10 +1248,17 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
|||||||
{set, #pubsub{subscribe = #ps_subscribe{node = Node, jid = JID},
|
{set, #pubsub{subscribe = #ps_subscribe{node = Node, jid = JID},
|
||||||
options = Options, _ = undefined}} ->
|
options = Options, _ = undefined}} ->
|
||||||
Config = case Options of
|
Config = case Options of
|
||||||
#ps_options{xdata = XData} -> get_xdata_fields(XData);
|
#ps_options{xdata = XData} ->
|
||||||
_ -> []
|
decode_subscribe_options(XData, Lang);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
end,
|
end,
|
||||||
subscribe_node(Host, Node, From, JID, Config);
|
case Config of
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err;
|
||||||
|
_ ->
|
||||||
|
subscribe_node(Host, Node, From, JID, Config)
|
||||||
|
end;
|
||||||
{set, #pubsub{unsubscribe = #ps_unsubscribe{node = Node, jid = JID, subid = SubId},
|
{set, #pubsub{unsubscribe = #ps_unsubscribe{node = Node, jid = JID, subid = SubId},
|
||||||
_ = undefined}} ->
|
_ = undefined}} ->
|
||||||
unsubscribe_node(Host, Node, From, JID, SubId);
|
unsubscribe_node(Host, Node, From, JID, SubId);
|
||||||
@ -1262,7 +1281,12 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
|
|||||||
{set, #pubsub{options = #ps_options{node = Node, subid = SubId,
|
{set, #pubsub{options = #ps_options{node = Node, subid = SubId,
|
||||||
jid = JID, xdata = XData},
|
jid = JID, xdata = XData},
|
||||||
_ = undefined}} ->
|
_ = undefined}} ->
|
||||||
set_options(Host, Node, JID, SubId, get_xdata_fields(XData));
|
case decode_subscribe_options(XData, Lang) of
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err;
|
||||||
|
Config ->
|
||||||
|
set_options(Host, Node, JID, SubId, Config)
|
||||||
|
end;
|
||||||
{set, #pubsub{}} ->
|
{set, #pubsub{}} ->
|
||||||
{error, xmpp:err_bad_request()};
|
{error, xmpp:err_bad_request()};
|
||||||
_ ->
|
_ ->
|
||||||
@ -1284,8 +1308,12 @@ iq_pubsub_owner(Host, #iq{type = IQType, from = From,
|
|||||||
#xdata{type = cancel} ->
|
#xdata{type = cancel} ->
|
||||||
{result, #pubsub_owner{}};
|
{result, #pubsub_owner{}};
|
||||||
#xdata{type = submit} ->
|
#xdata{type = submit} ->
|
||||||
Config = get_xdata_fields(XData),
|
case decode_node_config(XData, Host, Lang) of
|
||||||
set_configure(Host, Node, From, Config, Lang);
|
{error, _} = Err ->
|
||||||
|
Err;
|
||||||
|
Config ->
|
||||||
|
set_configure(Host, Node, From, Config, Lang)
|
||||||
|
end;
|
||||||
#xdata{} ->
|
#xdata{} ->
|
||||||
{error, xmpp:err_bad_request(<<"Incorrect data form">>, Lang)}
|
{error, xmpp:err_bad_request(<<"Incorrect data form">>, Lang)}
|
||||||
end;
|
end;
|
||||||
@ -1318,19 +1346,20 @@ adhoc_request(Host, _ServerHost, Owner,
|
|||||||
send_pending_node_form(Host, Owner, Lang, Plugins);
|
send_pending_node_form(Host, Owner, Lang, Plugins);
|
||||||
adhoc_request(Host, _ServerHost, Owner,
|
adhoc_request(Host, _ServerHost, Owner,
|
||||||
#adhoc_command{node = ?NS_PUBSUB_GET_PENDING, lang = Lang,
|
#adhoc_command{node = ?NS_PUBSUB_GET_PENDING, lang = Lang,
|
||||||
action = execute, xdata = #xdata{} = XData},
|
action = execute, xdata = #xdata{} = XData} = Request,
|
||||||
_Access, _Plugins) ->
|
_Access, _Plugins) ->
|
||||||
Config = get_xdata_fields(XData),
|
case decode_get_pending(XData, Lang) of
|
||||||
case set_xoption(Host, Config, []) of
|
{error, _} = Err ->
|
||||||
XForm when is_list(XForm) ->
|
Err;
|
||||||
case lists:keysearch(node, 1, XForm) of
|
Config ->
|
||||||
{value, {_, Node}} ->
|
Node = proplists:get_value(node, Config),
|
||||||
send_pending_auth_events(Host, Node, Owner, Lang);
|
case send_pending_auth_events(Host, Node, Owner, Lang) of
|
||||||
false ->
|
ok ->
|
||||||
{error, extended_error(xmpp:err_bad_request(), err_invalid_payload())}
|
xmpp_util:make_adhoc_response(
|
||||||
end;
|
Request, #adhoc_command{action = completed});
|
||||||
Err ->
|
Err ->
|
||||||
Err
|
Err
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
adhoc_request(_Host, _ServerHost, _Owner,
|
adhoc_request(_Host, _ServerHost, _Owner,
|
||||||
#adhoc_command{action = cancel}, _Access, _Plugins) ->
|
#adhoc_command{action = cancel}, _Access, _Plugins) ->
|
||||||
@ -1353,12 +1382,9 @@ send_pending_node_form(Host, Owner, _Lang, Plugins) ->
|
|||||||
Ps ->
|
Ps ->
|
||||||
case get_pending_nodes(Host, Owner, Ps) of
|
case get_pending_nodes(Host, Owner, Ps) of
|
||||||
{ok, Nodes} ->
|
{ok, Nodes} ->
|
||||||
XOpts = [#xdata_option{value = Node} || Node <- Nodes],
|
|
||||||
XForm = #xdata{type = form,
|
XForm = #xdata{type = form,
|
||||||
fields = [#xdata_field{
|
fields = pubsub_get_pending:encode(
|
||||||
type = 'list-single',
|
[{node, Nodes}])},
|
||||||
var = <<"pubsub#node">>,
|
|
||||||
options = lists:usort(XOpts)}]},
|
|
||||||
#adhoc_command{status = executing, action = execute,
|
#adhoc_command{status = executing, action = execute,
|
||||||
xdata = XForm};
|
xdata = XForm};
|
||||||
Err ->
|
Err ->
|
||||||
@ -1423,24 +1449,11 @@ send_authorization_request(#pubsub_node{nodeid = {Host, Node},
|
|||||||
Subscriber) ->
|
Subscriber) ->
|
||||||
%% TODO: pass lang to this function
|
%% TODO: pass lang to this function
|
||||||
Lang = <<"en">>,
|
Lang = <<"en">>,
|
||||||
Fs = [#xdata_field{var = <<"FORM_TYPE">>,
|
Fs = pubsub_subscribe_authorization:encode(
|
||||||
type = hidden,
|
[{node, Node},
|
||||||
values = [?NS_PUBSUB_SUB_AUTH]},
|
{subscriber_jid, Subscriber},
|
||||||
#xdata_field{var = <<"pubsub#node">>,
|
{allow, false}],
|
||||||
type = 'text-single',
|
fun(T) -> translate:translate(Lang, T) end),
|
||||||
label = translate:translate(Lang, <<"Node ID">>),
|
|
||||||
values = [Node]},
|
|
||||||
#xdata_field{var = <<"pubsub#subscriber_jid">>,
|
|
||||||
type = 'jid-single',
|
|
||||||
label = translate:translate(Lang, <<"Subscriber Address">>),
|
|
||||||
values = [jid:to_string(Subscriber)]},
|
|
||||||
#xdata_field{var = <<"pubsub#allow">>,
|
|
||||||
type = boolean,
|
|
||||||
label = translate:translate(
|
|
||||||
Lang,
|
|
||||||
<<"Allow this Jabber ID to subscribe to "
|
|
||||||
"this pubsub node?">>),
|
|
||||||
values = [<<"false">>]}],
|
|
||||||
X = #xdata{type = form,
|
X = #xdata{type = form,
|
||||||
title = translate:translate(
|
title = translate:translate(
|
||||||
Lang, <<"PubSub subscriber request">>),
|
Lang, <<"PubSub subscriber request">>),
|
||||||
@ -1455,15 +1468,24 @@ send_authorization_request(#pubsub_node{nodeid = {Host, Node},
|
|||||||
ejabberd_router:route(service_jid(Host), jid:make(Owner), Stanza)
|
ejabberd_router:route(service_jid(Host), jid:make(Owner), Stanza)
|
||||||
end, node_owners_action(Host, Type, Nidx, O)).
|
end, node_owners_action(Host, Type, Nidx, O)).
|
||||||
|
|
||||||
-spec find_authorization_response(message()) -> undefined | xdata().
|
-spec find_authorization_response(message()) -> undefined |
|
||||||
|
pubsub_subscribe_authorization:result() |
|
||||||
|
{error, stanza_error()}.
|
||||||
find_authorization_response(Packet) ->
|
find_authorization_response(Packet) ->
|
||||||
case xmpp:get_subtag(Packet, #xdata{}) of
|
case xmpp:get_subtag(Packet, #xdata{}) of
|
||||||
#xdata{type = submit} = X ->
|
#xdata{type = cancel} ->
|
||||||
case xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X) of
|
undefined;
|
||||||
[?NS_PUBSUB_SUB_AUTH] -> X;
|
#xdata{type = submit, fields = Fs} ->
|
||||||
_ -> undefined
|
try pubsub_subscribe_authorization:decode(Fs) of
|
||||||
|
Result -> Result
|
||||||
|
catch _:{pubsub_subscribe_authorization, Why} ->
|
||||||
|
Lang = xmpp:get_lang(Packet),
|
||||||
|
Txt = pubsub_subscribe_authorization:format_error(Why),
|
||||||
|
{error, xmpp:err_bad_request(Txt, Lang)}
|
||||||
end;
|
end;
|
||||||
_ ->
|
#xdata{} ->
|
||||||
|
{error, xmpp:err_bad_request()};
|
||||||
|
false ->
|
||||||
undefined
|
undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -1477,43 +1499,33 @@ send_authorization_approval(Host, JID, SNode, Subscription) ->
|
|||||||
Stanza = #message{sub_els = [Event]},
|
Stanza = #message{sub_els = [Event]},
|
||||||
ejabberd_router:route(service_jid(Host), JID, Stanza).
|
ejabberd_router:route(service_jid(Host), JID, Stanza).
|
||||||
|
|
||||||
-spec handle_authorization_response(binary(), jid(), jid(), message(), xdata()) -> ok.
|
-spec handle_authorization_response(binary(), jid(), jid(), message(),
|
||||||
handle_authorization_response(Host, From, To, Packet, X) ->
|
pubsub_subscribe_authorization:result()) -> ok.
|
||||||
|
handle_authorization_response(Host, From, To, Packet, Response) ->
|
||||||
|
Node = proplists:get_value(node, Response),
|
||||||
|
Subscriber = proplists:get_value(subscriber_jid, Response),
|
||||||
|
Allow = proplists:get_value(allow, Response),
|
||||||
Lang = xmpp:get_lang(Packet),
|
Lang = xmpp:get_lang(Packet),
|
||||||
case {xmpp_util:get_xdata_values(<<"pubsub#node">>, X),
|
FromLJID = jid:tolower(jid:remove_resource(From)),
|
||||||
xmpp_util:get_xdata_values(<<"pubsub#subscriber_jid">>, X),
|
Action =
|
||||||
xmpp_util:get_xdata_values(<<"pubsub#allow">>, X)} of
|
fun(#pubsub_node{type = Type, id = Nidx, owners = O}) ->
|
||||||
{[Node], [SSubscriber], [SAllow]} ->
|
Owners = node_owners_call(Host, Type, Nidx, O),
|
||||||
FromLJID = jid:tolower(jid:remove_resource(From)),
|
case lists:member(FromLJID, Owners) of
|
||||||
Subscriber = jid:from_string(SSubscriber),
|
true ->
|
||||||
Allow = case SAllow of
|
{result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]),
|
||||||
<<"1">> -> true;
|
update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs);
|
||||||
<<"true">> -> true;
|
false ->
|
||||||
_ -> false
|
{error, xmpp:err_forbidden(<<"Owner privileges required">>, Lang)}
|
||||||
end,
|
end
|
||||||
Action =
|
end,
|
||||||
fun(#pubsub_node{type = Type, id = Nidx, owners = O}) ->
|
case transaction(Host, Node, Action, sync_dirty) of
|
||||||
Owners = node_owners_call(Host, Type, Nidx, O),
|
{error, Error} ->
|
||||||
case lists:member(FromLJID, Owners) of
|
ejabberd_router:route_error(To, From, Packet, Error);
|
||||||
true ->
|
{result, {_, _NewSubscription}} ->
|
||||||
{result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]),
|
%% XXX: notify about subscription state change, section 12.11
|
||||||
update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs);
|
ok;
|
||||||
false ->
|
|
||||||
{error, xmpp:err_forbidden(<<"Owner privileges required">>, Lang)}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
case transaction(Host, Node, Action, sync_dirty) of
|
|
||||||
{error, Error} ->
|
|
||||||
ejabberd_router:route_error(To, From, Packet, Error);
|
|
||||||
{result, {_, _NewSubscription}} ->
|
|
||||||
%% XXX: notify about subscription state change, section 12.11
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
Err = xmpp:err_internal_server_error(),
|
|
||||||
ejabberd_router:route_error(To, From, Packet, Err)
|
|
||||||
end;
|
|
||||||
_ ->
|
_ ->
|
||||||
Err = xmpp:err_not_acceptable(<<"Incorrect data form">>, Lang),
|
Err = xmpp:err_internal_server_error(),
|
||||||
ejabberd_router:route_error(To, From, Packet, Err)
|
ejabberd_router:route_error(To, From, Packet, Err)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -1539,45 +1551,6 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
|
|||||||
{error, xmpp:err_unexpected_request(Txt, ?MYLANG)}
|
{error, xmpp:err_unexpected_request(Txt, ?MYLANG)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-define(XFIELD(Type, Label, Var, Val),
|
|
||||||
#xdata_field{type = Type,
|
|
||||||
label = translate:translate(Lang, Label),
|
|
||||||
var = Var,
|
|
||||||
values = [Val]}).
|
|
||||||
|
|
||||||
-define(BOOLXFIELD(Label, Var, Val),
|
|
||||||
?XFIELD(boolean, Label, Var,
|
|
||||||
case Val of
|
|
||||||
true -> <<"1">>;
|
|
||||||
_ -> <<"0">>
|
|
||||||
end)).
|
|
||||||
|
|
||||||
-define(STRINGXFIELD(Label, Var, Val),
|
|
||||||
?XFIELD('text-single', Label, Var, Val)).
|
|
||||||
|
|
||||||
-define(STRINGMXFIELD(Label, Var, Vals),
|
|
||||||
#xdata_field{type = 'text-multi',
|
|
||||||
label = translate:translate(Lang, Label),
|
|
||||||
var = Var,
|
|
||||||
values = Vals}).
|
|
||||||
|
|
||||||
-define(XFIELDOPT(Type, Label, Var, Val, Opts),
|
|
||||||
#xdata_field{type = Type,
|
|
||||||
label = translate:translate(Lang, Label),
|
|
||||||
var = Var,
|
|
||||||
options = [#xdata_option{value = Opt} || Opt <- Opts],
|
|
||||||
values = [Val]}).
|
|
||||||
|
|
||||||
-define(LISTXFIELD(Label, Var, Val, Opts),
|
|
||||||
?XFIELDOPT('list-single', Label, Var, Val, Opts)).
|
|
||||||
|
|
||||||
-define(LISTMXFIELD(Label, Var, Vals, Opts),
|
|
||||||
#xdata_field{type = 'list-multi',
|
|
||||||
label = translate:translate(Lang, Label),
|
|
||||||
var = Var,
|
|
||||||
options = [#xdata_option{value = Opt} || Opt <- Opts],
|
|
||||||
values = Vals}).
|
|
||||||
|
|
||||||
%% @doc <p>Create new pubsub nodes</p>
|
%% @doc <p>Create new pubsub nodes</p>
|
||||||
%%<p>In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:</p>
|
%%<p>In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:</p>
|
||||||
%%<ul>
|
%%<ul>
|
||||||
@ -1617,70 +1590,66 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
|
|||||||
end;
|
end;
|
||||||
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||||
Type = select_type(ServerHost, Host, Node, GivenType),
|
Type = select_type(ServerHost, Host, Node, GivenType),
|
||||||
case set_xoption(Host, Configuration, node_options(Host, Type)) of
|
NodeOptions = merge_config(Configuration, node_options(Host, Type)),
|
||||||
NodeOptions when is_list(NodeOptions) ->
|
CreateNode =
|
||||||
CreateNode =
|
fun() ->
|
||||||
fun() ->
|
Parent = case node_call(Host, Type, node_to_path, [Node]) of
|
||||||
Parent = case node_call(Host, Type, node_to_path, [Node]) of
|
{result, [Node]} ->
|
||||||
{result, [Node]} ->
|
<<>>;
|
||||||
<<>>;
|
{result, Path} ->
|
||||||
{result, Path} ->
|
element(2, node_call(Host, Type, path_to_node,
|
||||||
element(2, node_call(Host, Type, path_to_node,
|
[lists:sublist(Path, length(Path)-1)]))
|
||||||
[lists:sublist(Path, length(Path)-1)]))
|
end,
|
||||||
end,
|
Parents = case Parent of
|
||||||
Parents = case Parent of
|
<<>> -> [];
|
||||||
<<>> -> [];
|
_ -> [Parent]
|
||||||
_ -> [Parent]
|
end,
|
||||||
end,
|
case node_call(Host, Type, create_node_permission,
|
||||||
case node_call(Host, Type, create_node_permission,
|
[Host, ServerHost, Node, Parent, Owner, Access]) of
|
||||||
[Host, ServerHost, Node, Parent, Owner, Access]) of
|
{result, true} ->
|
||||||
{result, true} ->
|
case tree_call(Host, create_node,
|
||||||
case tree_call(Host, create_node,
|
[Host, Node, Type, Owner, NodeOptions, Parents])
|
||||||
[Host, Node, Type, Owner, NodeOptions, Parents])
|
of
|
||||||
of
|
{ok, Nidx} ->
|
||||||
{ok, Nidx} ->
|
SubsByDepth = get_node_subs_by_depth(Host, Node, Owner),
|
||||||
SubsByDepth = get_node_subs_by_depth(Host, Node, Owner),
|
case node_call(Host, Type, create_node, [Nidx, Owner]) of
|
||||||
case node_call(Host, Type, create_node, [Nidx, Owner]) of
|
{result, Result} -> {result, {Nidx, SubsByDepth, Result}};
|
||||||
{result, Result} -> {result, {Nidx, SubsByDepth, Result}};
|
Error -> Error
|
||||||
Error -> Error
|
|
||||||
end;
|
|
||||||
{error, {virtual, Nidx}} ->
|
|
||||||
case node_call(Host, Type, create_node, [Nidx, Owner]) of
|
|
||||||
{result, Result} -> {result, {Nidx, [], Result}};
|
|
||||||
Error -> Error
|
|
||||||
end;
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end;
|
end;
|
||||||
_ ->
|
{error, {virtual, Nidx}} ->
|
||||||
Txt = <<"You're not allowed to create nodes">>,
|
case node_call(Host, Type, create_node, [Nidx, Owner]) of
|
||||||
{error, xmpp:err_forbidden(Txt, ?MYLANG)}
|
{result, Result} -> {result, {Nidx, [], Result}};
|
||||||
end
|
Error -> Error
|
||||||
end,
|
end;
|
||||||
Reply = #pubsub{create = Node},
|
Error ->
|
||||||
case transaction(Host, CreateNode, transaction) of
|
Error
|
||||||
{result, {Nidx, SubsByDepth, {Result, broadcast}}} ->
|
end;
|
||||||
broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth),
|
_ ->
|
||||||
ejabberd_hooks:run(pubsub_create_node, ServerHost,
|
Txt = <<"You're not allowed to create nodes">>,
|
||||||
[ServerHost, Host, Node, Nidx, NodeOptions]),
|
{error, xmpp:err_forbidden(Txt, ?MYLANG)}
|
||||||
case Result of
|
end
|
||||||
default -> {result, Reply};
|
end,
|
||||||
_ -> {result, Result}
|
Reply = #pubsub{create = Node},
|
||||||
end;
|
case transaction(Host, CreateNode, transaction) of
|
||||||
{result, {Nidx, _SubsByDepth, Result}} ->
|
{result, {Nidx, SubsByDepth, {Result, broadcast}}} ->
|
||||||
ejabberd_hooks:run(pubsub_create_node, ServerHost,
|
broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth),
|
||||||
[ServerHost, Host, Node, Nidx, NodeOptions]),
|
ejabberd_hooks:run(pubsub_create_node, ServerHost,
|
||||||
case Result of
|
[ServerHost, Host, Node, Nidx, NodeOptions]),
|
||||||
default -> {result, Reply};
|
case Result of
|
||||||
_ -> {result, Result}
|
default -> {result, Reply};
|
||||||
end;
|
_ -> {result, Result}
|
||||||
Error ->
|
end;
|
||||||
%% in case we change transaction to sync_dirty...
|
{result, {Nidx, _SubsByDepth, Result}} ->
|
||||||
%% node_call(Host, Type, delete_node, [Host, Node]),
|
ejabberd_hooks:run(pubsub_create_node, ServerHost,
|
||||||
%% tree_call(Host, delete_node, [Host, Node]),
|
[ServerHost, Host, Node, Nidx, NodeOptions]),
|
||||||
Error
|
case Result of
|
||||||
|
default -> {result, Reply};
|
||||||
|
_ -> {result, Result}
|
||||||
end;
|
end;
|
||||||
Error ->
|
Error ->
|
||||||
|
%% in case we change transaction to sync_dirty...
|
||||||
|
%% node_call(Host, Type, delete_node, [Host, Node]),
|
||||||
|
%% tree_call(Host, delete_node, [Host, Node]),
|
||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -2636,7 +2605,7 @@ set_subscriptions(Host, Node, From, Entities) ->
|
|||||||
Owner = jid:tolower(jid:remove_resource(From)),
|
Owner = jid:tolower(jid:remove_resource(From)),
|
||||||
Notify = fun(#ps_subscription{jid = JID, type = Sub}) ->
|
Notify = fun(#ps_subscription{jid = JID, type = Sub}) ->
|
||||||
Stanza = #message{
|
Stanza = #message{
|
||||||
sub_els = [#pubsub{
|
sub_els = [#ps_event{
|
||||||
subscription = #ps_subscription{
|
subscription = #ps_subscription{
|
||||||
jid = JID,
|
jid = JID,
|
||||||
type = Sub,
|
type = Sub,
|
||||||
@ -3266,83 +3235,17 @@ max_items(Host, Options) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-define(BOOL_CONFIG_FIELD(Label, Var),
|
-spec get_configure_xfields(_, pubsub_node_config:result(),
|
||||||
?BOOLXFIELD(Label,
|
binary(), [binary()]) -> [xdata_field()].
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(get_option(Options, Var)))).
|
|
||||||
|
|
||||||
-define(STRING_CONFIG_FIELD(Label, Var),
|
|
||||||
?STRINGXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(get_option(Options, Var, <<>>)))).
|
|
||||||
|
|
||||||
-define(INTEGER_CONFIG_FIELD(Label, Var),
|
|
||||||
?STRINGXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(integer_to_binary(get_option(Options, Var))))).
|
|
||||||
|
|
||||||
-define(JLIST_CONFIG_FIELD(Label, Var, Opts),
|
|
||||||
?LISTXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(jid:to_string(get_option(Options, Var))),
|
|
||||||
[jid:to_string(O) || O <- Opts])).
|
|
||||||
|
|
||||||
-define(ALIST_CONFIG_FIELD(Label, Var, Opts),
|
|
||||||
?LISTXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(atom_to_binary(get_option(Options, Var), latin1)),
|
|
||||||
[atom_to_binary(O, latin1) || O <- Opts])).
|
|
||||||
|
|
||||||
-define(LISTM_CONFIG_FIELD(Label, Var, Opts),
|
|
||||||
?LISTMXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
(get_option(Options, Var)), Opts)).
|
|
||||||
|
|
||||||
-define(NLIST_CONFIG_FIELD(Label, Var),
|
|
||||||
?STRINGMXFIELD(Label,
|
|
||||||
<<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
|
|
||||||
get_option(Options, Var, []))).
|
|
||||||
|
|
||||||
get_configure_xfields(_Type, Options, Lang, Groups) ->
|
get_configure_xfields(_Type, Options, Lang, Groups) ->
|
||||||
[?XFIELD(hidden, <<>>, <<"FORM_TYPE">>, ?NS_PUBSUB_NODE_CONFIG),
|
pubsub_node_config:encode(
|
||||||
?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
|
lists:map(
|
||||||
deliver_payloads),
|
fun({roster_groups_allowed, Value}) ->
|
||||||
?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>,
|
{roster_groups_allowed, Value, Groups};
|
||||||
deliver_notifications),
|
(Opt) ->
|
||||||
?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node configuration changes">>,
|
Opt
|
||||||
notify_config),
|
end, Options),
|
||||||
?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node is deleted">>,
|
fun(Txt) -> translate:translate(Lang, Txt) end).
|
||||||
notify_delete),
|
|
||||||
?BOOL_CONFIG_FIELD(<<"Notify subscribers when items are removed from the node">>,
|
|
||||||
notify_retract),
|
|
||||||
?BOOL_CONFIG_FIELD(<<"Persist items to storage">>,
|
|
||||||
persist_items),
|
|
||||||
?STRING_CONFIG_FIELD(<<"A friendly name for the node">>,
|
|
||||||
title),
|
|
||||||
?INTEGER_CONFIG_FIELD(<<"Max # of items to persist">>,
|
|
||||||
max_items),
|
|
||||||
?BOOL_CONFIG_FIELD(<<"Whether to allow subscriptions">>,
|
|
||||||
subscribe),
|
|
||||||
?ALIST_CONFIG_FIELD(<<"Specify the access model">>,
|
|
||||||
access_model, [open, authorize, presence, roster, whitelist]),
|
|
||||||
?LISTM_CONFIG_FIELD(<<"Roster groups allowed to subscribe">>,
|
|
||||||
roster_groups_allowed, Groups),
|
|
||||||
?ALIST_CONFIG_FIELD(<<"Specify the publisher model">>,
|
|
||||||
publish_model, [publishers, subscribers, open]),
|
|
||||||
?BOOL_CONFIG_FIELD(<<"Purge all items when the relevant publisher goes offline">>,
|
|
||||||
purge_offline),
|
|
||||||
?ALIST_CONFIG_FIELD(<<"Specify the event message type">>,
|
|
||||||
notification_type, [headline, normal]),
|
|
||||||
?INTEGER_CONFIG_FIELD(<<"Max payload size in bytes">>,
|
|
||||||
max_payload_size),
|
|
||||||
?ALIST_CONFIG_FIELD(<<"When to send the last published item">>,
|
|
||||||
send_last_published_item, [never, on_sub, on_sub_and_presence]),
|
|
||||||
?BOOL_CONFIG_FIELD(<<"Only deliver notifications to available users">>,
|
|
||||||
presence_based_delivery),
|
|
||||||
?NLIST_CONFIG_FIELD(<<"The collections with which a node is affiliated">>,
|
|
||||||
collection),
|
|
||||||
?ALIST_CONFIG_FIELD(<<"Whether owners or publisher should receive replies to items">>,
|
|
||||||
itemreply, [none, owner, publisher])].
|
|
||||||
|
|
||||||
%%<p>There are several reasons why the node configuration request might fail:</p>
|
%%<p>There are several reasons why the node configuration request might fail:</p>
|
||||||
%%<ul>
|
%%<ul>
|
||||||
@ -3365,18 +3268,13 @@ set_configure(Host, Node, From, Config, Lang) ->
|
|||||||
[] -> node_options(Host, Type);
|
[] -> node_options(Host, Type);
|
||||||
_ -> Options
|
_ -> Options
|
||||||
end,
|
end,
|
||||||
case set_xoption(Host, Config, OldOpts) of
|
NewOpts = merge_config(Config, OldOpts),
|
||||||
NewOpts when is_list(NewOpts) ->
|
case tree_call(Host,
|
||||||
case tree_call(Host,
|
set_node,
|
||||||
set_node,
|
[N#pubsub_node{options = NewOpts}]) of
|
||||||
[N#pubsub_node{options = NewOpts}])
|
{result, Nidx} -> {result, ok};
|
||||||
of
|
ok -> {result, ok};
|
||||||
{result, Nidx} -> {result, ok};
|
Err -> Err
|
||||||
ok -> {result, ok};
|
|
||||||
Err -> Err
|
|
||||||
end;
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, xmpp:err_forbidden(
|
{error, xmpp:err_forbidden(
|
||||||
@ -3394,119 +3292,82 @@ set_configure(Host, Node, From, Config, Lang) ->
|
|||||||
Other
|
Other
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec add_opt(atom(), any(), [{atom(), any()}]) -> [{atom(), any()}].
|
-spec merge_config([proplists:property()], [proplists:property()]) -> [proplists:property()].
|
||||||
add_opt(Key, Value, Opts) ->
|
merge_config(Config1, Config2) ->
|
||||||
lists:keystore(Key, 1, Opts, {Key, Value}).
|
lists:foldl(
|
||||||
|
fun({Opt, Val}, Acc) ->
|
||||||
|
lists:keystore(Opt, 1, Acc, {Opt, Val})
|
||||||
|
end, Config2, Config1).
|
||||||
|
|
||||||
-define(SET_BOOL_XOPT(Opt, Val),
|
-spec decode_node_config(undefined | xdata(), binary(), binary()) ->
|
||||||
BoolVal = case Val of
|
pubsub_node_config:result() |
|
||||||
<<"0">> -> false;
|
{error, stanza_error()}.
|
||||||
<<"1">> -> true;
|
decode_node_config(undefined, _, _) ->
|
||||||
<<"false">> -> false;
|
|
||||||
<<"true">> -> true;
|
|
||||||
_ -> error
|
|
||||||
end,
|
|
||||||
case BoolVal of
|
|
||||||
error ->
|
|
||||||
Txt = <<"Value of '~s' should be boolean">>,
|
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
|
||||||
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)};
|
|
||||||
_ -> set_xoption(Host, Opts, add_opt(Opt, BoolVal, NewOpts))
|
|
||||||
end).
|
|
||||||
|
|
||||||
-define(SET_STRING_XOPT(Opt, Val),
|
|
||||||
set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
|
|
||||||
|
|
||||||
-define(SET_INTEGER_XOPT(Opt, Val, Min, Max),
|
|
||||||
case catch binary_to_integer(Val) of
|
|
||||||
IVal when is_integer(IVal), IVal >= Min ->
|
|
||||||
if (Max =:= undefined) orelse (IVal =< Max) ->
|
|
||||||
set_xoption(Host, Opts, add_opt(Opt, IVal, NewOpts));
|
|
||||||
true ->
|
|
||||||
Txt = <<"Incorrect value of '~s'">>,
|
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
|
||||||
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
Txt = <<"Value of '~s' should be integer">>,
|
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
|
||||||
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
|
||||||
end).
|
|
||||||
|
|
||||||
-define(SET_ALIST_XOPT(Opt, Val, Vals),
|
|
||||||
case lists:member(Val, [atom_to_binary(V, latin1) || V <- Vals]) of
|
|
||||||
true ->
|
|
||||||
set_xoption(Host, Opts, add_opt(Opt, jlib:binary_to_atom(Val), NewOpts));
|
|
||||||
false ->
|
|
||||||
Txt = <<"Incorrect value of '~s'">>,
|
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
|
||||||
{error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}
|
|
||||||
end).
|
|
||||||
|
|
||||||
-define(SET_LIST_XOPT(Opt, Val),
|
|
||||||
set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
|
|
||||||
|
|
||||||
-spec set_xoption(host(), [{binary(), [binary()]}], [{atom(), any()}]) -> [{atom(), any()}].
|
|
||||||
set_xoption(_Host, [], NewOpts) -> NewOpts;
|
|
||||||
set_xoption(Host, [{<<"FORM_TYPE">>, _} | Opts], NewOpts) ->
|
|
||||||
set_xoption(Host, Opts, NewOpts);
|
|
||||||
set_xoption(Host, [{<<"pubsub#roster_groups_allowed">>, Value} | Opts], NewOpts) ->
|
|
||||||
?SET_LIST_XOPT(roster_groups_allowed, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#deliver_payloads">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(deliver_payloads, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#deliver_notifications">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(deliver_notifications, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#notify_config">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(notify_config, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#notify_delete">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(notify_delete, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#notify_retract">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(notify_retract, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#persist_items">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(persist_items, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#max_items">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
MaxItems = get_max_items_node(Host),
|
|
||||||
?SET_INTEGER_XOPT(max_items, Val, 0, MaxItems);
|
|
||||||
set_xoption(Host, [{<<"pubsub#subscribe">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(subscribe, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#access_model">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(access_model, Val, [open, authorize, presence, roster, whitelist]);
|
|
||||||
set_xoption(Host, [{<<"pubsub#publish_model">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(publish_model, Val, [publishers, subscribers, open]);
|
|
||||||
set_xoption(Host, [{<<"pubsub#notification_type">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(notification_type, Val, [headline, normal]);
|
|
||||||
set_xoption(Host, [{<<"pubsub#node_type">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(node_type, Val, [leaf, collection]);
|
|
||||||
set_xoption(Host, [{<<"pubsub#max_payload_size">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_INTEGER_XOPT(max_payload_size, Val, 0, (?MAX_PAYLOAD_SIZE));
|
|
||||||
set_xoption(Host, [{<<"pubsub#send_last_published_item">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(send_last_published_item, Val, [never, on_sub, on_sub_and_presence]);
|
|
||||||
set_xoption(Host, [{<<"pubsub#presence_based_delivery">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(presence_based_delivery, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#purge_offline">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_BOOL_XOPT(purge_offline, Val);
|
|
||||||
set_xoption(Host, [{<<"pubsub#title">>, Value} | Opts], NewOpts) ->
|
|
||||||
?SET_STRING_XOPT(title, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#type">>, Value} | Opts], NewOpts) ->
|
|
||||||
?SET_STRING_XOPT(type, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#body_xslt">>, Value} | Opts], NewOpts) ->
|
|
||||||
?SET_STRING_XOPT(body_xslt, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#collection">>, Value} | Opts], NewOpts) ->
|
|
||||||
% NewValue = [string_to_node(V) || V <- Value],
|
|
||||||
?SET_LIST_XOPT(collection, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#node">>, [Value]} | Opts], NewOpts) ->
|
|
||||||
% NewValue = string_to_node(Value),
|
|
||||||
?SET_LIST_XOPT(node, Value);
|
|
||||||
set_xoption(Host, [{<<"pubsub#itemreply">>, [Val]} | Opts], NewOpts) ->
|
|
||||||
?SET_ALIST_XOPT(itemreply, Val, [none, owner, publisher]);
|
|
||||||
set_xoption(Host, [_ | Opts], NewOpts) ->
|
|
||||||
set_xoption(Host, Opts, NewOpts).
|
|
||||||
|
|
||||||
-spec get_xdata_fields(undefined | xdata()) -> [{binary(), [binary()]}].
|
|
||||||
get_xdata_fields(undefined) ->
|
|
||||||
[];
|
[];
|
||||||
get_xdata_fields(#xdata{fields = Fs}) ->
|
decode_node_config(#xdata{fields = Fs}, Host, Lang) ->
|
||||||
[{Var, Vals} || #xdata_field{var = Var, values = Vals} <- Fs].
|
try
|
||||||
|
Config = pubsub_node_config:decode(Fs),
|
||||||
|
Max = get_max_items_node(Host),
|
||||||
|
case {check_opt_range(max_items, Config, Max),
|
||||||
|
check_opt_range(max_payload_size, Config, ?MAX_PAYLOAD_SIZE)} of
|
||||||
|
{true, true} ->
|
||||||
|
Config;
|
||||||
|
{true, false} ->
|
||||||
|
erlang:error(
|
||||||
|
{pubsub_node_config,
|
||||||
|
{bad_var_value, <<"pubsub#max_payload_size">>,
|
||||||
|
?NS_PUBSUB_NODE_CONFIG}});
|
||||||
|
{false, _} ->
|
||||||
|
erlang:error(
|
||||||
|
{pubsub_node_config,
|
||||||
|
{bad_var_value, <<"pubsub#max_items">>,
|
||||||
|
?NS_PUBSUB_NODE_CONFIG}})
|
||||||
|
end
|
||||||
|
catch _:{pubsub_node_config, Why} ->
|
||||||
|
Txt = pubsub_node_config:format_error(Why),
|
||||||
|
{error, xmpp:err_resource_constraint(Txt, Lang)}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec decode_subscribe_options(undefined | xdata(), binary()) ->
|
||||||
|
pubsub_subscribe_options:result() |
|
||||||
|
{error, stanza_error()}.
|
||||||
|
decode_subscribe_options(undefined, _) ->
|
||||||
|
[];
|
||||||
|
decode_subscribe_options(#xdata{fields = Fs}, Lang) ->
|
||||||
|
try pubsub_subscribe_options:decode(Fs)
|
||||||
|
catch _:{pubsub_subscribe_options, Why} ->
|
||||||
|
Txt = pubsub_subscribe_options:format_error(Why),
|
||||||
|
{error, xmpp:err_resource_constraint(Txt, Lang)}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec decode_publish_options(undefined | xdata(), binary()) ->
|
||||||
|
pubsub_publish_options:result() |
|
||||||
|
{error, stanza_error()}.
|
||||||
|
decode_publish_options(undefined, _) ->
|
||||||
|
[];
|
||||||
|
decode_publish_options(#xdata{fields = Fs}, Lang) ->
|
||||||
|
try pubsub_publish_options:decode(Fs)
|
||||||
|
catch _:{pubsub_publish_options, Why} ->
|
||||||
|
Txt = pubsub_publish_options:format_error(Why),
|
||||||
|
{error, xmpp:err_resource_constraint(Txt, Lang)}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec decode_get_pending(xdata(), binary()) ->
|
||||||
|
pubsub_get_pending:result() |
|
||||||
|
{error, stanza_error()}.
|
||||||
|
decode_get_pending(#xdata{fields = Fs}, Lang) ->
|
||||||
|
try pubsub_get_pending:decode(Fs)
|
||||||
|
catch _:{pubsub_get_pending, Why} ->
|
||||||
|
Txt = pubsub_get_pending:format_error(Why),
|
||||||
|
{error, xmpp:err_resource_constraint(Txt, Lang)}
|
||||||
|
end;
|
||||||
|
decode_get_pending(undefined, Lang) ->
|
||||||
|
{error, xmpp:err_bad_request(<<"No data form found">>, Lang)}.
|
||||||
|
|
||||||
|
-spec check_opt_range(atom(), [proplists:property()], non_neg_integer()) -> boolean().
|
||||||
|
check_opt_range(Opt, Opts, Max) ->
|
||||||
|
Val = proplists:get_value(Opt, Opts, Max),
|
||||||
|
Val =< Max.
|
||||||
|
|
||||||
-spec get_max_items_node(host()) -> undefined | non_neg_integer().
|
-spec get_max_items_node(host()) -> undefined | non_neg_integer().
|
||||||
get_max_items_node(Host) ->
|
get_max_items_node(Host) ->
|
||||||
|
364
src/muc_register.erl
Normal file
364
src/muc_register.erl
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
%% Created automatically by xdata generator (xdata_codec.erl)
|
||||||
|
%% Source: muc_register.xdata
|
||||||
|
%% Form type: http://jabber.org/protocol/muc#register
|
||||||
|
%% Document: XEP-0045
|
||||||
|
|
||||||
|
-module(muc_register).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("muc_register.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"0">>.
|
||||||
|
|
||||||
|
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, [<<"muc#register_roomnick">>]);
|
||||||
|
#xdata_field{values =
|
||||||
|
[<<"http://jabber.org/protocol/muc#register">>]} ->
|
||||||
|
decode(Fs, Acc, [<<"muc#register_roomnick">>]);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{allow, Val} -> [encode_allow(Val, Translate)];
|
||||||
|
{allow, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{email, Val} -> [encode_email(Val, Translate)];
|
||||||
|
{email, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{faqentry, Val} -> [encode_faqentry(Val, Translate)];
|
||||||
|
{faqentry, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{first, Val} -> [encode_first(Val, Translate)];
|
||||||
|
{first, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{last, Val} -> [encode_last(Val, Translate)];
|
||||||
|
{last, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{roomnick, Val} -> [encode_roomnick(Val, Translate)];
|
||||||
|
{roomnick, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{url, Val} -> [encode_url(Val, Translate)];
|
||||||
|
{url, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/muc#register">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"muc#register_allow">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{allow, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_allow">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_allow">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_allow">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_allow">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_allow">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_allow">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#register_email">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{email, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_email">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_email">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_email">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_email">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_email">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_email">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#register_faqentry">>,
|
||||||
|
values = Values}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try [Value || Value <- Values] of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{faqentry, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_faqentry">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_faqentry">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_first">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{first, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_first">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_first">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_first">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_first">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_first">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_first">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#register_last">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{last, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_last">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_last">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_last">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_last">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_last">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_last">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#register_roomnick">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{roomnick, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_roomnick">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_roomnick">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_roomnick">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_roomnick">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_roomnick">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_roomnick">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#register_url">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{url, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#register_url">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#register_url">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#register_url">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#register_url">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#register_url">>} | _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#register_url">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#register">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_allow(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_allow">>,
|
||||||
|
values = Values, required = false, type = boolean,
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Allow this person to register with the "
|
||||||
|
"room?">>)}.
|
||||||
|
|
||||||
|
encode_email(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_email">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Email Address">>)}.
|
||||||
|
|
||||||
|
encode_faqentry(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
[] -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_faqentry">>,
|
||||||
|
values = Values, required = false, type = 'text-multi',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"FAQ Entry">>)}.
|
||||||
|
|
||||||
|
encode_first(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_first">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Given Name">>)}.
|
||||||
|
|
||||||
|
encode_last(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_last">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Family Name">>)}.
|
||||||
|
|
||||||
|
encode_roomnick(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_roomnick">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Nickname">>)}.
|
||||||
|
|
||||||
|
encode_url(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#register_url">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"A Web Page">>)}.
|
269
src/muc_request.erl
Normal file
269
src/muc_request.erl
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
%% Created automatically by xdata generator (xdata_codec.erl)
|
||||||
|
%% Source: muc_request.xdata
|
||||||
|
%% Form type: http://jabber.org/protocol/muc#request
|
||||||
|
%% Document: XEP-0045
|
||||||
|
|
||||||
|
-module(muc_request).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("muc_request.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
dec_enum(Val, Enums) ->
|
||||||
|
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
|
||||||
|
case lists:member(AtomVal, Enums) of
|
||||||
|
true -> AtomVal
|
||||||
|
end.
|
||||||
|
|
||||||
|
enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"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, [<<"muc#role">>]);
|
||||||
|
#xdata_field{values =
|
||||||
|
[<<"http://jabber.org/protocol/muc#request">>]} ->
|
||||||
|
decode(Fs, Acc, [<<"muc#role">>]);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{role, Val} -> [encode_role(Val, default, Translate)];
|
||||||
|
{role, Val, Opts} ->
|
||||||
|
[encode_role(Val, Opts, Translate)];
|
||||||
|
{jid, Val} -> [encode_jid(Val, Translate)];
|
||||||
|
{jid, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{roomnick, Val} -> [encode_roomnick(Val, Translate)];
|
||||||
|
{roomnick, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{request_allow, Val} ->
|
||||||
|
[encode_request_allow(Val, Translate)];
|
||||||
|
{request_allow, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/muc#request">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"muc#role">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_enum(Value, [participant]) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{role, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#role">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#role">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#role">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#role">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#role">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#role">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#jid">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_jid(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{jid, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#jid">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#jid">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#jid">>, values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#jid">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#jid">>} | _], _, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#jid">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roomnick">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{roomnick, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roomnick">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roomnick">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roomnick">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#roomnick">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roomnick">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roomnick">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#request_allow">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{request_allow, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#request_allow">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#request_allow">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#request_allow">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#request_allow">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#request_allow">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#request_allow">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#request">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_role(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_enum(Value)]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default ->
|
||||||
|
[#xdata_option{label = Translate(<<"Participant">>),
|
||||||
|
value = <<"participant">>}];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L),
|
||||||
|
value = enc_enum(V)}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"muc#role">>, values = Values,
|
||||||
|
required = false, type = 'list-single', options = Opts,
|
||||||
|
desc = <<>>, label = Translate(<<"Requested role">>)}.
|
||||||
|
|
||||||
|
encode_jid(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_jid(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#jid">>, values = Values,
|
||||||
|
required = false, type = 'jid-single', options = Opts,
|
||||||
|
desc = <<>>, label = Translate(<<"User JID">>)}.
|
||||||
|
|
||||||
|
encode_roomnick(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roomnick">>, values = Values,
|
||||||
|
required = false, type = 'text-single', options = Opts,
|
||||||
|
desc = <<>>, label = Translate(<<"Nickname">>)}.
|
||||||
|
|
||||||
|
encode_request_allow(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#request_allow">>,
|
||||||
|
values = Values, required = false, type = boolean,
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Grant voice to this person?">>)}.
|
1675
src/muc_roomconfig.erl
Normal file
1675
src/muc_roomconfig.erl
Normal file
File diff suppressed because it is too large
Load Diff
491
src/muc_roominfo.erl
Normal file
491
src/muc_roominfo.erl
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
%% Created automatically by xdata generator (xdata_codec.erl)
|
||||||
|
%% Source: muc_roominfo.xdata
|
||||||
|
%% Form type: http://jabber.org/protocol/muc#roominfo
|
||||||
|
%% Document: XEP-0045
|
||||||
|
|
||||||
|
-module(muc_roominfo).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("muc_roominfo.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).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"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 =
|
||||||
|
[<<"http://jabber.org/protocol/muc#roominfo">>]} ->
|
||||||
|
decode(Fs, Acc, []);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{maxhistoryfetch, Val} ->
|
||||||
|
[encode_maxhistoryfetch(Val, Translate)];
|
||||||
|
{maxhistoryfetch, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{contactjid, Val} ->
|
||||||
|
[encode_contactjid(Val, Translate)];
|
||||||
|
{contactjid, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{description, Val} ->
|
||||||
|
[encode_description(Val, Translate)];
|
||||||
|
{description, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{lang, Val} -> [encode_lang(Val, Translate)];
|
||||||
|
{lang, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{ldapgroup, Val} -> [encode_ldapgroup(Val, Translate)];
|
||||||
|
{ldapgroup, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{logs, Val} -> [encode_logs(Val, Translate)];
|
||||||
|
{logs, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{occupants, Val} -> [encode_occupants(Val, Translate)];
|
||||||
|
{occupants, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{subject, Val} -> [encode_subject(Val, Translate)];
|
||||||
|
{subject, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{subjectmod, Val} ->
|
||||||
|
[encode_subjectmod(Val, Translate)];
|
||||||
|
{subjectmod, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/muc#roominfo">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"muc#maxhistoryfetch">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_int(Value, 0, infinity) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{maxhistoryfetch, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#maxhistoryfetch">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#maxhistoryfetch">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#maxhistoryfetch">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#maxhistoryfetch">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#maxhistoryfetch">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#maxhistoryfetch">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_contactjid">>,
|
||||||
|
values = Values}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try [dec_jid(Value) || Value <- Values] of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{contactjid, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_contactjid">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_contactjid">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_description">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{description, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_description">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_description">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_description">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"muc#roominfo_description">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_description">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_description">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_lang">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{lang, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_lang">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_lang">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_lang">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#roominfo_lang">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_lang">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_lang">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_ldapgroup">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{ldapgroup, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_ldapgroup">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_ldapgroup">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_ldapgroup">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"muc#roominfo_ldapgroup">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_ldapgroup">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_ldapgroup">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_logs">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{logs, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_logs">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_logs">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_logs">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#roominfo_logs">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_logs">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_logs">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_occupants">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_int(Value, 0, infinity) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{occupants, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_occupants">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_occupants">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_occupants">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"muc#roominfo_occupants">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_occupants">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_occupants">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_subject">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subject, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_subject">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_subject">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_subject">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"muc#roominfo_subject">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"muc#roominfo_subject">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_subject">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_subjectmod">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subjectmod, Result} | Acc],
|
||||||
|
lists:delete(<<"muc#roominfo_subjectmod">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"muc#roominfo_subjectmod">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_subjectmod">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"muc#roominfo_subjectmod">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"muc#roominfo_subjectmod">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"muc#roominfo_subjectmod">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/muc#roominfo">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_maxhistoryfetch(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_int(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#maxhistoryfetch">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Maximum Number of History Messages Returned "
|
||||||
|
"by Room">>)}.
|
||||||
|
|
||||||
|
encode_contactjid(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
[] -> [];
|
||||||
|
Value -> [enc_jid(V) || V <- Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_contactjid">>,
|
||||||
|
values = Values, required = false, type = 'jid-multi',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Contact Addresses (normally, room owner "
|
||||||
|
"or owners)">>)}.
|
||||||
|
|
||||||
|
encode_description(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_description">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Room description">>)}.
|
||||||
|
|
||||||
|
encode_lang(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_lang">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Natural Language for Room Discussions">>)}.
|
||||||
|
|
||||||
|
encode_ldapgroup(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_ldapgroup">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"An associated LDAP group that defines "
|
||||||
|
"room membership; this should be an LDAP "
|
||||||
|
"Distinguished Name according to an implementa"
|
||||||
|
"tion-specific or deployment-specific "
|
||||||
|
"definition of a group.">>)}.
|
||||||
|
|
||||||
|
encode_logs(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_logs">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"URL for Archived Discussion Logs">>)}.
|
||||||
|
|
||||||
|
encode_occupants(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_int(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_occupants">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Number of occupants">>)}.
|
||||||
|
|
||||||
|
encode_subject(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_subject">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Current Discussion Topic">>)}.
|
||||||
|
|
||||||
|
encode_subjectmod(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"muc#roominfo_subjectmod">>,
|
||||||
|
values = Values, required = false, type = boolean,
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The room subject can be modified by "
|
||||||
|
"participants">>)}.
|
@ -84,6 +84,7 @@ options() ->
|
|||||||
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
||||||
{send_last_published_item, on_sub_and_presence},
|
{send_last_published_item, on_sub_and_presence},
|
||||||
{deliver_notifications, true},
|
{deliver_notifications, true},
|
||||||
|
{title, <<>>},
|
||||||
{presence_based_delivery, false},
|
{presence_based_delivery, false},
|
||||||
{itemreply, none}].
|
{itemreply, none}].
|
||||||
|
|
||||||
@ -452,7 +453,7 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) ->
|
|||||||
end,
|
end,
|
||||||
{error, xmpp:err_item_not_found()}, States);
|
{error, xmpp:err_item_not_found()}, States);
|
||||||
_ ->
|
_ ->
|
||||||
{error, xmpp:err_item_not_found()}
|
{error, xmpp:err_forbidden()}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
@ -901,7 +901,7 @@ first_in_list(Pred, [H | T]) ->
|
|||||||
|
|
||||||
itemids(Nidx, {_U, _S, _R} = JID) ->
|
itemids(Nidx, {_U, _S, _R} = JID) ->
|
||||||
SJID = encode_jid(JID),
|
SJID = encode_jid(JID),
|
||||||
SJIDLike = <<(ejabberd_sql:escape(encode_jid_like(JID)))/binary, "/%">>,
|
SJIDLike = <<(encode_jid_like(JID))/binary, "/%">>,
|
||||||
case catch
|
case catch
|
||||||
ejabberd_sql:sql_query_t(
|
ejabberd_sql:sql_query_t(
|
||||||
?SQL("select @(itemid)s from pubsub_item where "
|
?SQL("select @(itemid)s from pubsub_item where "
|
||||||
|
130
src/pubsub_get_pending.erl
Normal file
130
src/pubsub_get_pending.erl
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
%% 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
|
||||||
|
|
||||||
|
-module(pubsub_get_pending).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("pubsub_get_pending.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
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, [<<"pubsub#node">>]);
|
||||||
|
#xdata_field{values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>]} ->
|
||||||
|
decode(Fs, Acc, [<<"pubsub#node">>]);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{node, Val} -> [encode_node(Val, default, Translate)];
|
||||||
|
{node, Val, Opts} ->
|
||||||
|
[encode_node(Val, Opts, Translate)];
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{node, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#node">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#node">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#node">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_node(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default -> [];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L), value = V}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"pubsub#node">>, values = Values,
|
||||||
|
required = false, type = 'list-single', options = Opts,
|
||||||
|
desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The NodeID of the relevant node">>)}.
|
1666
src/pubsub_node_config.erl
Normal file
1666
src/pubsub_node_config.erl
Normal file
File diff suppressed because it is too large
Load Diff
157
src/pubsub_publish_options.erl
Normal file
157
src/pubsub_publish_options.erl
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
%% 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
|
||||||
|
|
||||||
|
-module(pubsub_publish_options).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("pubsub_publish_options.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
dec_enum(Val, Enums) ->
|
||||||
|
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
|
||||||
|
case lists:member(AtomVal, Enums) of
|
||||||
|
true -> AtomVal
|
||||||
|
end.
|
||||||
|
|
||||||
|
enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
|
||||||
|
|
||||||
|
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/pubsub#publish-opt"
|
||||||
|
"ions">>]} ->
|
||||||
|
decode(Fs, Acc, []);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{access_model, Val} ->
|
||||||
|
[encode_access_model(Val, default, Translate)];
|
||||||
|
{access_model, Val, Opts} ->
|
||||||
|
[encode_access_model(Val, Opts, Translate)];
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"pubsub#access_model">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_enum(Value,
|
||||||
|
[authorize, open, presence, roster, whitelist])
|
||||||
|
of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{access_model, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#access_model">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#access_model">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#access_model">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#access_model">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#access_model">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#access_model">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#publish-opt"
|
||||||
|
"ions">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_access_model(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_enum(Value)]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default ->
|
||||||
|
[#xdata_option{label =
|
||||||
|
Translate(<<"Access model of authorize">>),
|
||||||
|
value = <<"authorize">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Access model of open">>),
|
||||||
|
value = <<"open">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Access model of presence">>),
|
||||||
|
value = <<"presence">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Access model of roster">>),
|
||||||
|
value = <<"roster">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Access model of whitelist">>),
|
||||||
|
value = <<"whitelist">>}];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L),
|
||||||
|
value = enc_enum(V)}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"pubsub#access_model">>,
|
||||||
|
values = Values, required = false, type = 'list-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Specify the access model">>)}.
|
279
src/pubsub_subscribe_authorization.erl
Normal file
279
src/pubsub_subscribe_authorization.erl
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
%% 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
|
||||||
|
|
||||||
|
-module(pubsub_subscribe_authorization).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("pubsub_subscribe_authorization.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"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,
|
||||||
|
[<<"pubsub#allow">>, <<"pubsub#node">>,
|
||||||
|
<<"pubsub#subscriber_jid">>]);
|
||||||
|
#xdata_field{values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>]} ->
|
||||||
|
decode(Fs, Acc,
|
||||||
|
[<<"pubsub#allow">>, <<"pubsub#node">>,
|
||||||
|
<<"pubsub#subscriber_jid">>]);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{allow, Val} -> [encode_allow(Val, Translate)];
|
||||||
|
{allow, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{node, Val} -> [encode_node(Val, Translate)];
|
||||||
|
{node, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{subscriber_jid, Val} ->
|
||||||
|
[encode_subscriber_jid(Val, Translate)];
|
||||||
|
{subscriber_jid, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{subid, Val} -> [encode_subid(Val, Translate)];
|
||||||
|
{subid, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"pubsub#allow">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{allow, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#allow">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#allow">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#allow">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#allow">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#allow">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#allow">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{node, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#node">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#node">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#node">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#node">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#node">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subscriber_jid">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_jid(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subscriber_jid, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#subscriber_jid">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#subscriber_jid">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subscriber_jid">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#subscriber_jid">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subscriber_jid">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#subscriber_jid">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subid">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subid, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#subid">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#subid">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subid">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#subid">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#subid">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#subid">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_a"
|
||||||
|
"uthorization">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_allow(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#allow">>, values = Values,
|
||||||
|
required = false, type = boolean, options = Opts,
|
||||||
|
desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Allow this Jabber ID to subscribe to "
|
||||||
|
"this pubsub node?">>)}.
|
||||||
|
|
||||||
|
encode_node(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#node">>, values = Values,
|
||||||
|
required = false, type = 'text-single', options = Opts,
|
||||||
|
desc = <<>>, label = Translate(<<"Node ID">>)}.
|
||||||
|
|
||||||
|
encode_subscriber_jid(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_jid(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#subscriber_jid">>,
|
||||||
|
values = Values, required = false, type = 'jid-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label = Translate(<<"Subscriber Address">>)}.
|
||||||
|
|
||||||
|
encode_subid(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#subid">>, values = Values,
|
||||||
|
required = false, type = 'text-single', options = Opts,
|
||||||
|
desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The subscription identifier associated "
|
||||||
|
"with the subscription request">>)}.
|
508
src/pubsub_subscribe_options.erl
Normal file
508
src/pubsub_subscribe_options.erl
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
%% 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
|
||||||
|
|
||||||
|
-module(pubsub_subscribe_options).
|
||||||
|
|
||||||
|
-export([decode/1, decode/2, encode/1, encode/2,
|
||||||
|
format_error/1]).
|
||||||
|
|
||||||
|
-include("xmpp_codec.hrl").
|
||||||
|
|
||||||
|
-include("pubsub_subscribe_options.hrl").
|
||||||
|
|
||||||
|
-export_type([{property, 0}, {result, 0}, {form, 0}]).
|
||||||
|
|
||||||
|
dec_enum(Val, Enums) ->
|
||||||
|
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
|
||||||
|
case lists:member(AtomVal, Enums) of
|
||||||
|
true -> AtomVal
|
||||||
|
end.
|
||||||
|
|
||||||
|
enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"0">>.
|
||||||
|
|
||||||
|
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/pubsub#subscribe_o"
|
||||||
|
"ptions">>]} ->
|
||||||
|
decode(Fs, Acc, []);
|
||||||
|
_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{form_type_mismatch,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
|
||||||
|
|
||||||
|
encode(List, Translate) when is_list(List) ->
|
||||||
|
Fs = [case Opt of
|
||||||
|
{deliver, Val} -> [encode_deliver(Val, Translate)];
|
||||||
|
{deliver, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{digest, Val} -> [encode_digest(Val, Translate)];
|
||||||
|
{digest, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{digest_frequency, Val} ->
|
||||||
|
[encode_digest_frequency(Val, Translate)];
|
||||||
|
{digest_frequency, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{expire, Val} -> [encode_expire(Val, Translate)];
|
||||||
|
{expire, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{include_body, Val} ->
|
||||||
|
[encode_include_body(Val, Translate)];
|
||||||
|
{include_body, _, _} -> erlang:error({badarg, Opt});
|
||||||
|
{'show-values', Val} ->
|
||||||
|
['encode_show-values'(Val, default, Translate)];
|
||||||
|
{'show-values', Val, Opts} ->
|
||||||
|
['encode_show-values'(Val, Opts, Translate)];
|
||||||
|
{subscription_type, Val} ->
|
||||||
|
[encode_subscription_type(Val, default, Translate)];
|
||||||
|
{subscription_type, Val, Opts} ->
|
||||||
|
[encode_subscription_type(Val, Opts, Translate)];
|
||||||
|
{subscription_depth, Val} ->
|
||||||
|
[encode_subscription_depth(Val, default, Translate)];
|
||||||
|
{subscription_depth, Val, Opts} ->
|
||||||
|
[encode_subscription_depth(Val, Opts, Translate)];
|
||||||
|
#xdata_field{} -> [Opt];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
|| Opt <- List],
|
||||||
|
FormType = #xdata_field{var = <<"FORM_TYPE">>,
|
||||||
|
type = hidden,
|
||||||
|
values =
|
||||||
|
[<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>]},
|
||||||
|
[FormType | lists:flatten(Fs)].
|
||||||
|
|
||||||
|
decode([#xdata_field{var = <<"pubsub#deliver">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{deliver, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#deliver">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#deliver">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#deliver">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#deliver">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#deliver">>} | _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#deliver">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#digest">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{digest, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#digest">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#digest">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#digest">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#digest">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#digest">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#digest">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#digest_frequency">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{digest_frequency, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#digest_frequency">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#digest_frequency">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#digest_frequency">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"pubsub#digest_frequency">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#digest_frequency">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#digest_frequency">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#expire">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try Value of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{expire, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#expire">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#expire">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#expire">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#expire">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#expire">>} | _], _,
|
||||||
|
_) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#expire">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#include_body">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_bool(Value) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{include_body, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#include_body">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#include_body">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var = <<"pubsub#include_body">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var = <<"pubsub#include_body">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var = <<"pubsub#include_body">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#include_body">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var = <<"pubsub#show-values">>,
|
||||||
|
values = Values}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try [dec_enum(Value, [away, chat, dnd, online, xa])
|
||||||
|
|| Value <- Values]
|
||||||
|
of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{'show-values', Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#show-values">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#show-values">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_type">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_enum(Value, [items, nodes]) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subscription_type, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#subscription_type">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#subscription_type">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_type">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_type">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_type">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#subscription_type">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_depth">>,
|
||||||
|
values = [Value]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
try dec_enum(Value, ['1', all]) of
|
||||||
|
Result ->
|
||||||
|
decode(Fs, [{subscription_depth, Result} | Acc],
|
||||||
|
lists:delete(<<"pubsub#subscription_depth">>, Required))
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{bad_var_value, <<"pubsub#subscription_depth">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}})
|
||||||
|
end;
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_depth">>,
|
||||||
|
values = []} =
|
||||||
|
F
|
||||||
|
| Fs],
|
||||||
|
Acc, Required) ->
|
||||||
|
decode([F#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_depth">>,
|
||||||
|
values = [<<>>]}
|
||||||
|
| Fs],
|
||||||
|
Acc, Required);
|
||||||
|
decode([#xdata_field{var =
|
||||||
|
<<"pubsub#subscription_depth">>}
|
||||||
|
| _],
|
||||||
|
_, _) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{too_many_values, <<"pubsub#subscription_depth">>,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
|
||||||
|
if Var /= <<"FORM_TYPE">> ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{unknown_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
true -> decode(Fs, Acc, Required)
|
||||||
|
end;
|
||||||
|
decode([], _, [Var | _]) ->
|
||||||
|
erlang:error({?MODULE,
|
||||||
|
{missing_required_var, Var,
|
||||||
|
<<"http://jabber.org/protocol/pubsub#subscribe_o"
|
||||||
|
"ptions">>}});
|
||||||
|
decode([], Acc, []) -> Acc.
|
||||||
|
|
||||||
|
encode_deliver(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#deliver">>,
|
||||||
|
values = Values, required = false, type = boolean,
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Whether an entity wants to receive or "
|
||||||
|
"disable notifications">>)}.
|
||||||
|
|
||||||
|
encode_digest(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#digest">>, values = Values,
|
||||||
|
required = false, type = boolean, options = Opts,
|
||||||
|
desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Whether an entity wants to receive digests "
|
||||||
|
"(aggregations) of notifications or all "
|
||||||
|
"notifications individually">>)}.
|
||||||
|
|
||||||
|
encode_digest_frequency(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#digest_frequency">>,
|
||||||
|
values = Values, required = false, type = 'text-single',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The minimum number of milliseconds between "
|
||||||
|
"sending any two notification digests">>)}.
|
||||||
|
|
||||||
|
encode_expire(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
<<>> -> [];
|
||||||
|
Value -> [Value]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#expire">>, values = Values,
|
||||||
|
required = false, type = 'text-single', options = Opts,
|
||||||
|
desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The DateTime at which a leased subscription "
|
||||||
|
"will end or has ended">>)}.
|
||||||
|
|
||||||
|
encode_include_body(Value, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_bool(Value)]
|
||||||
|
end,
|
||||||
|
Opts = [],
|
||||||
|
#xdata_field{var = <<"pubsub#include_body">>,
|
||||||
|
values = Values, required = false, type = boolean,
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"Whether an entity wants to receive an "
|
||||||
|
"XMPP message body in addition to the "
|
||||||
|
"payload format">>)}.
|
||||||
|
|
||||||
|
'encode_show-values'(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
[] -> [];
|
||||||
|
Value -> [enc_enum(V) || V <- Value]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default ->
|
||||||
|
[#xdata_option{label =
|
||||||
|
Translate(<<"XMPP Show Value of Away">>),
|
||||||
|
value = <<"away">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"XMPP Show Value of Chat">>),
|
||||||
|
value = <<"chat">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"XMPP Show Value of DND (Do Not Disturb)">>),
|
||||||
|
value = <<"dnd">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Mere Availability in XMPP (No Show Value)">>),
|
||||||
|
value = <<"online">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"XMPP Show Value of XA (Extended Away)">>),
|
||||||
|
value = <<"xa">>}];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L),
|
||||||
|
value = enc_enum(V)}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"pubsub#show-values">>,
|
||||||
|
values = Values, required = false, type = 'list-multi',
|
||||||
|
options = Opts, desc = <<>>,
|
||||||
|
label =
|
||||||
|
Translate(<<"The presence states for which an entity "
|
||||||
|
"wants to receive notifications">>)}.
|
||||||
|
|
||||||
|
encode_subscription_type(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_enum(Value)]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default ->
|
||||||
|
[#xdata_option{label =
|
||||||
|
Translate(<<"Receive notification of new items only">>),
|
||||||
|
value = <<"items">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Receive notification of new nodes only">>),
|
||||||
|
value = <<"nodes">>}];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L),
|
||||||
|
value = enc_enum(V)}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"pubsub#subscription_type">>,
|
||||||
|
values = Values, required = false, type = 'list-single',
|
||||||
|
options = Opts, desc = <<>>, label = <<>>}.
|
||||||
|
|
||||||
|
encode_subscription_depth(Value, Options, Translate) ->
|
||||||
|
Values = case Value of
|
||||||
|
undefined -> [];
|
||||||
|
Value -> [enc_enum(Value)]
|
||||||
|
end,
|
||||||
|
Opts = if Options == default ->
|
||||||
|
[#xdata_option{label =
|
||||||
|
Translate(<<"Receive notification from direct child "
|
||||||
|
"nodes only">>),
|
||||||
|
value = <<"1">>},
|
||||||
|
#xdata_option{label =
|
||||||
|
Translate(<<"Receive notification from all descendent "
|
||||||
|
"nodes">>),
|
||||||
|
value = <<"all">>}];
|
||||||
|
true ->
|
||||||
|
[#xdata_option{label = Translate(L),
|
||||||
|
value = enc_enum(V)}
|
||||||
|
|| {L, V} <- Options]
|
||||||
|
end,
|
||||||
|
#xdata_field{var = <<"pubsub#subscription_depth">>,
|
||||||
|
values = Values, required = false, type = 'list-single',
|
||||||
|
options = Opts, desc = <<>>, label = <<>>}.
|
648
src/xdata_codec.erl
Normal file
648
src/xdata_codec.erl
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 27 Sep 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(xdata_codec).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([compile/1, compile/2]).
|
||||||
|
-export([dec_int/1, dec_int/3, dec_enum/2, dec_bool/1, not_empty/1,
|
||||||
|
dec_enum_int/2, dec_enum_int/4, enc_int/1, enc_enum/1,
|
||||||
|
enc_bool/1, enc_enum_int/1, format_error/1, enc_jid/1, dec_jid/1]).
|
||||||
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
|
-record(state, {mod_name :: atom(),
|
||||||
|
file_name :: string(),
|
||||||
|
erl = "" :: string(),
|
||||||
|
hrl = "" :: string(),
|
||||||
|
dir = "" :: string(),
|
||||||
|
ns = <<>> :: binary(),
|
||||||
|
doc = <<>> :: binary(),
|
||||||
|
erl_dir = "" :: string(),
|
||||||
|
hrl_dir = "" :: string(),
|
||||||
|
prefix = [] :: [binary()],
|
||||||
|
dec_mfas = [] :: [{binary(), mfa()}],
|
||||||
|
enc_mfas = [] :: [{binary(), mfa()}],
|
||||||
|
specs = [] :: [{binary(), string()}],
|
||||||
|
required = [] :: [{binary(), boolean()} | binary()],
|
||||||
|
defaults = [] :: [{binary(), any()}]}).
|
||||||
|
|
||||||
|
-define(is_multi_type(T),
|
||||||
|
((T == 'list-multi') or (T == 'jid-multi') or (T == 'text-multi'))).
|
||||||
|
|
||||||
|
-define(is_list_type(T),
|
||||||
|
((T == 'list-single') or (T == 'list-multi'))).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
compile(Path) ->
|
||||||
|
compile(Path, []).
|
||||||
|
|
||||||
|
compile(Path, Opts) ->
|
||||||
|
case filelib:is_dir(Path) of
|
||||||
|
true ->
|
||||||
|
filelib:fold_files(
|
||||||
|
Path, ".*.xdata", false,
|
||||||
|
fun(File, ok) ->
|
||||||
|
compile_file(File, Opts);
|
||||||
|
(_, Err) ->
|
||||||
|
Err
|
||||||
|
end, ok);
|
||||||
|
false ->
|
||||||
|
compile_file(Path, Opts)
|
||||||
|
end.
|
||||||
|
|
||||||
|
compile_file(Path, Opts) ->
|
||||||
|
try
|
||||||
|
ok = application:ensure_started(fast_xml),
|
||||||
|
DirName = filename:dirname(Path),
|
||||||
|
FileName = filename:basename(Path),
|
||||||
|
RootName = filename:rootname(FileName),
|
||||||
|
ConfigPath = filename:join(DirName, RootName) ++ ".cfg",
|
||||||
|
ModName = list_to_atom(RootName),
|
||||||
|
{ok, Data} = file:read_file(Path),
|
||||||
|
Config = case file:consult(ConfigPath) of
|
||||||
|
{ok, Terms} -> lists:flatten(Terms);
|
||||||
|
{error, enoent} -> []
|
||||||
|
end,
|
||||||
|
State = #state{mod_name = ModName,
|
||||||
|
file_name = FileName,
|
||||||
|
erl = filename:rootname(FileName) ++ ".erl",
|
||||||
|
hrl = filename:rootname(FileName) ++ ".hrl",
|
||||||
|
dir = DirName,
|
||||||
|
prefix = proplists:get_all_values(prefix, Config),
|
||||||
|
erl_dir = proplists:get_value(erl_dir, Opts, DirName),
|
||||||
|
hrl_dir = proplists:get_value(hrl_dir, Opts, DirName),
|
||||||
|
dec_mfas = proplists:get_value(decode, Config, []),
|
||||||
|
enc_mfas = proplists:get_value(encode, Config, []),
|
||||||
|
specs = proplists:get_value(specs, Config, []),
|
||||||
|
required = proplists:get_value(required, Config, []),
|
||||||
|
defaults = proplists:get_value(defaults, Config, [])},
|
||||||
|
#xmlel{} = El = fxml_stream:parse_element(Data),
|
||||||
|
ok = compile_element(normalize(El), State),
|
||||||
|
io:format("Compiled ~s~n", [Path])
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
io:format(standard_error, "Failed to compile ~s: ~p~n",
|
||||||
|
[Path, Err]),
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
emit(Format) ->
|
||||||
|
emit(Format, []).
|
||||||
|
|
||||||
|
emit(Format, Args) ->
|
||||||
|
put(outbuf, get(outbuf) ++ io_lib:format(Format, Args)).
|
||||||
|
|
||||||
|
dec_int(Val) ->
|
||||||
|
dec_int(Val, infinity, infinity).
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
dec_enum(Val, Enums) ->
|
||||||
|
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
|
||||||
|
case lists:member(AtomVal, Enums) of
|
||||||
|
true ->
|
||||||
|
AtomVal
|
||||||
|
end.
|
||||||
|
|
||||||
|
enc_enum(Atom) ->
|
||||||
|
erlang:atom_to_binary(Atom, utf8).
|
||||||
|
|
||||||
|
dec_enum_int(Val, Enums) ->
|
||||||
|
try dec_int(Val)
|
||||||
|
catch _:_ -> dec_enum(Val, Enums)
|
||||||
|
end.
|
||||||
|
|
||||||
|
dec_enum_int(Val, Enums, Min, Max) ->
|
||||||
|
try dec_int(Val, Min, Max)
|
||||||
|
catch _:_ -> dec_enum(Val, Enums)
|
||||||
|
end.
|
||||||
|
|
||||||
|
enc_enum_int(Int) when is_integer(Int) ->
|
||||||
|
enc_int(Int);
|
||||||
|
enc_enum_int(Atom) ->
|
||||||
|
enc_enum(Atom).
|
||||||
|
|
||||||
|
dec_bool(<<"1">>) -> true;
|
||||||
|
dec_bool(<<"0">>) -> false;
|
||||||
|
dec_bool(<<"true">>) -> true;
|
||||||
|
dec_bool(<<"false">>) -> false.
|
||||||
|
|
||||||
|
enc_bool(true) -> <<"1">>;
|
||||||
|
enc_bool(false) -> <<"0">>.
|
||||||
|
|
||||||
|
enc_jid(J) -> jid:to_string(J).
|
||||||
|
|
||||||
|
dec_jid(Val) ->
|
||||||
|
case jid:from_string(Val) of
|
||||||
|
error -> erlang:error(badarg);
|
||||||
|
J -> J
|
||||||
|
end.
|
||||||
|
|
||||||
|
not_empty(<<_, _/binary>> = Val) ->
|
||||||
|
Val.
|
||||||
|
|
||||||
|
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, "'">>.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
compile_element(#xmlel{name = <<"form_type">>, children = Els} = Form,
|
||||||
|
#state{erl = OutErl, erl_dir = ErlDir,
|
||||||
|
hrl = OutHrl, hrl_dir = HrlDir} = State0) ->
|
||||||
|
try
|
||||||
|
Name = fxml:get_subtag_cdata(Form, <<"name">>),
|
||||||
|
Doc = fxml:get_subtag_cdata(Form, <<"doc">>),
|
||||||
|
X = #xmlel{name = <<"x">>,
|
||||||
|
attrs = [{<<"type">>, <<"form">>},
|
||||||
|
{<<"xmlns">>, <<"jabber:x:data">>}],
|
||||||
|
children = Els},
|
||||||
|
State = State0#state{ns = Name, doc = Doc},
|
||||||
|
#xdata{fields = Fs} = xmpp_codec:decode(X),
|
||||||
|
put(outbuf, []),
|
||||||
|
mk_header(State),
|
||||||
|
mk_aux_funs(),
|
||||||
|
mk_top_decoder(Fs, State),
|
||||||
|
mk_top_encoder(Fs, State),
|
||||||
|
mk_decoder(Fs, State),
|
||||||
|
mk_encoders(Fs, State),
|
||||||
|
ErlData = get(outbuf),
|
||||||
|
ok = file:write_file(filename:join(ErlDir, OutErl), ErlData),
|
||||||
|
ok = erl_tidy:file(filename:join(ErlDir, OutErl), [{backups, false}]),
|
||||||
|
put(outbuf, []),
|
||||||
|
mk_type_definitions(Fs, State),
|
||||||
|
HrlData = get(outbuf),
|
||||||
|
ok = file:write_file(filename:join(HrlDir, OutHrl), HrlData)
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_aux_funs() ->
|
||||||
|
case get_abstract_code_from_myself() of
|
||||||
|
{ok, AbsCode} ->
|
||||||
|
AST = lists:filter(
|
||||||
|
fun(T) ->
|
||||||
|
case catch erl_syntax_lib:analyze_function(T) of
|
||||||
|
{format_error, 1} -> true;
|
||||||
|
{dec_int, 3} -> true;
|
||||||
|
{dec_int, 1} -> true;
|
||||||
|
{dec_enum, 2} -> true;
|
||||||
|
{dec_enum_int, 2} -> true;
|
||||||
|
{dec_enum_int, 4} -> true;
|
||||||
|
{enc_int, 1} -> true;
|
||||||
|
{enc_enum, 1} -> true;
|
||||||
|
{enc_enum_int, 1} -> true;
|
||||||
|
{not_empty, 1} -> true;
|
||||||
|
{dec_bool, 1} -> true;
|
||||||
|
{enc_bool, 1} -> true;
|
||||||
|
{enc_jid, 1} -> true;
|
||||||
|
{dec_jid, 1} -> true;
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end, AbsCode),
|
||||||
|
emit(erl_prettypr:format(erl_syntax:form_list(AST)) ++ io_lib:nl());
|
||||||
|
error ->
|
||||||
|
erlang:error({no_abstract_code_found, ?MODULE})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_abstract_code_from_myself() ->
|
||||||
|
{file, File} = code:is_loaded(?MODULE),
|
||||||
|
case beam_lib:chunks(File, [abstract_code]) of
|
||||||
|
{ok, {_, List}} ->
|
||||||
|
case lists:keyfind(abstract_code, 1, List) of
|
||||||
|
{abstract_code, {raw_abstract_v1, Abstr}} ->
|
||||||
|
{ok, Abstr};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_comment_header(#state{file_name = Source, ns = NS, doc = Doc}) ->
|
||||||
|
emit("%% Created automatically by xdata generator (xdata_codec.erl)~n"
|
||||||
|
"%% Source: ~s~n"
|
||||||
|
"%% Form type: ~s~n", [Source, NS]),
|
||||||
|
if Doc /= <<>> -> emit("%% Document: ~s~n~n", [Doc]);
|
||||||
|
true -> emit("~n")
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_header(#state{mod_name = Mod, hrl = Include} = State) ->
|
||||||
|
mk_comment_header(State),
|
||||||
|
emit("~n-module(~s).~n", [Mod]),
|
||||||
|
emit("-export([decode/1, decode/2, encode/1, encode/2, format_error/1]).~n"),
|
||||||
|
emit("-include(\"xmpp_codec.hrl\").~n"),
|
||||||
|
emit("-include(\"~s\").~n", [Include]),
|
||||||
|
emit("-export_type([property/0, result/0, form/0]).~n").
|
||||||
|
|
||||||
|
mk_type_definitions(Fs, State) ->
|
||||||
|
mk_comment_header(State),
|
||||||
|
lists:foreach(
|
||||||
|
fun(#xdata_field{var = Var} = F) ->
|
||||||
|
Spec = get_typespec(F, State),
|
||||||
|
case is_complex_type(Spec) of
|
||||||
|
true ->
|
||||||
|
emit("-type '~s'() :: ~s.~n",
|
||||||
|
[var_to_rec_field(Var, State), Spec]);
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end, Fs),
|
||||||
|
emit("~n-type property() :: "),
|
||||||
|
Fields = lists:map(
|
||||||
|
fun(#xdata_field{var = Var} = F) ->
|
||||||
|
RecField = var_to_rec_field(Var, State),
|
||||||
|
[io_lib:format("{'~s', ~s}",
|
||||||
|
[RecField, mk_typespec(F, State)])]
|
||||||
|
end, Fs),
|
||||||
|
emit(string:join(Fields, " |~n ") ++ ".~n"),
|
||||||
|
emit("-type result() :: [property()].~n~n"),
|
||||||
|
VarsWithSpec = lists:flatmap(
|
||||||
|
fun(#xdata_field{type = T, var = Var} = F)
|
||||||
|
when ?is_list_type(T) ->
|
||||||
|
RecName = var_to_rec_field(Var, State),
|
||||||
|
Spec0 = get_typespec(F, State),
|
||||||
|
Spec = case is_complex_type(Spec0) of
|
||||||
|
true ->
|
||||||
|
io_lib:format("'~s'()", [RecName]);
|
||||||
|
false ->
|
||||||
|
Spec0
|
||||||
|
end,
|
||||||
|
[{RecName, mk_typespec(F, State), Spec}];
|
||||||
|
(_) ->
|
||||||
|
[]
|
||||||
|
end, Fs),
|
||||||
|
case VarsWithSpec of
|
||||||
|
[] ->
|
||||||
|
emit("-type form() :: [property() | xdata_field()].~n");
|
||||||
|
_ ->
|
||||||
|
emit("-type options(T) :: [{binary(), T}].~n"),
|
||||||
|
emit("-type property_with_options() ::~n "),
|
||||||
|
Options = [io_lib:format("{'~s', ~s, options(~s)}",
|
||||||
|
[Var, Spec1, Spec2])
|
||||||
|
|| {Var, Spec1, Spec2} <- VarsWithSpec],
|
||||||
|
emit(string:join(Options, " |~n ") ++ ".~n"),
|
||||||
|
emit("-type form() :: [property() | property_with_options() | xdata_field()].~n")
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_top_decoder(Fs, State) ->
|
||||||
|
Required = [Var || #xdata_field{var = Var} <- Fs, is_required(Var, State)],
|
||||||
|
emit("decode(Fs) -> decode(Fs, []).~n"),
|
||||||
|
emit("decode(Fs, Acc) ->"
|
||||||
|
" case lists:keyfind(<<\"FORM_TYPE\">>, #xdata_field.var, Fs) of"
|
||||||
|
" false ->"
|
||||||
|
" decode(Fs, Acc, ~p);"
|
||||||
|
" #xdata_field{values = [~p]} ->"
|
||||||
|
" decode(Fs, Acc, ~p);"
|
||||||
|
" _ ->"
|
||||||
|
" erlang:error({?MODULE, {form_type_mismatch, ~p}})~n"
|
||||||
|
" end.~n",
|
||||||
|
[Required, State#state.ns, Required, State#state.ns]).
|
||||||
|
|
||||||
|
mk_top_encoder(Fs, State) ->
|
||||||
|
Clauses = string:join(
|
||||||
|
lists:map(
|
||||||
|
fun(#xdata_field{var = Var, type = T}) when ?is_list_type(T) ->
|
||||||
|
Field = var_to_rec_field(Var, State),
|
||||||
|
io_lib:format(
|
||||||
|
"{'~s', Val} -> ['encode_~s'(Val, default, Translate)];"
|
||||||
|
"{'~s', Val, Opts} -> ['encode_~s'(Val, Opts, Translate)]",
|
||||||
|
[Field, Field, Field, Field]);
|
||||||
|
(#xdata_field{var = Var}) ->
|
||||||
|
Field = var_to_rec_field(Var, State),
|
||||||
|
io_lib:format(
|
||||||
|
"{'~s', Val} -> ['encode_~s'(Val, Translate)];"
|
||||||
|
"{'~s', _, _} -> erlang:error({badarg, Opt})",
|
||||||
|
[Field, Field, Field])
|
||||||
|
end, Fs) ++ ["#xdata_field{} -> [Opt]; _ -> []"],
|
||||||
|
";"),
|
||||||
|
emit("encode(Cfg) -> encode(Cfg, fun(Text) -> Text end).~n"),
|
||||||
|
emit("encode(List, Translate) when is_list(List) ->"
|
||||||
|
" Fs = [case Opt of ~s end || Opt <- List],"
|
||||||
|
" FormType = #xdata_field{var = <<\"FORM_TYPE\">>, type = hidden,"
|
||||||
|
" values = [~p]},"
|
||||||
|
" [FormType|lists:flatten(Fs)].~n",
|
||||||
|
[Clauses, State#state.ns]).
|
||||||
|
|
||||||
|
mk_decoder([#xdata_field{var = Var, type = Type} = F|Fs], State) ->
|
||||||
|
ValVar = if ?is_multi_type(Type) -> "Values";
|
||||||
|
true -> "[Value]"
|
||||||
|
end,
|
||||||
|
DecFun = if ?is_multi_type(Type) ->
|
||||||
|
["[", mk_decoding_fun(F, State), " || Value <- Values]"];
|
||||||
|
true ->
|
||||||
|
mk_decoding_fun(F, State)
|
||||||
|
end,
|
||||||
|
emit("decode([#xdata_field{var = ~p, values = ~s}|Fs], Acc, Required) ->"
|
||||||
|
" try ~s of"
|
||||||
|
" Result -> decode(Fs, [{'~s', Result}|Acc],"
|
||||||
|
" lists:delete(~p, Required))"
|
||||||
|
" catch _:_ ->"
|
||||||
|
" erlang:error({?MODULE, {bad_var_value, ~p, ~p}})"
|
||||||
|
" end;",
|
||||||
|
[Var, ValVar, DecFun, var_to_rec_field(Var, State),
|
||||||
|
Var, Var, State#state.ns]),
|
||||||
|
if not ?is_multi_type(Type) ->
|
||||||
|
emit("decode([#xdata_field{var = ~p, values = []} = F|Fs],"
|
||||||
|
" Acc, Required) ->"
|
||||||
|
" decode([F#xdata_field{var = ~p, values = [<<>>]}|Fs],"
|
||||||
|
" Acc, Required);",
|
||||||
|
[Var, Var]),
|
||||||
|
emit("decode([#xdata_field{var = ~p}|_], _, _) ->"
|
||||||
|
" erlang:error({?MODULE, {too_many_values, ~p, ~p}});",
|
||||||
|
[Var, Var, State#state.ns]);
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
mk_decoder(Fs, State);
|
||||||
|
mk_decoder([], State) ->
|
||||||
|
emit("decode([#xdata_field{var = Var}|Fs], Acc, Required) ->"
|
||||||
|
" if Var /= <<\"FORM_TYPE\">> ->"
|
||||||
|
" erlang:error({?MODULE, {unknown_var, Var, ~p}});"
|
||||||
|
" true ->"
|
||||||
|
" decode(Fs, Acc, Required)"
|
||||||
|
" end;",
|
||||||
|
[State#state.ns]),
|
||||||
|
emit("decode([], _, [Var|_]) ->"
|
||||||
|
" erlang:error({?MODULE, {missing_required_var, Var, ~p}});~n",
|
||||||
|
[State#state.ns]),
|
||||||
|
emit("decode([], Acc, []) -> Acc.~n").
|
||||||
|
|
||||||
|
mk_encoders(Fs, State) ->
|
||||||
|
lists:foreach(
|
||||||
|
fun(#xdata_field{var = Var, required = IsRequired, desc = Desc,
|
||||||
|
label = Label, type = Type} = F) ->
|
||||||
|
EncVals = mk_encoded_values(F, State),
|
||||||
|
EncOpts = mk_encoded_options(F, State),
|
||||||
|
FieldName = var_to_rec_field(Var, State),
|
||||||
|
DescStr = if Desc == <<>> -> "<<>>";
|
||||||
|
true -> io_lib:format("Translate(~p)", [Desc])
|
||||||
|
end,
|
||||||
|
LabelStr = if Label == <<>> -> "<<>>";
|
||||||
|
true -> io_lib:format("Translate(~p)", [Label])
|
||||||
|
end,
|
||||||
|
if ?is_list_type(Type) ->
|
||||||
|
emit("'encode_~s'(Value, Options, Translate) ->", [FieldName]);
|
||||||
|
true ->
|
||||||
|
emit("'encode_~s'(Value, Translate) ->", [FieldName])
|
||||||
|
end,
|
||||||
|
emit(" Values = ~s,"
|
||||||
|
" Opts = ~s,"
|
||||||
|
" #xdata_field{var = ~p,"
|
||||||
|
" values = Values,"
|
||||||
|
" required = ~p,"
|
||||||
|
" type = ~p,"
|
||||||
|
" options = Opts,"
|
||||||
|
" desc = ~s,"
|
||||||
|
" label = ~s}.~n",
|
||||||
|
[EncVals, EncOpts, Var, IsRequired, Type, DescStr, LabelStr])
|
||||||
|
end, Fs).
|
||||||
|
|
||||||
|
mk_encoded_values(#xdata_field{var = Var, type = Type,
|
||||||
|
options = Options}, State) ->
|
||||||
|
EncFun =
|
||||||
|
case get_enc_fun(Var, Type, Options, State) of
|
||||||
|
{M, Fun, Args} ->
|
||||||
|
Mod = if M == undefined -> "";
|
||||||
|
true -> io_lib:format("~s:", [M])
|
||||||
|
end,
|
||||||
|
FArgs = [io_lib:format(", ~p", [A]) || A <- Args],
|
||||||
|
if ?is_multi_type(Type) ->
|
||||||
|
"[" ++ io_lib:format("~s~s(V~s)", [Mod, Fun, FArgs]) ++
|
||||||
|
" || V <- Value]";
|
||||||
|
true ->
|
||||||
|
"[" ++ io_lib:format("~s~s(Value~s)", [Mod, Fun, FArgs])
|
||||||
|
++ "]"
|
||||||
|
end;
|
||||||
|
undefined ->
|
||||||
|
"[Value]"
|
||||||
|
end,
|
||||||
|
Default = case get_dec_fun(Var, Type, Options, State) of
|
||||||
|
_ when ?is_multi_type(Type) -> "[]";
|
||||||
|
undefined -> "<<>>";
|
||||||
|
_MFA -> "undefined"
|
||||||
|
end,
|
||||||
|
io_lib:format(
|
||||||
|
"case Value of"
|
||||||
|
" ~s -> [];~n"
|
||||||
|
" Value -> ~s~n"
|
||||||
|
"end",
|
||||||
|
[Default, EncFun]).
|
||||||
|
|
||||||
|
mk_encoded_options(#xdata_field{var = Var, type = Type,
|
||||||
|
options = Options}, State) ->
|
||||||
|
EncFun = case get_enc_fun(Var, Type, Options, State) of
|
||||||
|
{M, Fun, Args} ->
|
||||||
|
Mod = if M == undefined -> "";
|
||||||
|
true -> io_lib:format("~s:", [M])
|
||||||
|
end,
|
||||||
|
FArgs = [io_lib:format(", ~p", [A]) || A <- Args],
|
||||||
|
io_lib:format("~s~s(V~s)", [Mod, Fun, FArgs]);
|
||||||
|
undefined ->
|
||||||
|
"V"
|
||||||
|
end,
|
||||||
|
EncOpts = string:join(
|
||||||
|
[case L of
|
||||||
|
<<>> ->
|
||||||
|
io_lib:format("#xdata_option{value = ~p}", [V]);
|
||||||
|
_ ->
|
||||||
|
io_lib:format(
|
||||||
|
"#xdata_option{label = Translate(~p), value = ~p}",
|
||||||
|
[L, V])
|
||||||
|
end || #xdata_option{label = L, value = V} <- Options],
|
||||||
|
","),
|
||||||
|
if ?is_list_type(Type) ->
|
||||||
|
io_lib:format(
|
||||||
|
"if Options == default ->"
|
||||||
|
" [~s];"
|
||||||
|
"true ->"
|
||||||
|
" [#xdata_option{label = Translate(L), value = ~s}"
|
||||||
|
" || {L, V} <- Options]"
|
||||||
|
"end",
|
||||||
|
[EncOpts, EncFun]);
|
||||||
|
true ->
|
||||||
|
"[]"
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_decoding_fun(#xdata_field{var = Var, type = Type,
|
||||||
|
options = Options}, State) ->
|
||||||
|
case get_dec_fun(Var, Type, Options, State) of
|
||||||
|
{M, Fun, Args} ->
|
||||||
|
Mod = if M == undefined -> "";
|
||||||
|
true -> io_lib:format("~s:", [M])
|
||||||
|
end,
|
||||||
|
FArgs = [io_lib:format(", ~p", [A]) || A <- Args],
|
||||||
|
io_lib:format("~s~s(Value~s)", [Mod, Fun, FArgs]);
|
||||||
|
undefined ->
|
||||||
|
"Value"
|
||||||
|
end.
|
||||||
|
|
||||||
|
var_to_rec_field(Var, #state{prefix = [Prefix|T]} = State) ->
|
||||||
|
Size = size(Prefix),
|
||||||
|
case Var of
|
||||||
|
<<(Prefix):Size/binary, Rest/binary>> ->
|
||||||
|
binary_to_atom(Rest, utf8);
|
||||||
|
_ ->
|
||||||
|
var_to_rec_field(Var, State#state{prefix = T})
|
||||||
|
end;
|
||||||
|
var_to_rec_field(Var, #state{prefix = []}) ->
|
||||||
|
Var.
|
||||||
|
|
||||||
|
get_dec_fun(Var, Type, Options, State) ->
|
||||||
|
case lists:keyfind(Var, 1, State#state.dec_mfas) of
|
||||||
|
false when Type == 'list-multi'; Type == 'list-single' ->
|
||||||
|
if Options /= [] ->
|
||||||
|
Variants = [binary_to_atom(V, utf8)
|
||||||
|
|| #xdata_option{value = V} <- Options],
|
||||||
|
{undefined, dec_enum, [Variants]};
|
||||||
|
true ->
|
||||||
|
undefined
|
||||||
|
end;
|
||||||
|
false when Type == 'jid-multi'; Type == 'jid-single' ->
|
||||||
|
{undefined, dec_jid, []};
|
||||||
|
false when Type == boolean ->
|
||||||
|
{undefined, dec_bool, []};
|
||||||
|
false ->
|
||||||
|
undefined;
|
||||||
|
{Var, {M, F, A}} ->
|
||||||
|
{M, F, A};
|
||||||
|
{Var, {dec_bool, []}} ->
|
||||||
|
{undefined, dec_bool, []};
|
||||||
|
{Var, {not_empty, []}} ->
|
||||||
|
{undefined, not_empty, []};
|
||||||
|
{Var, {dec_enum, [Variants]}} ->
|
||||||
|
{undefined, dec_enum, [Variants]};
|
||||||
|
{Var, {dec_int, Args}} ->
|
||||||
|
{undefined, dec_int, Args};
|
||||||
|
{Var, {dec_enum_int, Args}} ->
|
||||||
|
{undefined, dec_enum_int, Args};
|
||||||
|
{Var, {dec_jid, []}} ->
|
||||||
|
{undefined, dec_jid, []}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_enc_fun(Var, Type, Options, State) ->
|
||||||
|
case get_dec_fun(Var, Type, Options, State) of
|
||||||
|
{undefined, dec_enum, _} ->
|
||||||
|
{undefined, enc_enum, []};
|
||||||
|
{undefined, dec_bool, _} ->
|
||||||
|
{undefined, enc_bool, []};
|
||||||
|
{undefined, dec_int, _} ->
|
||||||
|
{undefined, enc_int, []};
|
||||||
|
{undefined, dec_enum_int, _} ->
|
||||||
|
{undefined, enc_enum_int, []};
|
||||||
|
{undefined, dec_jid, _} ->
|
||||||
|
{undefined, enc_jid, []};
|
||||||
|
_ ->
|
||||||
|
case lists:keyfind(Var, 1, State#state.enc_mfas) of
|
||||||
|
false ->
|
||||||
|
undefined;
|
||||||
|
{Var, {M, F, A}} ->
|
||||||
|
{M, F, A};
|
||||||
|
{Var, {enc_bool, []}} ->
|
||||||
|
{undefined, enc_bool, []};
|
||||||
|
{Var, {dec_enum, _}} ->
|
||||||
|
{undefined, enc_enum, []};
|
||||||
|
{Var, {enc_int, _}} ->
|
||||||
|
{undefined, enc_int, []};
|
||||||
|
{Var, {dec_enum_int, _}} ->
|
||||||
|
{undefined, enc_enum_int, []};
|
||||||
|
{Var, {enc_jid, _}} ->
|
||||||
|
{undefined, enc_jid, []}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
mk_typespec(#xdata_field{type = Type, var = Var} = Field, State) ->
|
||||||
|
Spec0 = get_typespec(Field, State),
|
||||||
|
Spec1 = case is_complex_type(Spec0) of
|
||||||
|
true ->
|
||||||
|
io_lib:format("'~s'()", [var_to_rec_field(Var, State)]);
|
||||||
|
false ->
|
||||||
|
Spec0
|
||||||
|
end,
|
||||||
|
if ?is_multi_type(Type) -> "[" ++ Spec1 ++ "]";
|
||||||
|
true -> Spec1
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_typespec(#xdata_field{var = Var, type = Type, options = Options}, State) ->
|
||||||
|
case lists:keyfind(Var, 1, State#state.specs) of
|
||||||
|
false ->
|
||||||
|
case get_dec_fun(Var, Type, Options, State) of
|
||||||
|
{undefined, dec_enum, Args} ->
|
||||||
|
enum_spec(Args);
|
||||||
|
{undefined, dec_bool, _} ->
|
||||||
|
"boolean()";
|
||||||
|
{undefined, dec_jid, _} ->
|
||||||
|
"jid:jid()";
|
||||||
|
{undefined, dec_int, Args} ->
|
||||||
|
int_spec(Args);
|
||||||
|
{undefined, dec_enum_int, [Variants|T]} ->
|
||||||
|
enum_spec([Variants]) ++ " | " ++ int_spec(T);
|
||||||
|
_ ->
|
||||||
|
"binary()"
|
||||||
|
end;
|
||||||
|
{Var, Spec} ->
|
||||||
|
Spec
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec is_complex_type(string()) -> boolean().
|
||||||
|
is_complex_type(Spec) ->
|
||||||
|
string:chr(Spec, $|) /= 0.
|
||||||
|
|
||||||
|
int_spec([]) ->
|
||||||
|
"integer()";
|
||||||
|
int_spec([From, To]) ->
|
||||||
|
if From /= infinity, To /= infinity ->
|
||||||
|
io_lib:format("~p..~p", [From, To]);
|
||||||
|
From > 0 ->
|
||||||
|
"pos_integer()";
|
||||||
|
From == 0 ->
|
||||||
|
"non_neg_integer()";
|
||||||
|
true ->
|
||||||
|
"integer()"
|
||||||
|
end.
|
||||||
|
|
||||||
|
enum_spec([Variants]) ->
|
||||||
|
string:join([atom_to_list(V) || V <- Variants], " | ").
|
||||||
|
|
||||||
|
is_required(Var, State) ->
|
||||||
|
lists:member(Var, State#state.required) orelse
|
||||||
|
proplists:get_bool(Var, State#state.required).
|
||||||
|
|
||||||
|
normalize(#xmlel{name = Name, attrs = Attrs, children = Els}) ->
|
||||||
|
#xmlel{name = Name,
|
||||||
|
attrs = [normalize(Attr) || Attr <- Attrs],
|
||||||
|
children = [normalize(El) || El <- Els]};
|
||||||
|
normalize({Key, Data}) ->
|
||||||
|
{Key, normalize(Data)};
|
||||||
|
normalize(Txt) when is_binary(Txt) ->
|
||||||
|
case re:split(Txt, "[\\s\\r\\n\\t]+", [trim, {return, list}]) of
|
||||||
|
[""|T] ->
|
||||||
|
list_to_binary(string:join(T, " "));
|
||||||
|
T ->
|
||||||
|
list_to_binary(string:join(T, " "))
|
||||||
|
end.
|
@ -6602,6 +6602,20 @@ pp(upload_slot, 3) -> [get, put, xmlns];
|
|||||||
pp(thumbnail, 4) -> [uri, 'media-type', width, height];
|
pp(thumbnail, 4) -> [uri, 'media-type', width, height];
|
||||||
pp(_, _) -> no.
|
pp(_, _) -> no.
|
||||||
|
|
||||||
|
enc_ps_aff(member) -> <<"member">>;
|
||||||
|
enc_ps_aff(none) -> <<"none">>;
|
||||||
|
enc_ps_aff(outcast) -> <<"outcast">>;
|
||||||
|
enc_ps_aff(owner) -> <<"owner">>;
|
||||||
|
enc_ps_aff(publisher) -> <<"publisher">>;
|
||||||
|
enc_ps_aff(publish_only) -> <<"publish-only">>.
|
||||||
|
|
||||||
|
dec_ps_aff(<<"member">>) -> member;
|
||||||
|
dec_ps_aff(<<"none">>) -> none;
|
||||||
|
dec_ps_aff(<<"outcast">>) -> outcast;
|
||||||
|
dec_ps_aff(<<"owner">>) -> owner;
|
||||||
|
dec_ps_aff(<<"publisher">>) -> publisher;
|
||||||
|
dec_ps_aff(<<"publish-only">>) -> publish_only.
|
||||||
|
|
||||||
enc_version({Maj, Min}) ->
|
enc_version({Maj, Min}) ->
|
||||||
<<(integer_to_binary(Maj))/binary, $.,
|
<<(integer_to_binary(Maj))/binary, $.,
|
||||||
(integer_to_binary(Min))/binary>>.
|
(integer_to_binary(Min))/binary>>.
|
||||||
@ -19203,10 +19217,7 @@ decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS,
|
|||||||
__TopXMLNS}});
|
__TopXMLNS}});
|
||||||
decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS,
|
decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS,
|
||||||
_val) ->
|
_val) ->
|
||||||
case catch dec_enum(_val,
|
case catch dec_ps_aff(_val) of
|
||||||
[member, none, outcast, owner, publisher,
|
|
||||||
'publish-only'])
|
|
||||||
of
|
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
erlang:error({xmpp_codec,
|
erlang:error({xmpp_codec,
|
||||||
{bad_attr_value, <<"affiliation">>, <<"affiliation">>,
|
{bad_attr_value, <<"affiliation">>, <<"affiliation">>,
|
||||||
@ -19216,7 +19227,7 @@ decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS,
|
|||||||
|
|
||||||
encode_pubsub_owner_affiliation_attr_affiliation(_val,
|
encode_pubsub_owner_affiliation_attr_affiliation(_val,
|
||||||
_acc) ->
|
_acc) ->
|
||||||
[{<<"affiliation">>, enc_enum(_val)} | _acc].
|
[{<<"affiliation">>, enc_ps_aff(_val)} | _acc].
|
||||||
|
|
||||||
decode_pubsub_affiliation(__TopXMLNS, __IgnoreEls,
|
decode_pubsub_affiliation(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"affiliation">>, _attrs, _els}) ->
|
{xmlel, <<"affiliation">>, _attrs, _els}) ->
|
||||||
@ -19290,10 +19301,7 @@ decode_pubsub_affiliation_attr_affiliation(__TopXMLNS,
|
|||||||
__TopXMLNS}});
|
__TopXMLNS}});
|
||||||
decode_pubsub_affiliation_attr_affiliation(__TopXMLNS,
|
decode_pubsub_affiliation_attr_affiliation(__TopXMLNS,
|
||||||
_val) ->
|
_val) ->
|
||||||
case catch dec_enum(_val,
|
case catch dec_ps_aff(_val) of
|
||||||
[member, none, outcast, owner, publisher,
|
|
||||||
'publish-only'])
|
|
||||||
of
|
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
erlang:error({xmpp_codec,
|
erlang:error({xmpp_codec,
|
||||||
{bad_attr_value, <<"affiliation">>, <<"affiliation">>,
|
{bad_attr_value, <<"affiliation">>, <<"affiliation">>,
|
||||||
@ -19303,7 +19311,7 @@ decode_pubsub_affiliation_attr_affiliation(__TopXMLNS,
|
|||||||
|
|
||||||
encode_pubsub_affiliation_attr_affiliation(_val,
|
encode_pubsub_affiliation_attr_affiliation(_val,
|
||||||
_acc) ->
|
_acc) ->
|
||||||
[{<<"affiliation">>, enc_enum(_val)} | _acc].
|
[{<<"affiliation">>, enc_ps_aff(_val)} | _acc].
|
||||||
|
|
||||||
decode_pubsub_subscription(__TopXMLNS, __IgnoreEls,
|
decode_pubsub_subscription(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"subscription">>, _attrs, _els}) ->
|
{xmlel, <<"subscription">>, _attrs, _els}) ->
|
||||||
@ -19826,7 +19834,7 @@ decode_xdata_field(__TopXMLNS, __IgnoreEls,
|
|||||||
{xmlel, <<"field">>, _attrs, _els}) ->
|
{xmlel, <<"field">>, _attrs, _els}) ->
|
||||||
{Options, Values, Desc, Required, __Els} =
|
{Options, Values, Desc, Required, __Els} =
|
||||||
decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els,
|
decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els,
|
||||||
[], [], undefined, false, []),
|
[], [], <<>>, false, []),
|
||||||
{Label, Type, Var} =
|
{Label, Type, Var} =
|
||||||
decode_xdata_field_attrs(__TopXMLNS, _attrs, undefined,
|
decode_xdata_field_attrs(__TopXMLNS, _attrs, undefined,
|
||||||
undefined, undefined),
|
undefined, undefined),
|
||||||
@ -20003,8 +20011,7 @@ encode_xdata_field({xdata_field, Label, Type, Var,
|
|||||||
[encode_xdata_field_value(Values, __TopXMLNS)
|
[encode_xdata_field_value(Values, __TopXMLNS)
|
||||||
| _acc]).
|
| _acc]).
|
||||||
|
|
||||||
'encode_xdata_field_$desc'(undefined, __TopXMLNS,
|
'encode_xdata_field_$desc'(<<>>, __TopXMLNS, _acc) ->
|
||||||
_acc) ->
|
|
||||||
_acc;
|
_acc;
|
||||||
'encode_xdata_field_$desc'(Desc, __TopXMLNS, _acc) ->
|
'encode_xdata_field_$desc'(Desc, __TopXMLNS, _acc) ->
|
||||||
[encode_xdata_field_desc(Desc, __TopXMLNS) | _acc].
|
[encode_xdata_field_desc(Desc, __TopXMLNS) | _acc].
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
|
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
|
||||||
is_standalone_chat_state/1, get_xdata_values/2,
|
is_standalone_chat_state/1, get_xdata_values/2,
|
||||||
has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2]).
|
has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2,
|
||||||
|
decode_timestamp/1, encode_timestamp/1]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
@ -95,11 +96,66 @@ make_adhoc_response(#adhoc_command{lang = Lang, node = Node, sid = SID},
|
|||||||
|
|
||||||
-spec make_adhoc_response(adhoc_command()) -> adhoc_command().
|
-spec make_adhoc_response(adhoc_command()) -> adhoc_command().
|
||||||
make_adhoc_response(#adhoc_command{sid = <<"">>} = Command) ->
|
make_adhoc_response(#adhoc_command{sid = <<"">>} = Command) ->
|
||||||
SID = jlib:now_to_utc_string(p1_time_compat:timestamp()),
|
SID = encode_timestamp(p1_time_compat:timestamp()),
|
||||||
Command#adhoc_command{sid = SID};
|
Command#adhoc_command{sid = SID};
|
||||||
make_adhoc_response(Command) ->
|
make_adhoc_response(Command) ->
|
||||||
Command.
|
Command.
|
||||||
|
|
||||||
|
-spec decode_timestamp(binary()) -> erlang:timestamp().
|
||||||
|
decode_timestamp(S) ->
|
||||||
|
try try_decode_timestamp(S)
|
||||||
|
catch _:_ -> erlang:error({bad_timestamp, S})
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec encode_timestamp(erlang:timestamp()) -> binary().
|
||||||
|
encode_timestamp({MegaSecs, Secs, MicroSecs}) ->
|
||||||
|
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||||
|
calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}),
|
||||||
|
Fraction = if MicroSecs > 0 ->
|
||||||
|
io_lib:format(".~6..0B", [MicroSecs]);
|
||||||
|
true ->
|
||||||
|
""
|
||||||
|
end,
|
||||||
|
list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT"
|
||||||
|
"~2..0B:~2..0B:~2..0B~sZ",
|
||||||
|
[Year, Month, Day, Hour, Minute, Second,
|
||||||
|
Fraction])).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
try_decode_timestamp(<<Y:4/binary, $-, Mo:2/binary, $-, D:2/binary, $T,
|
||||||
|
H:2/binary, $:, Mi:2/binary, $:, S:2/binary, T/binary>>) ->
|
||||||
|
Date = {to_integer(Y, 1970, 9999), to_integer(Mo, 1, 12), to_integer(D, 1, 31)},
|
||||||
|
Time = {to_integer(H, 0, 23), to_integer(Mi, 0, 59), to_integer(S, 0, 59)},
|
||||||
|
{MS, {TZH, TZM}} = try_decode_fraction(T),
|
||||||
|
Seconds = calendar:datetime_to_gregorian_seconds({Date, Time}) -
|
||||||
|
calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}}) -
|
||||||
|
TZH * 60 * 60 - TZM * 60,
|
||||||
|
{Seconds div 1000000, Seconds rem 1000000, MS};
|
||||||
|
try_decode_timestamp(<<Y:4/binary, Mo:2/binary, D:2/binary, $T,
|
||||||
|
H:2/binary, $:, Mi:2/binary, $:, S:2/binary>>) ->
|
||||||
|
try_decode_timestamp(<<Y:4/binary, $-, Mo:2/binary, $-, D:2/binary, $T,
|
||||||
|
H:2/binary, $:, Mi:2/binary, $:, S:2/binary, $Z>>).
|
||||||
|
|
||||||
|
try_decode_fraction(<<$., T/binary>>) ->
|
||||||
|
{match, [V]} = re:run(T, <<"^[0-9]+">>, [{capture, [0], binary}]),
|
||||||
|
Size = size(V),
|
||||||
|
<<V:Size/binary, TZD/binary>> = T,
|
||||||
|
{to_integer(binary:part(V, 0, min(6, Size)), 0, 999999),
|
||||||
|
try_decode_tzd(TZD)};
|
||||||
|
try_decode_fraction(TZD) ->
|
||||||
|
{0, try_decode_tzd(TZD)}.
|
||||||
|
|
||||||
|
try_decode_tzd(<<$Z>>) ->
|
||||||
|
{0, 0};
|
||||||
|
try_decode_tzd(<<$-, H:2/binary, $:, M:2/binary>>) ->
|
||||||
|
{-1 * to_integer(H, 0, 12), to_integer(M, 0, 59)};
|
||||||
|
try_decode_tzd(<<$+, H:2/binary, $:, M:2/binary>>) ->
|
||||||
|
{to_integer(H, 0, 12), to_integer(M, 0, 59)}.
|
||||||
|
|
||||||
|
to_integer(S, Min, Max) ->
|
||||||
|
case binary_to_integer(S) of
|
||||||
|
I when I >= Min, I =< Max ->
|
||||||
|
I
|
||||||
|
end.
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
-include("suite.hrl").
|
-include("suite.hrl").
|
||||||
|
|
||||||
suite() ->
|
suite() ->
|
||||||
[{timetrap, {seconds,30}}].
|
[{timetrap, {seconds,60}}].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
NewConfig = init_config(Config),
|
NewConfig = init_config(Config),
|
||||||
@ -325,6 +325,36 @@ no_db_tests() ->
|
|||||||
{replaced, [parallel],
|
{replaced, [parallel],
|
||||||
[replaced_master, replaced_slave]}].
|
[replaced_master, replaced_slave]}].
|
||||||
|
|
||||||
|
pubsub_single_tests() ->
|
||||||
|
{pubsub_single, [sequence],
|
||||||
|
[test_pubsub_features,
|
||||||
|
test_pubsub_create,
|
||||||
|
test_pubsub_configure,
|
||||||
|
test_pubsub_delete,
|
||||||
|
test_pubsub_get_affiliations,
|
||||||
|
test_pubsub_get_subscriptions,
|
||||||
|
test_pubsub_create_instant,
|
||||||
|
test_pubsub_default,
|
||||||
|
test_pubsub_create_configure,
|
||||||
|
test_pubsub_publish,
|
||||||
|
test_pubsub_auto_create,
|
||||||
|
test_pubsub_get_items,
|
||||||
|
test_pubsub_delete_item,
|
||||||
|
test_pubsub_purge,
|
||||||
|
test_pubsub_subscribe,
|
||||||
|
test_pubsub_unsubscribe]}.
|
||||||
|
|
||||||
|
pubsub_multiple_tests() ->
|
||||||
|
{pubsub_multiple, [sequence],
|
||||||
|
[{pubsub_publish, [parallel],
|
||||||
|
[pubsub_publish_master, pubsub_publish_slave]},
|
||||||
|
{pubsub_subscriptions, [parallel],
|
||||||
|
[pubsub_subscriptions_master, pubsub_subscriptions_slave]},
|
||||||
|
{pubsub_affiliations, [parallel],
|
||||||
|
[pubsub_affiliations_master, pubsub_affiliations_slave]},
|
||||||
|
{pubsub_authorize, [parallel],
|
||||||
|
[pubsub_authorize_master, pubsub_authorize_slave]}]}.
|
||||||
|
|
||||||
db_tests(riak) ->
|
db_tests(riak) ->
|
||||||
%% No support for mod_pubsub
|
%% No support for mod_pubsub
|
||||||
[{single_user, [sequence],
|
[{single_user, [sequence],
|
||||||
@ -340,7 +370,7 @@ db_tests(riak) ->
|
|||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
test_unregister]},
|
test_unregister]},
|
||||||
{test_muc_register, [sequence],
|
{test_muc_register, [parallel],
|
||||||
[muc_register_master, muc_register_slave]},
|
[muc_register_master, muc_register_slave]},
|
||||||
{test_roster_subscribe, [parallel],
|
{test_roster_subscribe, [parallel],
|
||||||
[roster_subscribe_master,
|
[roster_subscribe_master,
|
||||||
@ -372,9 +402,10 @@ db_tests(DB) when DB == mnesia; DB == redis ->
|
|||||||
privacy,
|
privacy,
|
||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
pubsub,
|
pubsub_single_tests(),
|
||||||
test_unregister]},
|
test_unregister]},
|
||||||
{test_muc_register, [sequence],
|
pubsub_multiple_tests(),
|
||||||
|
{test_muc_register, [parallel],
|
||||||
[muc_register_master, muc_register_slave]},
|
[muc_register_master, muc_register_slave]},
|
||||||
{test_mix, [parallel],
|
{test_mix, [parallel],
|
||||||
[mix_master, mix_slave]},
|
[mix_master, mix_slave]},
|
||||||
@ -409,19 +440,20 @@ db_tests(_) ->
|
|||||||
[{single_user, [sequence],
|
[{single_user, [sequence],
|
||||||
[test_register,
|
[test_register,
|
||||||
legacy_auth_tests(),
|
legacy_auth_tests(),
|
||||||
auth_plain,
|
auth_plain,
|
||||||
auth_md5,
|
auth_md5,
|
||||||
presence_broadcast,
|
presence_broadcast,
|
||||||
last,
|
last,
|
||||||
roster_get,
|
roster_get,
|
||||||
roster_ver,
|
roster_ver,
|
||||||
private,
|
private,
|
||||||
privacy,
|
privacy,
|
||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
pubsub,
|
pubsub_single_tests(),
|
||||||
test_unregister]},
|
test_unregister]},
|
||||||
{test_muc_register, [sequence],
|
pubsub_multiple_tests(),
|
||||||
|
{test_muc_register, [parallel],
|
||||||
[muc_register_master, muc_register_slave]},
|
[muc_register_master, muc_register_slave]},
|
||||||
{test_mix, [parallel],
|
{test_mix, [parallel],
|
||||||
[mix_master, mix_slave]},
|
[mix_master, mix_slave]},
|
||||||
@ -512,17 +544,17 @@ groups() ->
|
|||||||
{riak, [sequence], db_tests(riak)}].
|
{riak, [sequence], db_tests(riak)}].
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[%%{group, ldap},
|
[{group, ldap},
|
||||||
{group, no_db},
|
{group, no_db},
|
||||||
%% {group, mnesia},
|
{group, mnesia},
|
||||||
%% {group, redis},
|
{group, redis},
|
||||||
%% {group, mysql},
|
{group, mysql},
|
||||||
%% {group, pgsql},
|
{group, pgsql},
|
||||||
%% {group, sqlite},
|
{group, sqlite},
|
||||||
%% {group, extauth},
|
{group, extauth},
|
||||||
%% {group, riak},
|
{group, riak},
|
||||||
%% {group, component},
|
{group, component},
|
||||||
%% {group, s2s},
|
{group, s2s},
|
||||||
stop_ejabberd].
|
stop_ejabberd].
|
||||||
|
|
||||||
stop_ejabberd(Config) ->
|
stop_ejabberd(Config) ->
|
||||||
@ -943,16 +975,22 @@ disco(Config) ->
|
|||||||
end, Items),
|
end, Items),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
replaced_master(Config0) ->
|
%% replaced_master(Config0) ->
|
||||||
Config = bind(Config0),
|
%% Config = bind(Config0),
|
||||||
wait_for_slave(Config),
|
%% wait_for_slave(Config),
|
||||||
?recv1(#stream_error{reason = conflict}),
|
%% ?recv1(#stream_error{reason = conflict}),
|
||||||
?recv1({xmlstreamend, <<"stream:stream">>}),
|
%% ?recv1({xmlstreamend, <<"stream:stream">>}),
|
||||||
close_socket(Config).
|
%% close_socket(Config).
|
||||||
|
|
||||||
replaced_slave(Config0) ->
|
%% replaced_slave(Config0) ->
|
||||||
wait_for_master(Config0),
|
%% wait_for_master(Config0),
|
||||||
Config = bind(Config0),
|
%% Config = bind(Config0),
|
||||||
|
%% disconnect(Config).
|
||||||
|
|
||||||
|
replaced_master(Config) ->
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
replaced_slave(Config) ->
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
sm(Config) ->
|
sm(Config) ->
|
||||||
@ -1226,78 +1264,663 @@ stats(Config) ->
|
|||||||
end, Stats),
|
end, Stats),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
pubsub(Config) ->
|
test_pubsub_features(Config) ->
|
||||||
Features = get_features(Config, pubsub_jid(Config)),
|
PJID = pubsub_jid(Config),
|
||||||
true = lists:member(?NS_PUBSUB, Features),
|
AllFeatures = sets:from_list(get_features(Config, PJID)),
|
||||||
%% Publish <presence/> element within node "presence"
|
NeededFeatures = sets:from_list(
|
||||||
ItemID = randoms:get_string(),
|
[?NS_PUBSUB,
|
||||||
Node = <<"presence!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>,
|
?PUBSUB("access-open"),
|
||||||
Item = #ps_item{id = ItemID,
|
?PUBSUB("access-authorize"),
|
||||||
xml_els = [xmpp:encode(#presence{})]},
|
?PUBSUB("create-nodes"),
|
||||||
#iq{type = result,
|
?PUBSUB("instant-nodes"),
|
||||||
sub_els = [#pubsub{publish = #ps_publish{
|
?PUBSUB("config-node"),
|
||||||
node = Node,
|
?PUBSUB("retrieve-default"),
|
||||||
items = [#ps_item{id = ItemID}]}}]} =
|
?PUBSUB("create-and-configure"),
|
||||||
send_recv(Config,
|
?PUBSUB("publish"),
|
||||||
#iq{type = set, to = pubsub_jid(Config),
|
?PUBSUB("auto-create"),
|
||||||
sub_els = [#pubsub{publish = #ps_publish{
|
?PUBSUB("retrieve-items"),
|
||||||
node = Node,
|
?PUBSUB("delete-items"),
|
||||||
items = [Item]}}]}),
|
?PUBSUB("subscribe"),
|
||||||
%% Subscribe to node "presence"
|
?PUBSUB("retrieve-affiliations"),
|
||||||
I1 = send(Config,
|
?PUBSUB("modify-affiliations"),
|
||||||
#iq{type = set, to = pubsub_jid(Config),
|
?PUBSUB("retrieve-subscriptions"),
|
||||||
sub_els = [#pubsub{subscribe = #ps_subscribe{
|
?PUBSUB("manage-subscriptions"),
|
||||||
node = Node,
|
?PUBSUB("purge-nodes"),
|
||||||
jid = my_jid(Config)}}]}),
|
?PUBSUB("delete-nodes")]),
|
||||||
?recv2(
|
true = sets:is_subset(NeededFeatures, AllFeatures),
|
||||||
#message{sub_els = [#ps_event{}, #delay{}]},
|
|
||||||
#iq{type = result, id = I1}),
|
|
||||||
%% Get subscriptions
|
|
||||||
true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
|
|
||||||
#iq{type = result,
|
|
||||||
sub_els =
|
|
||||||
[#pubsub{subscriptions =
|
|
||||||
{<<>>, [#ps_subscription{node = Node}]}}]} =
|
|
||||||
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
|
|
||||||
sub_els = [#pubsub{subscriptions = {<<>>, []}}]}),
|
|
||||||
%% Get affiliations
|
|
||||||
true = lists:member(?PUBSUB("retrieve-affiliations"), Features),
|
|
||||||
#iq{type = result,
|
|
||||||
sub_els = [#pubsub{
|
|
||||||
affiliations =
|
|
||||||
{<<>>, [#ps_affiliation{node = Node, type = owner}]}}]} =
|
|
||||||
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
|
|
||||||
sub_els = [#pubsub{affiliations = {<<>>, []}}]}),
|
|
||||||
%% Fetching published items from node "presence"
|
|
||||||
#iq{type = result,
|
|
||||||
sub_els = [#pubsub{items = #ps_items{
|
|
||||||
node = Node,
|
|
||||||
items = [Item]}}]} =
|
|
||||||
send_recv(Config,
|
|
||||||
#iq{type = get, to = pubsub_jid(Config),
|
|
||||||
sub_els = [#pubsub{items = #ps_items{node = Node}}]}),
|
|
||||||
%% Deleting the item from the node
|
|
||||||
true = lists:member(?PUBSUB("delete-items"), Features),
|
|
||||||
I2 = send(Config,
|
|
||||||
#iq{type = set, to = pubsub_jid(Config),
|
|
||||||
sub_els = [#pubsub{retract = #ps_retract{
|
|
||||||
node = Node,
|
|
||||||
items = [#ps_item{id = ItemID}]}}]}),
|
|
||||||
?recv2(
|
|
||||||
#iq{type = result, id = I2, sub_els = []},
|
|
||||||
#message{sub_els = [#ps_event{
|
|
||||||
items = #ps_items{
|
|
||||||
node = Node,
|
|
||||||
retract = ItemID}}]}),
|
|
||||||
%% Unsubscribe from node "presence"
|
|
||||||
#iq{type = result, sub_els = []} =
|
|
||||||
send_recv(Config,
|
|
||||||
#iq{type = set, to = pubsub_jid(Config),
|
|
||||||
sub_els = [#pubsub{unsubscribe = #ps_unsubscribe{
|
|
||||||
node = Node,
|
|
||||||
jid = my_jid(Config)}}]}),
|
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_create(Config) ->
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
Node = create_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_create_instant(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_configure(Config) ->
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
NodeTitle = ?config(pubsub_node_title, Config),
|
||||||
|
NodeConfig = get_node_config(Config, Node),
|
||||||
|
MyNodeConfig = set_opts(NodeConfig,
|
||||||
|
[{title, NodeTitle}]),
|
||||||
|
set_node_config(Config, Node, MyNodeConfig),
|
||||||
|
NewNodeConfig = get_node_config(Config, Node),
|
||||||
|
NodeTitle = proplists:get_value(title, NewNodeConfig),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_default(Config) ->
|
||||||
|
get_default_node_config(Config),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_create_configure(Config) ->
|
||||||
|
NodeTitle = ?config(pubsub_node_title, Config),
|
||||||
|
DefaultNodeConfig = get_default_node_config(Config),
|
||||||
|
CustomNodeConfig = set_opts(DefaultNodeConfig,
|
||||||
|
[{title, NodeTitle}]),
|
||||||
|
Node = create_node(Config, <<>>, CustomNodeConfig),
|
||||||
|
NodeConfig = get_node_config(Config, Node),
|
||||||
|
NodeTitle = proplists:get_value(title, NodeConfig),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_publish(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
publish_item(Config, Node),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_auto_create(Config) ->
|
||||||
|
Node = randoms:get_string(),
|
||||||
|
publish_item(Config, Node),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_get_items(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
ItemsIn = [publish_item(Config, Node) || _ <- lists:seq(1, 5)],
|
||||||
|
ItemsOut = get_items(Config, Node),
|
||||||
|
true = [I || #ps_item{id = I} <- lists:sort(ItemsIn)]
|
||||||
|
== [I || #ps_item{id = I} <- lists:sort(ItemsOut)],
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_delete_item(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
#ps_item{id = I} = publish_item(Config, Node),
|
||||||
|
[#ps_item{id = I}] = get_items(Config, Node),
|
||||||
|
delete_item(Config, Node, I),
|
||||||
|
[] = get_items(Config, Node),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_subscribe(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
#ps_subscription{type = subscribed} = subscribe_node(Config, Node),
|
||||||
|
[#ps_subscription{node = Node}] = get_subscriptions(Config),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_unsubscribe(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
subscribe_node(Config, Node),
|
||||||
|
[#ps_subscription{node = Node}] = get_subscriptions(Config),
|
||||||
|
unsubscribe_node(Config, Node),
|
||||||
|
[] = get_subscriptions(Config),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_get_affiliations(Config) ->
|
||||||
|
Nodes = lists:sort([create_node(Config, <<>>) || _ <- lists:seq(1, 5)]),
|
||||||
|
Affs = get_affiliations(Config),
|
||||||
|
Nodes = lists:sort([Node || #ps_affiliation{node = Node,
|
||||||
|
type = owner} <- Affs]),
|
||||||
|
[delete_node(Config, Node) || Node <- Nodes],
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_get_subscriptions(Config) ->
|
||||||
|
Nodes = lists:sort([create_node(Config, <<>>) || _ <- lists:seq(1, 5)]),
|
||||||
|
[subscribe_node(Config, Node) || Node <- Nodes],
|
||||||
|
Subs = get_subscriptions(Config),
|
||||||
|
Nodes = lists:sort([Node || #ps_subscription{node = Node} <- Subs]),
|
||||||
|
[delete_node(Config, Node) || Node <- Nodes],
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_purge(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
ItemsIn = [publish_item(Config, Node) || _ <- lists:seq(1, 5)],
|
||||||
|
ItemsOut = get_items(Config, Node),
|
||||||
|
true = [I || #ps_item{id = I} <- lists:sort(ItemsIn)]
|
||||||
|
== [I || #ps_item{id = I} <- lists:sort(ItemsOut)],
|
||||||
|
purge_node(Config, Node),
|
||||||
|
[] = get_items(Config, Node),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
test_pubsub_delete(Config) ->
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_publish_master(Config) ->
|
||||||
|
Node = create_node(Config, <<>>),
|
||||||
|
put_event(Config, Node),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
#ps_item{id = ID} = publish_item(Config, Node),
|
||||||
|
#ps_item{id = ID} = get_event(Config),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_publish_slave(Config) ->
|
||||||
|
Node = get_event(Config),
|
||||||
|
subscribe_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
#message{
|
||||||
|
sub_els =
|
||||||
|
[#ps_event{
|
||||||
|
items = #ps_items{node = Node,
|
||||||
|
items = [Item]}}]} = recv(Config),
|
||||||
|
put_event(Config, Item),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_subscriptions_master(Config) ->
|
||||||
|
Peer = ?config(slave, Config),
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
Node = create_node(Config, Node),
|
||||||
|
[] = get_subscriptions(Config, Node),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Type) ->
|
||||||
|
ok = set_subscriptions(Config, Node, [{Peer, Type}]),
|
||||||
|
#ps_item{} = publish_item(Config, Node),
|
||||||
|
case get_subscriptions(Config, Node) of
|
||||||
|
[] when Type == none; Type == pending ->
|
||||||
|
ok;
|
||||||
|
[#ps_subscription{jid = Peer, type = Type}] ->
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end, [subscribed, unconfigured, pending, none]),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_subscriptions_slave(Config) ->
|
||||||
|
wait_for_master(Config),
|
||||||
|
MyJID = my_jid(Config),
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
lists:foreach(
|
||||||
|
fun(subscribed = Type) ->
|
||||||
|
?recv2(#message{
|
||||||
|
sub_els =
|
||||||
|
[#ps_event{
|
||||||
|
subscription = #ps_subscription{
|
||||||
|
node = Node,
|
||||||
|
jid = MyJID,
|
||||||
|
type = Type}}]},
|
||||||
|
#message{sub_els = [#ps_event{}]});
|
||||||
|
(Type) ->
|
||||||
|
?recv1(#message{
|
||||||
|
sub_els =
|
||||||
|
[#ps_event{
|
||||||
|
subscription = #ps_subscription{
|
||||||
|
node = Node,
|
||||||
|
jid = MyJID,
|
||||||
|
type = Type}}]})
|
||||||
|
end, [subscribed, unconfigured, pending, none]),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_affiliations_master(Config) ->
|
||||||
|
Peer = ?config(slave, Config),
|
||||||
|
BarePeer = jid:remove_resource(Peer),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Aff) ->
|
||||||
|
Node = <<(atom_to_binary(Aff, utf8))/binary,
|
||||||
|
$-, (randoms:get_string())/binary>>,
|
||||||
|
create_node(Config, Node, default_node_config(Config)),
|
||||||
|
#ps_item{id = I} = publish_item(Config, Node),
|
||||||
|
ok = set_affiliations(Config, Node, [{Peer, Aff}]),
|
||||||
|
Affs = get_affiliations(Config, Node),
|
||||||
|
case lists:keyfind(BarePeer, #ps_affiliation.jid, Affs) of
|
||||||
|
false when Aff == none ->
|
||||||
|
ok;
|
||||||
|
#ps_affiliation{type = Aff} ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
put_event(Config, {Aff, Node, I}),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
delete_node(Config, Node)
|
||||||
|
end, [outcast, none, member, publish_only, publisher, owner]),
|
||||||
|
put_event(Config, disconnect),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_affiliations_slave(Config) ->
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config)).
|
||||||
|
|
||||||
|
pubsub_affiliations_slave(Config, {outcast, Node, ItemID}) ->
|
||||||
|
#stanza_error{reason = 'forbidden'} = subscribe_node(Config, Node),
|
||||||
|
#stanza_error{} = unsubscribe_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_items(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = publish_item(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_item(Config, Node, ItemID),
|
||||||
|
#stanza_error{reason = 'forbidden'} = purge_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_node_config(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_subscriptions(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_subscriptions(Config, Node, [{my_jid(Config), subscribed}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_affiliations(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_affiliations(Config, Node, [{?config(master, Config), outcast},
|
||||||
|
{my_jid(Config), owner}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, {none, Node, ItemID}) ->
|
||||||
|
#ps_subscription{type = subscribed} = subscribe_node(Config, Node),
|
||||||
|
ok = unsubscribe_node(Config, Node),
|
||||||
|
%% This violates the affiliation char from section 4.1
|
||||||
|
[_|_] = get_items(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = publish_item(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_item(Config, Node, ItemID),
|
||||||
|
#stanza_error{reason = 'forbidden'} = purge_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_node_config(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_subscriptions(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_subscriptions(Config, Node, [{my_jid(Config), subscribed}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_affiliations(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_affiliations(Config, Node, [{?config(master, Config), outcast},
|
||||||
|
{my_jid(Config), owner}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, {member, Node, ItemID}) ->
|
||||||
|
#ps_subscription{type = subscribed} = subscribe_node(Config, Node),
|
||||||
|
ok = unsubscribe_node(Config, Node),
|
||||||
|
[_|_] = get_items(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = publish_item(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_item(Config, Node, ItemID),
|
||||||
|
#stanza_error{reason = 'forbidden'} = purge_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_node_config(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_subscriptions(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_subscriptions(Config, Node, [{my_jid(Config), subscribed}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_affiliations(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_affiliations(Config, Node, [{?config(master, Config), outcast},
|
||||||
|
{my_jid(Config), owner}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, {publish_only, Node, ItemID}) ->
|
||||||
|
#stanza_error{reason = 'forbidden'} = subscribe_node(Config, Node),
|
||||||
|
#stanza_error{} = unsubscribe_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_items(Config, Node),
|
||||||
|
#ps_item{id = MyItemID} = publish_item(Config, Node),
|
||||||
|
%% BUG: This should be fixed
|
||||||
|
%% ?match(ok, delete_item(Config, Node, MyItemID)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_item(Config, Node, ItemID),
|
||||||
|
#stanza_error{reason = 'forbidden'} = purge_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_node_config(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_subscriptions(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_subscriptions(Config, Node, [{my_jid(Config), subscribed}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_affiliations(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_affiliations(Config, Node, [{?config(master, Config), outcast},
|
||||||
|
{my_jid(Config), owner}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, {publisher, Node, ItemID}) ->
|
||||||
|
#ps_subscription{type = subscribed} = subscribe_node(Config, Node),
|
||||||
|
ok = unsubscribe_node(Config, Node),
|
||||||
|
[_|_] = get_items(Config, Node),
|
||||||
|
#ps_item{id = MyItemID} = publish_item(Config, Node),
|
||||||
|
ok = delete_item(Config, Node, MyItemID),
|
||||||
|
%% BUG: this should be fixed
|
||||||
|
%% #stanza_error{reason = 'forbidden'} = delete_item(Config, Node, ItemID),
|
||||||
|
#stanza_error{reason = 'forbidden'} = purge_node(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_node_config(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_subscriptions(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_subscriptions(Config, Node, [{my_jid(Config), subscribed}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = get_affiliations(Config, Node),
|
||||||
|
#stanza_error{reason = 'forbidden'} =
|
||||||
|
set_affiliations(Config, Node, [{?config(master, Config), outcast},
|
||||||
|
{my_jid(Config), owner}]),
|
||||||
|
#stanza_error{reason = 'forbidden'} = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, {owner, Node, ItemID}) ->
|
||||||
|
MyJID = my_jid(Config),
|
||||||
|
Peer = ?config(master, Config),
|
||||||
|
#ps_subscription{type = subscribed} = subscribe_node(Config, Node),
|
||||||
|
ok = unsubscribe_node(Config, Node),
|
||||||
|
[_|_] = get_items(Config, Node),
|
||||||
|
#ps_item{id = MyItemID} = publish_item(Config, Node),
|
||||||
|
ok = delete_item(Config, Node, MyItemID),
|
||||||
|
ok = delete_item(Config, Node, ItemID),
|
||||||
|
ok = purge_node(Config, Node),
|
||||||
|
[_|_] = get_node_config(Config, Node),
|
||||||
|
ok = set_node_config(Config, Node, default_node_config(Config)),
|
||||||
|
ok = set_subscriptions(Config, Node, []),
|
||||||
|
[] = get_subscriptions(Config, Node),
|
||||||
|
ok = set_affiliations(Config, Node, [{Peer, outcast}, {MyJID, owner}]),
|
||||||
|
[_, _] = get_affiliations(Config, Node),
|
||||||
|
ok = delete_node(Config, Node),
|
||||||
|
wait_for_master(Config),
|
||||||
|
pubsub_affiliations_slave(Config, get_event(Config));
|
||||||
|
pubsub_affiliations_slave(Config, disconnect) ->
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_authorize_master(Config) ->
|
||||||
|
send(Config, #presence{}),
|
||||||
|
?recv1(#presence{}),
|
||||||
|
Peer = ?config(slave, Config),
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
NodeConfig = set_opts(default_node_config(Config),
|
||||||
|
[{access_model, authorize}]),
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
Node = create_node(Config, Node, NodeConfig),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
#message{sub_els = [#xdata{fields = F1}]} = recv(Config),
|
||||||
|
C1 = pubsub_subscribe_authorization:decode(F1),
|
||||||
|
Node = proplists:get_value(node, C1),
|
||||||
|
Peer = proplists:get_value(subscriber_jid, C1),
|
||||||
|
%% Deny it at first
|
||||||
|
Deny = #xdata{type = submit,
|
||||||
|
fields = pubsub_subscribe_authorization:encode(
|
||||||
|
[{node, Node},
|
||||||
|
{subscriber_jid, Peer},
|
||||||
|
{allow, false}])},
|
||||||
|
send(Config, #message{to = PJID, sub_els = [Deny]}),
|
||||||
|
%% We should not have any subscriptions
|
||||||
|
[] = get_subscriptions(Config, Node),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
#message{sub_els = [#xdata{fields = F2}]} = recv(Config),
|
||||||
|
C2 = pubsub_subscribe_authorization:decode(F2),
|
||||||
|
Node = proplists:get_value(node, C2),
|
||||||
|
Peer = proplists:get_value(subscriber_jid, C2),
|
||||||
|
%% Now we accept is as the peer is very insisting ;)
|
||||||
|
Approve = #xdata{type = submit,
|
||||||
|
fields = pubsub_subscribe_authorization:encode(
|
||||||
|
[{node, Node},
|
||||||
|
{subscriber_jid, Peer},
|
||||||
|
{allow, true}])},
|
||||||
|
send(Config, #message{to = PJID, sub_els = [Approve]}),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
delete_node(Config, Node),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
pubsub_authorize_slave(Config) ->
|
||||||
|
Node = ?config(pubsub_node, Config),
|
||||||
|
MyJID = my_jid(Config),
|
||||||
|
wait_for_master(Config),
|
||||||
|
#ps_subscription{type = pending} = subscribe_node(Config, Node),
|
||||||
|
%% We're denied at first
|
||||||
|
?recv1(#message{
|
||||||
|
sub_els =
|
||||||
|
[#ps_event{
|
||||||
|
subscription = #ps_subscription{type = none,
|
||||||
|
jid = MyJID}}]}),
|
||||||
|
wait_for_master(Config),
|
||||||
|
#ps_subscription{type = pending} = subscribe_node(Config, Node),
|
||||||
|
%% Now much better!
|
||||||
|
?recv1(#message{
|
||||||
|
sub_els =
|
||||||
|
[#ps_event{
|
||||||
|
subscription = #ps_subscription{type = subscribed,
|
||||||
|
jid = MyJID}}]}),
|
||||||
|
wait_for_master(Config),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
create_node(Config, Node) ->
|
||||||
|
create_node(Config, Node, undefined).
|
||||||
|
|
||||||
|
create_node(Config, Node, Options) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
NodeConfig = if is_list(Options) ->
|
||||||
|
#xdata{type = submit,
|
||||||
|
fields = pubsub_node_config:encode(Options)};
|
||||||
|
true ->
|
||||||
|
undefined
|
||||||
|
end,
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub{create = Node,
|
||||||
|
configure = {<<>>, NodeConfig}}]}) of
|
||||||
|
#iq{type = result, sub_els = [#pubsub{create = NewNode}]} ->
|
||||||
|
NewNode;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
delete_node(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{delete = {Node, <<>>}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
purge_node(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{purge = Node}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_default_node_config(Config) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{default = {<<>>, undefined}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub_owner{default = {<<>>, NodeConfig}}]} ->
|
||||||
|
pubsub_node_config:decode(NodeConfig#xdata.fields);
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_node_config(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{configure = {Node, undefined}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub_owner{configure = {Node, NodeConfig}}]} ->
|
||||||
|
pubsub_node_config:decode(NodeConfig#xdata.fields);
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_node_config(Config, Node, Options) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
NodeConfig = #xdata{type = submit,
|
||||||
|
fields = pubsub_node_config:encode(Options)},
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{configure =
|
||||||
|
{Node, NodeConfig}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
publish_item(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
ItemID = randoms:get_string(),
|
||||||
|
Item = #ps_item{id = ItemID, xml_els = [xmpp:encode(#presence{id = ItemID})]},
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub{publish = #ps_publish{
|
||||||
|
node = Node,
|
||||||
|
items = [Item]}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub{publish = #ps_publish{
|
||||||
|
node = Node,
|
||||||
|
items = [#ps_item{id = ItemID}]}}]} ->
|
||||||
|
Item;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_items(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub{items = #ps_items{node = Node}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub{items = #ps_items{node = Node, items = Items}}]} ->
|
||||||
|
Items;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
delete_item(Config, Node, I) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub{retract =
|
||||||
|
#ps_retract{
|
||||||
|
node = Node,
|
||||||
|
items = [#ps_item{id = I}]}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
subscribe_node(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
MyJID = my_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub{subscribe = #ps_subscribe{
|
||||||
|
node = Node,
|
||||||
|
jid = MyJID}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub{
|
||||||
|
subscription = #ps_subscription{
|
||||||
|
node = Node,
|
||||||
|
jid = MyJID} = Sub}]} ->
|
||||||
|
Sub;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
unsubscribe_node(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
MyJID = my_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub{
|
||||||
|
unsubscribe = #ps_unsubscribe{
|
||||||
|
node = Node,
|
||||||
|
jid = MyJID}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_affiliations(Config) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub{affiliations = {<<>>, []}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub{affiliations = {<<>>, Affs}}]} ->
|
||||||
|
Affs;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_affiliations(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{affiliations = {Node, []}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub_owner{affiliations = {Node, Affs}}]} ->
|
||||||
|
Affs;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_affiliations(Config, Node, JTs) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
Affs = [#ps_affiliation{jid = J, type = T} || {J, T} <- JTs],
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{affiliations =
|
||||||
|
{Node, Affs}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_subscriptions(Config) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub{subscriptions = {<<>>, []}}]}) of
|
||||||
|
#iq{type = result, sub_els = [#pubsub{subscriptions = {<<>>, Subs}}]} ->
|
||||||
|
Subs;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_subscriptions(Config, Node) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = get, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{subscriptions = {Node, []}}]}) of
|
||||||
|
#iq{type = result,
|
||||||
|
sub_els = [#pubsub_owner{subscriptions = {Node, Subs}}]} ->
|
||||||
|
Subs;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_subscriptions(Config, Node, JTs) ->
|
||||||
|
PJID = pubsub_jid(Config),
|
||||||
|
Subs = [#ps_subscription{jid = J, type = T} || {J, T} <- JTs],
|
||||||
|
case send_recv(Config,
|
||||||
|
#iq{type = set, to = PJID,
|
||||||
|
sub_els = [#pubsub_owner{subscriptions =
|
||||||
|
{Node, Subs}}]}) of
|
||||||
|
#iq{type = result, sub_els = []} ->
|
||||||
|
ok;
|
||||||
|
#iq{type = error} = IQ ->
|
||||||
|
xmpp:get_subtag(IQ, #stanza_error{})
|
||||||
|
end.
|
||||||
|
|
||||||
|
default_node_config(Config) ->
|
||||||
|
[{title, ?config(pubsub_node_title, Config)},
|
||||||
|
{notify_delete, false},
|
||||||
|
{send_last_published_item, never}].
|
||||||
|
|
||||||
mix_master(Config) ->
|
mix_master(Config) ->
|
||||||
MIX = mix_jid(Config),
|
MIX = mix_jid(Config),
|
||||||
Room = mix_room_jid(Config),
|
Room = mix_room_jid(Config),
|
||||||
@ -1627,7 +2250,7 @@ muc_mam_master(Config) ->
|
|||||||
to = Room}),
|
to = Room}),
|
||||||
%% Find the MAM field in the config and enable it
|
%% Find the MAM field in the config and enable it
|
||||||
NewFields = lists:flatmap(
|
NewFields = lists:flatmap(
|
||||||
fun(#xdata_field{var = <<"muc#roomconfig_mam">> = Var}) ->
|
fun(#xdata_field{var = <<"mam">> = Var}) ->
|
||||||
[#xdata_field{var = Var, values = [<<"1">>]}];
|
[#xdata_field{var = Var, values = [<<"1">>]}];
|
||||||
(_) ->
|
(_) ->
|
||||||
[]
|
[]
|
||||||
@ -1934,39 +2557,36 @@ muc_slave(Config) ->
|
|||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
muc_register_nick(Config, MUC, PrevNick, Nick) ->
|
muc_register_nick(Config, MUC, PrevNick, Nick) ->
|
||||||
{Registered, PrevNickVals} = if PrevNick /= <<"">> ->
|
PrevRegistered = if PrevNick /= <<"">> -> true;
|
||||||
{true, [PrevNick]};
|
true -> false
|
||||||
true ->
|
end,
|
||||||
{false, []}
|
NewRegistered = if Nick /= <<"">> -> true;
|
||||||
end,
|
true -> false
|
||||||
|
end,
|
||||||
%% Request register form
|
%% Request register form
|
||||||
#iq{type = result,
|
#iq{type = result,
|
||||||
sub_els = [#register{registered = Registered,
|
sub_els = [#register{registered = PrevRegistered,
|
||||||
xdata = #xdata{type = form,
|
xdata = #xdata{type = form,
|
||||||
fields = FsWithoutNick}}]} =
|
fields = FsWithoutNick}}]} =
|
||||||
send_recv(Config, #iq{type = get, to = MUC,
|
send_recv(Config, #iq{type = get, to = MUC,
|
||||||
sub_els = [#register{}]}),
|
sub_els = [#register{}]}),
|
||||||
%% Check if 'nick' field presents
|
%% Check if previous nick is registered
|
||||||
#xdata_field{type = 'text-single',
|
PrevNick = proplists:get_value(
|
||||||
var = <<"nick">>,
|
roomnick, muc_register:decode(FsWithoutNick)),
|
||||||
values = PrevNickVals} =
|
X = #xdata{type = submit, fields = muc_register:encode([{roomnick, Nick}])},
|
||||||
lists:keyfind(<<"nick">>, #xdata_field.var, FsWithoutNick),
|
|
||||||
X = #xdata{type = submit,
|
|
||||||
fields = [#xdata_field{var = <<"nick">>, values = [Nick]}]},
|
|
||||||
%% Submitting form
|
%% Submitting form
|
||||||
#iq{type = result, sub_els = []} =
|
#iq{type = result, sub_els = []} =
|
||||||
send_recv(Config, #iq{type = set, to = MUC,
|
send_recv(Config, #iq{type = set, to = MUC,
|
||||||
sub_els = [#register{xdata = X}]}),
|
sub_els = [#register{xdata = X}]}),
|
||||||
%% Check if the nick was registered
|
%% Check if new nick was registered
|
||||||
#iq{type = result,
|
#iq{type = result,
|
||||||
sub_els = [#register{registered = true,
|
sub_els = [#register{registered = NewRegistered,
|
||||||
xdata = #xdata{type = form,
|
xdata = #xdata{type = form,
|
||||||
fields = FsWithNick}}]} =
|
fields = FsWithNick}}]} =
|
||||||
send_recv(Config, #iq{type = get, to = MUC,
|
send_recv(Config, #iq{type = get, to = MUC,
|
||||||
sub_els = [#register{}]}),
|
sub_els = [#register{}]}),
|
||||||
#xdata_field{type = 'text-single', var = <<"nick">>,
|
Nick = proplists:get_value(
|
||||||
values = [Nick]} =
|
roomnick, muc_register:decode(FsWithNick)).
|
||||||
lists:keyfind(<<"nick">>, #xdata_field.var, FsWithNick).
|
|
||||||
|
|
||||||
muc_register_master(Config) ->
|
muc_register_master(Config) ->
|
||||||
MUC = muc_jid(Config),
|
MUC = muc_jid(Config),
|
||||||
@ -1980,17 +2600,23 @@ muc_register_master(Config) ->
|
|||||||
muc_register_nick(Config, MUC, <<"">>, <<"master2">>),
|
muc_register_nick(Config, MUC, <<"">>, <<"master2">>),
|
||||||
%% Now register nick "master"
|
%% Now register nick "master"
|
||||||
muc_register_nick(Config, MUC, <<"master2">>, <<"master">>),
|
muc_register_nick(Config, MUC, <<"master2">>, <<"master">>),
|
||||||
|
%% Wait for slave to fail trying to register nick "master"
|
||||||
|
wait_for_slave(Config),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
%% Now register empty ("") nick, which means we're unregistering
|
||||||
|
muc_register_nick(Config, MUC, <<"master">>, <<"">>),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
muc_register_slave(Config) ->
|
muc_register_slave(Config) ->
|
||||||
MUC = muc_jid(Config),
|
MUC = muc_jid(Config),
|
||||||
|
wait_for_master(Config),
|
||||||
%% Trying to register occupied nick "master"
|
%% Trying to register occupied nick "master"
|
||||||
X = #xdata{type = submit,
|
Fs = muc_register:encode([{roomnick, <<"master">>}]),
|
||||||
fields = [#xdata_field{var = <<"nick">>,
|
X = #xdata{type = submit, fields = Fs},
|
||||||
values = [<<"master">>]}]},
|
|
||||||
#iq{type = error} =
|
#iq{type = error} =
|
||||||
send_recv(Config, #iq{type = set, to = MUC,
|
send_recv(Config, #iq{type = set, to = MUC,
|
||||||
sub_els = [#register{xdata = X}]}),
|
sub_els = [#register{xdata = X}]}),
|
||||||
|
wait_for_master(Config),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
announce_master(Config) ->
|
announce_master(Config) ->
|
||||||
@ -2741,6 +3367,12 @@ socks5_send(Sock, Data) ->
|
|||||||
socks5_recv(Sock, Data) ->
|
socks5_recv(Sock, Data) ->
|
||||||
{ok, Data} = gen_tcp:recv(Sock, size(Data)).
|
{ok, Data} = gen_tcp:recv(Sock, size(Data)).
|
||||||
|
|
||||||
|
set_opts(Config, Options) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun({Opt, Val}, Acc) ->
|
||||||
|
lists:keystore(Opt, 1, Acc, {Opt, Val})
|
||||||
|
end, Config, Options).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% SQL stuff
|
%%% SQL stuff
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -86,6 +86,8 @@ init_config(Config) ->
|
|||||||
{lang, <<"en">>},
|
{lang, <<"en">>},
|
||||||
{base_dir, BaseDir},
|
{base_dir, BaseDir},
|
||||||
{socket, undefined},
|
{socket, undefined},
|
||||||
|
{pubsub_node, <<"node!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
|
{pubsub_node_title, <<"title!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
|
@ -66,6 +66,14 @@
|
|||||||
end
|
end
|
||||||
end)()).
|
end)()).
|
||||||
|
|
||||||
|
-define(match(Pattern, Result),
|
||||||
|
case Result of
|
||||||
|
Pattern ->
|
||||||
|
Pattern;
|
||||||
|
Mismatch ->
|
||||||
|
suite:match_failure([Mismatch], [??Pattern])
|
||||||
|
end).
|
||||||
|
|
||||||
-define(COMMON_VHOST, <<"localhost">>).
|
-define(COMMON_VHOST, <<"localhost">>).
|
||||||
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
|
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
|
||||||
-define(REDIS_VHOST, <<"redis.localhost">>).
|
-define(REDIS_VHOST, <<"redis.localhost">>).
|
||||||
|
Loading…
Reference in New Issue
Block a user