From 6a3691ef7ce33c17d379ba40fa0c27f21c5ed75d Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 7 Oct 2016 10:31:03 +0300 Subject: Add xdata generator and make some code using it --- Makefile.in | 6 +- include/flex_offline.hrl | 10 + include/muc_register.hrl | 16 + include/muc_request.hrl | 16 + include/muc_roomconfig.hrl | 55 + include/muc_roominfo.hrl | 18 + include/pubsub_get_pending.hrl | 13 + include/pubsub_node_config.hrl | 60 + include/pubsub_publish_options.hrl | 14 + include/pubsub_subscribe_authorization.hrl | 13 + include/pubsub_subscribe_options.hrl | 25 + include/xmpp_codec.hrl | 4 +- specs/flex_offline.cfg | 1 + specs/flex_offline.xdata | 12 + specs/muc_register.cfg | 2 + specs/muc_register.xdata | 37 + specs/muc_request.cfg | 2 + specs/muc_request.xdata | 31 + specs/muc_roomconfig.cfg | 11 + specs/muc_roomconfig.xdata | 192 ++ specs/muc_roominfo.cfg | 11 + specs/muc_roominfo.xdata | 55 + specs/pubsub_get_pending.cfg | 7 + specs/pubsub_get_pending.xdata | 15 + specs/pubsub_node_config.cfg | 8 + specs/pubsub_node_config.xdata | 189 ++ specs/pubsub_publish_options.cfg | 6 + specs/pubsub_publish_options.xdata | 34 + specs/pubsub_subscribe_authorization.cfg | 7 + specs/pubsub_subscribe_authorization.xdata | 27 + specs/pubsub_subscribe_options.cfg | 1 + specs/pubsub_subscribe_options.xdata | 70 + specs/xmpp_codec.spec | 3522 ++++++++++++++++++++++++++++ src/ejabberd_captcha.erl | 1 + src/flex_offline.erl | 128 + src/mod_caps.erl | 13 +- src/mod_mam.erl | 40 +- src/mod_muc.erl | 30 +- src/mod_muc_room.erl | 933 +++----- src/mod_offline.erl | 11 +- src/mod_pubsub.erl | 637 ++--- src/muc_register.erl | 364 +++ src/muc_request.erl | 269 +++ src/muc_roomconfig.erl | 1675 +++++++++++++ src/muc_roominfo.erl | 491 ++++ src/node_flat.erl | 3 +- src/node_flat_sql.erl | 2 +- src/pubsub_get_pending.erl | 130 + src/pubsub_node_config.erl | 1666 +++++++++++++ src/pubsub_publish_options.erl | 157 ++ src/pubsub_subscribe_authorization.erl | 279 +++ src/pubsub_subscribe_options.erl | 508 ++++ src/xdata_codec.erl | 648 +++++ src/xmpp_codec.erl | 33 +- src/xmpp_util.erl | 60 +- test/ejabberd_SUITE.erl | 888 ++++++- test/suite.erl | 2 + test/suite.hrl | 8 + tools/xmpp_codec.spec | 3507 --------------------------- 59 files changed, 12219 insertions(+), 4754 deletions(-) create mode 100644 include/flex_offline.hrl create mode 100644 include/muc_register.hrl create mode 100644 include/muc_request.hrl create mode 100644 include/muc_roomconfig.hrl create mode 100644 include/muc_roominfo.hrl create mode 100644 include/pubsub_get_pending.hrl create mode 100644 include/pubsub_node_config.hrl create mode 100644 include/pubsub_publish_options.hrl create mode 100644 include/pubsub_subscribe_authorization.hrl create mode 100644 include/pubsub_subscribe_options.hrl create mode 100644 specs/flex_offline.cfg create mode 100644 specs/flex_offline.xdata create mode 100644 specs/muc_register.cfg create mode 100644 specs/muc_register.xdata create mode 100644 specs/muc_request.cfg create mode 100644 specs/muc_request.xdata create mode 100644 specs/muc_roomconfig.cfg create mode 100644 specs/muc_roomconfig.xdata create mode 100644 specs/muc_roominfo.cfg create mode 100644 specs/muc_roominfo.xdata create mode 100644 specs/pubsub_get_pending.cfg create mode 100644 specs/pubsub_get_pending.xdata create mode 100644 specs/pubsub_node_config.cfg create mode 100644 specs/pubsub_node_config.xdata create mode 100644 specs/pubsub_publish_options.cfg create mode 100644 specs/pubsub_publish_options.xdata create mode 100644 specs/pubsub_subscribe_authorization.cfg create mode 100644 specs/pubsub_subscribe_authorization.xdata create mode 100644 specs/pubsub_subscribe_options.cfg create mode 100644 specs/pubsub_subscribe_options.xdata create mode 100644 specs/xmpp_codec.spec create mode 100644 src/flex_offline.erl create mode 100644 src/muc_register.erl create mode 100644 src/muc_request.erl create mode 100644 src/muc_roomconfig.erl create mode 100644 src/muc_roominfo.erl create mode 100644 src/pubsub_get_pending.erl create mode 100644 src/pubsub_node_config.erl create mode 100644 src/pubsub_publish_options.erl create mode 100644 src/pubsub_subscribe_authorization.erl create mode 100644 src/pubsub_subscribe_options.erl create mode 100644 src/xdata_codec.erl delete mode 100644 tools/xmpp_codec.spec diff --git a/Makefile.in b/Makefile.in index 07502f06e..728b2fa98 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,7 +110,11 @@ edoc: spec: $(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)) diff --git a/include/flex_offline.hrl b/include/flex_offline.hrl new file mode 100644 index 000000000..74a38fbb3 --- /dev/null +++ b/include/flex_offline.hrl @@ -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()]. diff --git a/include/muc_register.hrl b/include/muc_register.hrl new file mode 100644 index 000000000..0cfc928c2 --- /dev/null +++ b/include/muc_register.hrl @@ -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()]. diff --git a/include/muc_request.hrl b/include/muc_request.hrl new file mode 100644 index 000000000..bc14be35f --- /dev/null +++ b/include/muc_request.hrl @@ -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()]. diff --git a/include/muc_roomconfig.hrl b/include/muc_roomconfig.hrl new file mode 100644 index 000000000..89cfc3346 --- /dev/null +++ b/include/muc_roomconfig.hrl @@ -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()]. diff --git a/include/muc_roominfo.hrl b/include/muc_roominfo.hrl new file mode 100644 index 000000000..cf4f4ebf0 --- /dev/null +++ b/include/muc_roominfo.hrl @@ -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()]. diff --git a/include/pubsub_get_pending.hrl b/include/pubsub_get_pending.hrl new file mode 100644 index 000000000..4ddf9bad0 --- /dev/null +++ b/include/pubsub_get_pending.hrl @@ -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()]. diff --git a/include/pubsub_node_config.hrl b/include/pubsub_node_config.hrl new file mode 100644 index 000000000..e1519cdc0 --- /dev/null +++ b/include/pubsub_node_config.hrl @@ -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()]. diff --git a/include/pubsub_publish_options.hrl b/include/pubsub_publish_options.hrl new file mode 100644 index 000000000..3b04b4826 --- /dev/null +++ b/include/pubsub_publish_options.hrl @@ -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()]. diff --git a/include/pubsub_subscribe_authorization.hrl b/include/pubsub_subscribe_authorization.hrl new file mode 100644 index 000000000..fb67ab47a --- /dev/null +++ b/include/pubsub_subscribe_authorization.hrl @@ -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()]. diff --git a/include/pubsub_subscribe_options.hrl b/include/pubsub_subscribe_options.hrl new file mode 100644 index 000000000..9a05822a5 --- /dev/null +++ b/include/pubsub_subscribe_options.hrl @@ -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()]. diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index 8ffb37808..443769bb7 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -8,7 +8,7 @@ -record(ps_affiliation, {xmlns = <<>> :: binary(), node = <<>> :: binary(), type :: member | none | outcast | - owner | publisher | 'publish-only', + owner | publisher | publish_only, jid :: jid:jid()}). -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', var = <<>> :: binary(), required = false :: boolean(), - desc :: binary(), + desc = <<>> :: binary(), values = [] :: [binary()], options = [] :: [#xdata_option{}], sub_els = [] :: [xmpp_element() | fxml:xmlel()]}). diff --git a/specs/flex_offline.cfg b/specs/flex_offline.cfg new file mode 100644 index 000000000..5d5b2685e --- /dev/null +++ b/specs/flex_offline.cfg @@ -0,0 +1 @@ +[{decode, [{<<"number_of_messages">>, {dec_int, [0, infinity]}}]}]. diff --git a/specs/flex_offline.xdata b/specs/flex_offline.xdata new file mode 100644 index 000000000..d0c786289 --- /dev/null +++ b/specs/flex_offline.xdata @@ -0,0 +1,12 @@ + + http://jabber.org/protocol/offline + XEP-0013 + + Service Discovery extension for number of messages + in an offline message queue. + + + diff --git a/specs/muc_register.cfg b/specs/muc_register.cfg new file mode 100644 index 000000000..3efd0cf0b --- /dev/null +++ b/specs/muc_register.cfg @@ -0,0 +1,2 @@ +[{prefix, <<"muc#register_">>}, + {required, [<<"muc#register_roomnick">>]}]. diff --git a/specs/muc_register.xdata b/specs/muc_register.xdata new file mode 100644 index 000000000..e808b44e0 --- /dev/null +++ b/specs/muc_register.xdata @@ -0,0 +1,37 @@ + + http://jabber.org/protocol/muc#register + XEP-0045 + + Forms enabling user registration with a + Multi-User Chat (MUC) room or admin approval + of user registration requests. + + + + + + + + + diff --git a/specs/muc_request.cfg b/specs/muc_request.cfg new file mode 100644 index 000000000..4811b32e2 --- /dev/null +++ b/specs/muc_request.cfg @@ -0,0 +1,2 @@ +[{prefix, <<"muc#">>}, + {required, [<<"muc#role">>]}]. diff --git a/specs/muc_request.xdata b/specs/muc_request.xdata new file mode 100644 index 000000000..64fa1388b --- /dev/null +++ b/specs/muc_request.xdata @@ -0,0 +1,31 @@ + + http://jabber.org/protocol/muc#request + XEP-0045 + + Forms enabling voice requests in a + Multi-User Chat (MUC) room or admin + approval of such requests. + + + + + + + + + + diff --git a/specs/muc_roomconfig.cfg b/specs/muc_roomconfig.cfg new file mode 100644 index 000000000..de16099d5 --- /dev/null +++ b/specs/muc_roomconfig.cfg @@ -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: diff --git a/specs/muc_roomconfig.xdata b/specs/muc_roomconfig.xdata new file mode 100644 index 000000000..cab6465dc --- /dev/null +++ b/specs/muc_roomconfig.xdata @@ -0,0 +1,192 @@ + + http://jabber.org/protocol/muc#roomconfig + XEP-0045 + + Forms enabling creation and configuration of + a Multi-User Chat (MUC) room. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/specs/muc_roominfo.cfg b/specs/muc_roominfo.cfg new file mode 100644 index 000000000..98a985e7b --- /dev/null +++ b/specs/muc_roominfo.cfg @@ -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: diff --git a/specs/muc_roominfo.xdata b/specs/muc_roominfo.xdata new file mode 100644 index 000000000..70f696383 --- /dev/null +++ b/specs/muc_roominfo.xdata @@ -0,0 +1,55 @@ + + http://jabber.org/protocol/muc#roominfo + XEP-0045 + + Forms enabling the communication of extended service discovery + information about a Multi-User Chat (MUC) room. + + + + + + + + + + + + + diff --git a/specs/pubsub_get_pending.cfg b/specs/pubsub_get_pending.cfg new file mode 100644 index 000000000..4ae57ac88 --- /dev/null +++ b/specs/pubsub_get_pending.cfg @@ -0,0 +1,7 @@ +[{prefix, <<"pubsub#">>}, + {required, [<<"pubsub#node">>]}]. + +%% Local Variables: +%% mode: erlang +%% End: +%% vim: set filetype=erlang tabstop=8: diff --git a/specs/pubsub_get_pending.xdata b/specs/pubsub_get_pending.xdata new file mode 100644 index 000000000..1a6f55297 --- /dev/null +++ b/specs/pubsub_get_pending.xdata @@ -0,0 +1,15 @@ + + http://jabber.org/protocol/pubsub#subscribe_authorization + XEP-0060 + Forms enabling authorization of subscriptions to pubsub nodes + + + + diff --git a/specs/pubsub_node_config.cfg b/specs/pubsub_node_config.cfg new file mode 100644 index 000000000..f3bf72d2d --- /dev/null +++ b/specs/pubsub_node_config.cfg @@ -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: diff --git a/specs/pubsub_node_config.xdata b/specs/pubsub_node_config.xdata new file mode 100644 index 000000000..1691808f7 --- /dev/null +++ b/specs/pubsub_node_config.xdata @@ -0,0 +1,189 @@ + + http://jabber.org/protocol/pubsub#node_config + XEP-0060 + Forms enabling configuration of pubsub nodes + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + diff --git a/specs/pubsub_publish_options.cfg b/specs/pubsub_publish_options.cfg new file mode 100644 index 000000000..2a853e834 --- /dev/null +++ b/specs/pubsub_publish_options.cfg @@ -0,0 +1,6 @@ +[{prefix, <<"pubsub#">>}]. + +%% Local Variables: +%% mode: erlang +%% End: +%% vim: set filetype=erlang tabstop=8: diff --git a/specs/pubsub_publish_options.xdata b/specs/pubsub_publish_options.xdata new file mode 100644 index 000000000..26cac6ad5 --- /dev/null +++ b/specs/pubsub_publish_options.xdata @@ -0,0 +1,34 @@ + + http://jabber.org/protocol/pubsub#publish-options + XEP-0060 + + 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. + + + + + + + + + + + diff --git a/specs/pubsub_subscribe_authorization.cfg b/specs/pubsub_subscribe_authorization.cfg new file mode 100644 index 000000000..1f38fdb27 --- /dev/null +++ b/specs/pubsub_subscribe_authorization.cfg @@ -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: diff --git a/specs/pubsub_subscribe_authorization.xdata b/specs/pubsub_subscribe_authorization.xdata new file mode 100644 index 000000000..250d8754b --- /dev/null +++ b/specs/pubsub_subscribe_authorization.xdata @@ -0,0 +1,27 @@ + + http://jabber.org/protocol/pubsub#subscribe_authorization + XEP-0060 + Forms enabling authorization of subscriptions to pubsub nodes + + + + + + + diff --git a/specs/pubsub_subscribe_options.cfg b/specs/pubsub_subscribe_options.cfg new file mode 100644 index 000000000..e4ac06ad9 --- /dev/null +++ b/specs/pubsub_subscribe_options.cfg @@ -0,0 +1 @@ +[{prefix, <<"pubsub#">>}]. diff --git a/specs/pubsub_subscribe_options.xdata b/specs/pubsub_subscribe_options.xdata new file mode 100644 index 000000000..59b134676 --- /dev/null +++ b/specs/pubsub_subscribe_options.xdata @@ -0,0 +1,70 @@ + + http://jabber.org/protocol/pubsub#subscribe_options + XEP-0060 + Forms enabling configuration of subscription options for pubsub nodes + + + + + + + + + + + + + + + + + + + + + diff --git a/specs/xmpp_codec.spec b/specs/xmpp_codec.spec new file mode 100644 index 000000000..81f674bf3 --- /dev/null +++ b/specs/xmpp_codec.spec @@ -0,0 +1,3522 @@ +-xml(last, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:last">>, + result = {last, '$seconds', '$status'}, + attrs = [#attr{name = <<"seconds">>, + enc = {enc_int, []}, + dec = {dec_int, [0, infinity]}}], + cdata = #cdata{default = <<"">>, label = '$status'}}). + +-xml(version_name, + #elem{name = <<"name">>, + xmlns = <<"jabber:iq:version">>, + result = '$cdata', + cdata = #cdata{label = '$cdata', required = true}}). + +-xml(version_ver, + #elem{name = <<"version">>, + xmlns = <<"jabber:iq:version">>, + result = '$cdata', + cdata = #cdata{label = '$cdata', required = true}}). + +-xml(version_os, + #elem{name = <<"os">>, + xmlns = <<"jabber:iq:version">>, + result = '$cdata', + cdata = #cdata{label = '$cdata', required = true}}). + +-xml(version, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:version">>, + result = {version, '$name', '$ver', '$os'}, + refs = [#ref{name = version_name, + label = '$name', + min = 0, max = 1}, + #ref{name = version_ver, + label = '$ver', + min = 0, max = 1}, + #ref{name = version_os, + label = '$os', + min = 0, max = 1}]}). + +-xml(roster_group, + #elem{name = <<"group">>, + xmlns = <<"jabber:iq:roster">>, + result = '$cdata', + cdata = #cdata{required = true, label = '$cdata'}}). + +-xml(roster_item, + #elem{name = <<"item">>, + xmlns = <<"jabber:iq:roster">>, + result = {roster_item, '$jid', '$name', + '$groups', '$subscription', '$ask'}, + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"name">>, + default = <<"">>}, + #attr{name = <<"subscription">>, + default = none, + enc = {enc_enum, []}, + dec = {dec_enum, [[none,to,from,both,remove]]}}, + #attr{name = <<"ask">>, + enc = {enc_enum, []}, + dec = {dec_enum, [[subscribe]]}}], + refs = [#ref{name = roster_group, label = '$groups'}]}). + +-xml(roster_query, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:roster">>, + result = {roster_query, '$items', '$ver'}, + attrs = [#attr{name = <<"ver">>, default = undefined}], + refs = [#ref{name = roster_item, label = '$items'}]}). + +-xml(rosterver_feature, + #elem{name = <<"ver">>, + xmlns = <<"urn:xmpp:features:rosterver">>, + result = {rosterver_feature}}). + +-xml(privacy_message, #elem{name = <<"message">>, xmlns = <<"jabber:iq:privacy">>, + result = true}). +-xml(privacy_iq, #elem{name = <<"iq">>, xmlns = <<"jabber:iq:privacy">>, + result = true}). +-xml(privacy_presence_in, #elem{name = <<"presence-in">>, + xmlns = <<"jabber:iq:privacy">>, + result = true}). +-xml(privacy_presence_out, #elem{name = <<"presence-out">>, + xmlns = <<"jabber:iq:privacy">>, + result = true}). + +-xml(privacy_item, + #elem{name = <<"item">>, + xmlns = <<"jabber:iq:privacy">>, + result = {privacy_item, '$order', '$action', '$type', '$value', + '$message', '$iq', '$presence_in', '$presence_out'}, + attrs = [#attr{name = <<"action">>, + required = true, + dec = {dec_enum, [[allow, deny]]}, + enc = {enc_enum, []}}, + #attr{name = <<"order">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"type">>, + dec = {dec_enum, [[group, jid, subscription]]}, + enc = {enc_enum, []}}, + #attr{name = <<"value">>}], + refs = [#ref{name = privacy_message, default = false, + min = 0, max = 1, label = '$message'}, + #ref{name = privacy_iq, default = false, + min = 0, max = 1, label = '$iq'}, + #ref{name = privacy_presence_in, default = false, + min = 0, max = 1, label = '$presence_in'}, + #ref{name = privacy_presence_out, default = false, + min = 0, max = 1, label = '$presence_out'}]}). + +-xml(privacy_list, + #elem{name = <<"list">>, + xmlns = <<"jabber:iq:privacy">>, + result = {privacy_list, '$name', '$items'}, + attrs = [#attr{name = <<"name">>, + required = true}], + refs = [#ref{name = privacy_item, + label = '$items'}]}). + +-xml(privacy_default_list, + #elem{name = <<"default">>, + xmlns = <<"jabber:iq:privacy">>, + result = '$name', + attrs = [#attr{name = <<"name">>, + default = none}]}). + +-xml(privacy_active_list, + #elem{name = <<"active">>, + xmlns = <<"jabber:iq:privacy">>, + result = '$name', + attrs = [#attr{name = <<"name">>, + default = none}]}). + +-xml(privacy, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:privacy">>, + result = {privacy_query, '$lists', '$default', '$active'}, + refs = [#ref{name = privacy_list, + label = '$lists'}, + #ref{name = privacy_default_list, + min = 0, max = 1, + label = '$default'}, + #ref{name = privacy_active_list, + min = 0, max = 1, + label = '$active'}]}). + +-xml(block_item, + #elem{name = <<"item">>, + xmlns = <<"urn:xmpp:blocking">>, + result = '$jid', + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(block, + #elem{name = <<"block">>, + xmlns = <<"urn:xmpp:blocking">>, + result = {block, '$items'}, + refs = [#ref{name = block_item, + label = '$items'}]}). + +-xml(unblock, + #elem{name = <<"unblock">>, + xmlns = <<"urn:xmpp:blocking">>, + result = {unblock, '$items'}, + refs = [#ref{name = block_item, + label = '$items'}]}). + +-xml(block_list, + #elem{name = <<"blocklist">>, + xmlns = <<"urn:xmpp:blocking">>, + result = {block_list, '$items'}, + refs = [#ref{name = block_item, + label = '$items'}]}). + +-xml(disco_identity, + #elem{name = <<"identity">>, + xmlns = <<"http://jabber.org/protocol/disco#info">>, + result = {identity, '$category', '$type', '$lang', '$name'}, + attrs = [#attr{name = <<"category">>, + required = true}, + #attr{name = <<"type">>, + required = true}, + #attr{name = <<"xml:lang">>, + label = '$lang'}, + #attr{name = <<"name">>}]}). + +-xml(disco_feature, + #elem{name = <<"feature">>, + xmlns = <<"http://jabber.org/protocol/disco#info">>, + result = '$var', + attrs = [#attr{name = <<"var">>, + required = true}]}). + +-xml(disco_info, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/disco#info">>, + result = {disco_info, '$node', '$identities', '$features', '$xdata'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = disco_identity, + label = '$identities'}, + #ref{name = disco_feature, + label = '$features'}, + #ref{name = xdata, + label = '$xdata'}]}). + +-xml(disco_item, + #elem{name = <<"item">>, + xmlns = <<"http://jabber.org/protocol/disco#items">>, + result = {disco_item, '$jid', '$name', '$node'}, + attrs = [#attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}, + required = true}, + #attr{name = <<"name">>}, + #attr{name = <<"node">>}]}). +-xml(disco_items, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/disco#items">>, + result = {disco_items, '$node', '$items', '$rsm'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = disco_item, + label = '$items'}, + #ref{name = rsm_set, min = 0, max = 1, + label = '$rsm'}]}). + +-xml(private, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:private">>, + result = {private, '$_xmls'}}). + +-xml(conference_nick, + #elem{name = <<"nick">>, + xmlns = <<"storage:bookmarks">>, + result = '$cdata'}). + +-xml(conference_password, + #elem{name = <<"password">>, + xmlns = <<"storage:bookmarks">>, + result = '$cdata'}). + +-xml(bookmark_conference, + #elem{name = <<"conference">>, + xmlns = <<"storage:bookmarks">>, + result = {bookmark_conference, '$name', '$jid', + '$autojoin', '$nick', '$password'}, + attrs = [#attr{name = <<"name">>, + required = true}, + #attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"autojoin">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}], + refs = [#ref{name = conference_nick, + label = '$nick', + min = 0, max = 1}, + #ref{name = conference_password, + label = '$password', + min = 0, max = 1}]}). + +-xml(bookmark_url, + #elem{name = <<"url">>, + xmlns = <<"storage:bookmarks">>, + result = {bookmark_url, '$name', '$url'}, + attrs = [#attr{name = <<"name">>, + required = true}, + #attr{name = <<"url">>, + required = true}]}). + +-xml(bookmarks_storage, + #elem{name = <<"storage">>, + xmlns = <<"storage:bookmarks">>, + result = {bookmark_storage, '$conference', '$url'}, + refs = [#ref{name = bookmark_conference, + label = '$conference'}, + #ref{name = bookmark_url, + label = '$url'}]}). + +-xml(stat_error, + #elem{name = <<"error">>, + xmlns = <<"http://jabber.org/protocol/stats">>, + result = {stat_error, '$code', '$reason'}, + cdata = #cdata{default = <<"">>, label = '$reason'}, + attrs = [#attr{name = <<"code">>, + required = true, + enc = {enc_int, []}, + dec = {dec_int, []}}]}). + +-xml(stat, + #elem{name = <<"stat">>, + xmlns = <<"http://jabber.org/protocol/stats">>, + result = {stat, '$name', '$units', '$value', '$error'}, + attrs = [#attr{name = <<"name">>, + required = true}, + #attr{name = <<"units">>, default = <<"">>}, + #attr{name = <<"value">>, default = <<"">>}], + refs = [#ref{name = stat_error, label = '$error', + min = 0, max = 1}]}). + +-xml(stats, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/stats">>, + result = {stats, '$list', '$node'}, + attrs = [#attr{name = <<"node">>, default = <<"">>}], + refs = [#ref{name = stat, label = '$list'}]}). + +-xml(iq, + #elem{name = <<"iq">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {iq, '$id', '$type', '$lang', '$from', '$to', '$_els'}, + attrs = [#attr{name = <<"id">>, + required = true}, + #attr{name = <<"type">>, + required = true, + enc = {enc_enum, []}, + dec = {dec_enum, [[get, set, result, error]]}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xml:lang">>, + label = '$lang'}]}). + +-xml(message_subject, + #elem{name = <<"subject">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}). + +-xml(message_body, + #elem{name = <<"body">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}). + +-xml(message_thread, + #elem{name = <<"thread">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = '$cdata'}). + +-xml(message, + #elem{name = <<"message">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {message, '$id', '$type', '$lang', '$from', '$to', + '$subject', '$body', '$thread', '$_els'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"type">>, + default = normal, + enc = {enc_enum, []}, + dec = {dec_enum, [[chat, normal, groupchat, + headline, error]]}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xml:lang">>, + label = '$lang'}], + refs = [#ref{name = message_subject, label = '$subject'}, + #ref{name = message_thread, min = 0, max = 1, label = '$thread'}, + #ref{name = message_body, label = '$body'}]}). + +-xml(presence_show, + #elem{name = <<"show">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = '$cdata', + cdata = #cdata{enc = {enc_enum, []}, + dec = {dec_enum, [[away, chat, dnd, xa]]}}}). + +-xml(presence_status, + #elem{name = <<"status">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + attrs = [#attr{name = <<"xml:lang">>, + label = '$lang'}]}). + +-xml(presence_priority, + #elem{name = <<"priority">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = '$cdata', + cdata = #cdata{enc = {enc_int, []}, + dec = {dec_int, []}}}). + +-xml(presence, + #elem{name = <<"presence">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {presence, '$id', '$type', '$lang', '$from', '$to', + '$show', '$status', '$priority', '$_els'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"type">>, + default = available, + enc = {enc_enum, []}, + dec = {dec_enum, [[unavailable, subscribe, subscribed, + unsubscribe, unsubscribed, + available, probe, error]]}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xml:lang">>, + label = '$lang'}], + refs = [#ref{name = presence_show, min = 0, max = 1, label = '$show'}, + #ref{name = presence_status, label = '$status'}, + #ref{name = presence_priority, min = 0, max = 1, + label = '$priority'}]}). + +-xml(error_bad_request, + #elem{name = <<"bad-request">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'bad-request'}). +-xml(error_conflict, + #elem{name = <<"conflict">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'conflict'}). +-xml(error_feature_not_implemented, + #elem{name = <<"feature-not-implemented">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'feature-not-implemented'}). +-xml(error_forbidden, + #elem{name = <<"forbidden">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'forbidden'}). +-xml(error_gone, + #elem{name = <<"gone">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + cdata = #cdata{label = '$uri'}, + result = {'gone', '$uri'}}). +-xml(error_internal_server_error, + #elem{name = <<"internal-server-error">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'internal-server-error'}). +-xml(error_item_not_found, + #elem{name = <<"item-not-found">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'item-not-found'}). +-xml(error_jid_malformed, + #elem{name = <<"jid-malformed">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'jid-malformed'}). +-xml(error_not_acceptable, + #elem{name = <<"not-acceptable">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'not-acceptable'}). +-xml(error_not_allowed, + #elem{name = <<"not-allowed">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'not-allowed'}). +-xml(error_not_authorized, + #elem{name = <<"not-authorized">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'not-authorized'}). +-xml(error_payment_required, + #elem{name = <<"payment-required">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'payment-required'}). +-xml(error_policy_violation, + #elem{name = <<"policy-violation">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'policy-violation'}). +-xml(error_recipient_unavailable, + #elem{name = <<"recipient-unavailable">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'recipient-unavailable'}). +-xml(error_redirect, + #elem{name = <<"redirect">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + cdata = #cdata{label = '$uri'}, + result = {'redirect', '$uri'}}). +-xml(error_registration_required, + #elem{name = <<"registration-required">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'registration-required'}). +-xml(error_remote_server_not_found, + #elem{name = <<"remote-server-not-found">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'remote-server-not-found'}). +-xml(error_remote_server_timeout, + #elem{name = <<"remote-server-timeout">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'remote-server-timeout'}). +-xml(error_resource_constraint, + #elem{name = <<"resource-constraint">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'resource-constraint'}). +-xml(error_service_unavailable, + #elem{name = <<"service-unavailable">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'service-unavailable'}). +-xml(error_subscription_required, + #elem{name = <<"subscription-required">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'subscription-required'}). +-xml(error_undefined_condition, + #elem{name = <<"undefined-condition">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'undefined-condition'}). +-xml(error_unexpected_request, + #elem{name = <<"unexpected-request">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + result = 'unexpected-request'}). + +-xml(error_text, + #elem{name = <<"text">>, + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, + attrs = [#attr{name = <<"xml:lang">>, + label = '$lang'}]}). + +-xml(error, + #elem{name = <<"error">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {stanza_error, '$type', '$code', '$by', '$reason', '$text', '$_els'}, + attrs = [#attr{name = <<"type">>, + label = '$type', + required = true, + dec = {dec_enum, [[auth, cancel, continue, + modify, wait]]}, + enc = {enc_enum, []}}, + #attr{name = <<"code">>, + label = '$code', + enc = {enc_int, []}, + dec = {dec_int, [0, infinity]}}, + #attr{name = <<"by">>}], + refs = [#ref{name = error_text, + min = 0, max = 1, label = '$text'}, + #ref{name = error_bad_request, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_conflict, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_feature_not_implemented, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_forbidden, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_gone, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_internal_server_error, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_item_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_jid_malformed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_acceptable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_allowed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_authorized, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_payment_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_policy_violation, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_recipient_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_redirect, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_registration_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_timeout, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_resource_constraint, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_service_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_subscription_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_undefined_condition, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_unexpected_request, + min = 0, max = 1, label = '$reason'}]}). + +-xml(bind_jid, + #elem{name = <<"jid">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, + result = '$cdata', + cdata = #cdata{dec = {dec_jid, []}, + enc = {enc_jid, []}}}). + +-xml(bind_resource, + #elem{name = <<"resource">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, + result = '$cdata', + cdata = #cdata{dec = {resourceprep, []}, + enc = {resourceprep, []}}}). + +-xml(bind, #elem{name = <<"bind">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, + result = {bind, '$jid', '$resource'}, + refs = [#ref{name = bind_jid, + label = '$jid', + min = 0, max = 1}, + #ref{name = bind_resource, + min = 0, max = 1, + default = <<"">>, + label = '$resource'}]}). + +-xml(legacy_auth_username, + #elem{name = <<"username">>, + xmlns = <<"jabber:iq:auth">>, + result = '$cdata'}). +-xml(legacy_auth_password, + #elem{name = <<"password">>, + xmlns = <<"jabber:iq:auth">>, + result = '$cdata'}). +-xml(legacy_auth_digest, + #elem{name = <<"digest">>, + xmlns = <<"jabber:iq:auth">>, + result = '$cdata'}). +-xml(legacy_auth_resource, + #elem{name = <<"resource">>, + xmlns = <<"jabber:iq:auth">>, + result = '$cdata'}). + +-xml(legacy_auth, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:auth">>, + result = {legacy_auth, '$username', '$password', + '$digest', '$resource'}, + refs = [#ref{name = legacy_auth_username, min = 0, max = 1, + label = '$username'}, + #ref{name = legacy_auth_password, min = 0, max = 1, + label = '$password'}, + #ref{name = legacy_auth_digest, min = 0, max = 1, + label = '$digest'}, + #ref{name = legacy_auth_resource, min = 0, max = 1, + label = '$resource'}]}). + +-xml(sasl_auth, + #elem{name = <<"auth">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + cdata = #cdata{label = '$text', + dec = {base64, decode, []}, + enc = {base64, encode, []}}, + result = {sasl_auth, '$mechanism', '$text'}, + attrs = [#attr{name = <<"mechanism">>, + required = true}]}). + +-xml(sasl_abort, + #elem{name = <<"abort">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + result = {sasl_abort}}). + +-xml(sasl_challenge, + #elem{name = <<"challenge">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + cdata = #cdata{label = '$text', + dec = {base64, decode, []}, + enc = {base64, encode, []}}, + result = {sasl_challenge, '$text'}}). + +-xml(sasl_response, + #elem{name = <<"response">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + cdata = #cdata{label = '$text', + dec = {base64, decode, []}, + enc = {base64, encode, []}}, + result = {sasl_response, '$text'}}). + +-xml(sasl_success, + #elem{name = <<"success">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + cdata = #cdata{label = '$text', + dec = {base64, decode, []}, + enc = {base64, encode, []}}, + result = {sasl_success, '$text'}}). + +-xml(sasl_failure_text, + #elem{name = <<"text">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + attrs = [#attr{name = <<"xml:lang">>, + label = '$lang'}]}). + +-xml(sasl_failure_aborted, + #elem{name = <<"aborted">>, + result = 'aborted', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_account_disabled, + #elem{name = <<"account-disabled">>, + result = 'account-disabled', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_credentials_expired, + #elem{name = <<"credentials-expired">>, + result = 'credentials-expired', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_encryption_required, + #elem{name = <<"encryption-required">>, + result = 'encryption-required', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_incorrect_encoding, + #elem{name = <<"incorrect-encoding">>, + result = 'incorrect-encoding', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_invalid_authzid, + #elem{name = <<"invalid-authzid">>, + result = 'invalid-authzid', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_invalid_mechanism, + #elem{name = <<"invalid-mechanism">>, + result = 'invalid-mechanism', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_malformed_request, + #elem{name = <<"malformed-request">>, + result = 'malformed-request', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_mechanism_too_weak, + #elem{name = <<"mechanism-too-weak">>, + result = 'mechanism-too-weak', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_not_authorized, + #elem{name = <<"not-authorized">>, + result = 'not-authorized', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_bad_protocol, + #elem{name = <<"bad-protocol">>, + result = 'bad-protocol', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). +-xml(sasl_failure_temporary_auth_failure, + #elem{name = <<"temporary-auth-failure">>, + result = 'temporary-auth-failure', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). + +-xml(sasl_failure, + #elem{name = <<"failure">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + result = {sasl_failure, '$reason', '$text'}, + refs = [#ref{name = sasl_failure_text, + label = '$text'}, + #ref{name = sasl_failure_aborted, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_account_disabled, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_credentials_expired, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_encryption_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_incorrect_encoding, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_invalid_authzid, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_invalid_mechanism, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_malformed_request, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_mechanism_too_weak, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_not_authorized, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_bad_protocol, + min = 0, max = 1, label = '$reason'}, + #ref{name = sasl_failure_temporary_auth_failure, + min = 0, max = 1, label = '$reason'}]}). + +-xml(sasl_mechanism, + #elem{name = <<"mechanism">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + result = '$cdata'}). + +-xml(sasl_mechanisms, + #elem{name = <<"mechanisms">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, + result = {sasl_mechanisms, '$list'}, + refs = [#ref{name = sasl_mechanism, + label = '$list'}]}). + +-xml(starttls_required, + #elem{name = <<"required">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, + result = true}). + +-xml(starttls, + #elem{name = <<"starttls">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, + result = {starttls, '$required'}, + refs = [#ref{name = starttls_required, + label = '$required', + min = 0, max = 1, + default = false}]}). + +-xml(starttls_proceed, + #elem{name = <<"proceed">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, + result = {starttls_proceed}}). + +-xml(starttls_failure, + #elem{name = <<"failure">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, + result = {starttls_failure}}). + +-xml(compress_failure_setup_failed, + #elem{name = <<"setup-failed">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = 'setup-failed'}). +-xml(compress_failure_processing_failed, + #elem{name = <<"processing-failed">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = 'processing-failed'}). +-xml(compress_failure_unsupported_method, + #elem{name = <<"unsupported-method">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = 'unsupported-method'}). + +-xml(compress_failure, + #elem{name = <<"failure">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = {compress_failure, '$reason'}, + refs = [#ref{name = compress_failure_setup_failed, + min = 0, max = 1, label = '$reason'}, + #ref{name = compress_failure_processing_failed, + min = 0, max = 1, label = '$reason'}, + #ref{name = compress_failure_unsupported_method, + min = 0, max = 1, label = '$reason'}]}). + +-xml(compress_method, + #elem{name = <<"method">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = '$cdata'}). + +-xml(compress, + #elem{name = <<"compress">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = {compress, '$methods'}, + refs = [#ref{name = compress_method, + label = '$methods'}]}). + +-xml(compressed, + #elem{name = <<"compressed">>, + xmlns = <<"http://jabber.org/protocol/compress">>, + result = {compressed}}). + +-xml(compression_method, + #elem{name = <<"method">>, + xmlns = <<"http://jabber.org/features/compress">>, + result = '$cdata'}). + +-xml(compression, + #elem{name = <<"compression">>, + xmlns = <<"http://jabber.org/features/compress">>, + result = {compression, '$methods'}, + refs = [#ref{name = compression_method, label = '$methods'}]}). + +-xml(stream_features, + #elem{name = <<"stream:features">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>], + result = {stream_features, '$_els'}}). + +-xml(p1_push, + #elem{name = <<"push">>, + result = {p1_push}, + xmlns = <<"p1:push">>}). + +-xml(p1_rebind, + #elem{name = <<"rebind">>, + result = {p1_rebind}, + xmlns = <<"p1:rebind">>}). + +-xml(p1_ack, + #elem{name = <<"ack">>, + result = {p1_ack}, + xmlns = <<"p1:ack">>}). + +-xml(caps, + #elem{name = <<"c">>, + xmlns = <<"http://jabber.org/protocol/caps">>, + result = {caps, '$node', '$version', '$hash', '$exts'}, + attrs = [#attr{name = <<"hash">>}, + #attr{name = <<"node">>}, + #attr{name = <<"ext">>, + label = '$exts', + default = [], + dec = {re, split, ["\\h+"]}, + enc = {join, [$ ]}}, + #attr{name = <<"ver">>, + label = '$version'}]}). + +-xml(feature_register, + #elem{name = <<"register">>, + xmlns = <<"http://jabber.org/features/iq-register">>, + result = {feature_register}}). + +-xml(register_registered, + #elem{name = <<"registered">>, + xmlns = <<"jabber:iq:register">>, + result = true}). +-xml(register_remove, + #elem{name = <<"remove">>, + xmlns = <<"jabber:iq:register">>, + result = true}). +-xml(register_instructions, + #elem{name = <<"instructions">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_username, + #elem{name = <<"username">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_nick, + #elem{name = <<"nick">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_password, + #elem{name = <<"password">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_name, + #elem{name = <<"name">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_first, + #elem{name = <<"first">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_last, + #elem{name = <<"last">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_email, + #elem{name = <<"email">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_address, + #elem{name = <<"address">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_city, + #elem{name = <<"city">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_state, + #elem{name = <<"state">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_zip, + #elem{name = <<"zip">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_phone, + #elem{name = <<"phone">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_url, + #elem{name = <<"url">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_date, + #elem{name = <<"date">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_misc, + #elem{name = <<"misc">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_text, + #elem{name = <<"text">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). +-xml(register_key, + #elem{name = <<"key">>, + xmlns = <<"jabber:iq:register">>, + result = '$cdata'}). + +-xml(register, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:register">>, + result = {register, '$registered', '$remove', '$instructions', + '$username', '$nick', '$password', '$name', + '$first', '$last', '$email', '$address', + '$city', '$state', '$zip', '$phone', '$url', + '$date', '$misc', '$text', '$key', '$xdata', '$_els'}, + refs = [#ref{name = xdata, min = 0, max = 1, + label = '$xdata'}, + #ref{name = register_registered, min = 0, max = 1, + default = false, label = '$registered'}, + #ref{name = register_remove, min = 0, max = 1, + default = false, label = '$remove'}, + #ref{name = register_instructions, min = 0, max = 1, + label = '$instructions'}, + #ref{name = register_username, min = 0, max = 1, + label = '$username'}, + #ref{name = register_nick, min = 0, max = 1, + label = '$nick'}, + #ref{name = register_password, min = 0, max = 1, + label = '$password'}, + #ref{name = register_name, min = 0, max = 1, + label = '$name'}, + #ref{name = register_first, min = 0, max = 1, + label = '$first'}, + #ref{name = register_last, min = 0, max = 1, + label = '$last'}, + #ref{name = register_email, min = 0, max = 1, + label = '$email'}, + #ref{name = register_address, min = 0, max = 1, + label = '$address'}, + #ref{name = register_city, min = 0, max = 1, + label = '$city'}, + #ref{name = register_state, min = 0, max = 1, + label = '$state'}, + #ref{name = register_zip, min = 0, max = 1, + label = '$zip'}, + #ref{name = register_phone, min = 0, max = 1, + label = '$phone'}, + #ref{name = register_url, min = 0, max = 1, + label = '$url'}, + #ref{name = register_date, min = 0, max = 1, + label = '$date'}, + #ref{name = register_misc, min = 0, max = 1, + label = '$misc'}, + #ref{name = register_text, min = 0, max = 1, + label = '$text'}, + #ref{name = register_key, min = 0, max = 1, + label = '$key'}]}). + +-xml(session_optional, + #elem{name = <<"optional">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-session">>, + result = true}). + +-xml(session, + #elem{name = <<"session">>, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-session">>, + result = {xmpp_session, '$optional'}, + refs = [#ref{name = session_optional, + min = 0, max = 1, default = false, + label = '$optional'}]}). + +-xml(ping, + #elem{name = <<"ping">>, + xmlns = <<"urn:xmpp:ping">>, + result = {ping}}). + +-xml(time_utc, + #elem{name = <<"utc">>, + xmlns = <<"urn:xmpp:time">>, + result = '$cdata', + cdata = #cdata{dec = {dec_utc, []}, + enc = {enc_utc, []}}}). + +-xml(time_tzo, + #elem{name = <<"tzo">>, + xmlns = <<"urn:xmpp:time">>, + result = '$cdata', + cdata = #cdata{dec = {dec_tzo, []}, + enc = {enc_tzo, []}}}). + +-xml(time, + #elem{name = <<"time">>, + xmlns = <<"urn:xmpp:time">>, + result = {time, '$tzo', '$utc'}, + refs = [#ref{name = time_tzo, + label = '$tzo', + min = 0, max = 1}, + #ref{name = time_utc, + label = '$utc', + min = 0, max = 1}]}). + +-xml(stream_error_text, + #elem{name = <<"text">>, + result = {text, '$lang', '$data'}, + cdata = #cdata{label = '$data'}, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>, + attrs = [#attr{name = <<"xml:lang">>, + label = '$lang'}]}). + +-xml(stream_error_bad_format, + #elem{name = <<"bad-format">>, + result = 'bad-format', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_bad_namespace_prefix, + #elem{name = <<"bad-namespace-prefix">>, + result = 'bad-namespace-prefix', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_conflict, + #elem{name = <<"conflict">>, + result = 'conflict', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_connection_timeout, + #elem{name = <<"connection-timeout">>, + result = 'connection-timeout', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_host_gone, + #elem{name = <<"host-gone">>, + result = 'host-gone', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_host_unknown, + #elem{name = <<"host-unknown">>, + result = 'host-unknown', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_improper_addressing, + #elem{name = <<"improper-addressing">>, + result = 'improper-addressing', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_internal_server_error, + #elem{name = <<"internal-server-error">>, + result = 'internal-server-error', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_invalid_from, + #elem{name = <<"invalid-from">>, + result = 'invalid-from', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_invalid_id, + #elem{name = <<"invalid-id">>, + result = 'invalid-id', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_invalid_namespace, + #elem{name = <<"invalid-namespace">>, + result = 'invalid-namespace', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_invalid_xml, + #elem{name = <<"invalid-xml">>, + result = 'invalid-xml', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_not_authorized, + #elem{name = <<"not-authorized">>, + result = 'not-authorized', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_not_well_formed, + #elem{name = <<"not-well-formed">>, + result = 'not-well-formed', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_policy_violation, + #elem{name = <<"policy-violation">>, + result = 'policy-violation', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_remote_connection_failed, + #elem{name = <<"remote-connection-failed">>, + result = 'remote-connection-failed', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_reset, + #elem{name = <<"reset">>, + result = 'reset', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_resource_constraint, + #elem{name = <<"resource-constraint">>, + result = 'resource-constraint', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_restricted_xml, + #elem{name = <<"restricted-xml">>, + result = 'restricted-xml', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_see_other_host, + #elem{name = <<"see-other-host">>, + cdata = #cdata{required = true, label = '$host', + dec = {dec_host_port, []}, + enc = {enc_host_port, []}}, + result = {'see-other-host', '$host'}, + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_system_shutdown, + #elem{name = <<"system-shutdown">>, + result = 'system-shutdown', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_undefined_condition, + #elem{name = <<"undefined-condition">>, + result = 'undefined-condition', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_unsupported_encoding, + #elem{name = <<"unsupported-encoding">>, + result = 'unsupported-encoding', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_unsupported_stanza_type, + #elem{name = <<"unsupported-stanza-type">>, + result = 'unsupported-stanza-type', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). +-xml(stream_error_unsupported_version, + #elem{name = <<"unsupported-version">>, + result = 'unsupported-version', + xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). + +-xml(stream_error, + #elem{name = <<"stream:error">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {stream_error, '$reason', '$text'}, + refs = [#ref{name = stream_error_text, + label = '$text', + min = 0, max = 1}, + #ref{name = stream_error_bad_format, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_bad_namespace_prefix, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_conflict, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_connection_timeout, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_host_gone, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_host_unknown, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_improper_addressing, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_internal_server_error, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_invalid_from, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_invalid_id, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_invalid_namespace, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_invalid_xml, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_not_authorized, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_not_well_formed, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_policy_violation, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_remote_connection_failed, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_reset, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_resource_constraint, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_restricted_xml, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_see_other_host, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_system_shutdown, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_undefined_condition, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_unsupported_encoding, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_unsupported_stanza_type, + min = 0, max = 1, label = '$reason'}, + #ref{name = stream_error_unsupported_version, + min = 0, max = 1, label = '$reason'} + ]}). + +-xml(vcard_HOME, #elem{name = <<"HOME">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_WORK, #elem{name = <<"WORK">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_VOICE, #elem{name = <<"VOICE">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_FAX, #elem{name = <<"FAX">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_PAGER, #elem{name = <<"PAGER">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_MSG, #elem{name = <<"MSG">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_CELL, #elem{name = <<"CELL">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_VIDEO, #elem{name = <<"VIDEO">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_BBS, #elem{name = <<"BBS">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_MODEM, #elem{name = <<"MODEM">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_ISDN, #elem{name = <<"ISDN">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_PCS, #elem{name = <<"PCS">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_POSTAL, #elem{name = <<"POSTAL">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_PARCEL, #elem{name = <<"PARCEL">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_DOM, #elem{name = <<"DOM">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_INTL, #elem{name = <<"INTL">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_PREF, #elem{name = <<"PREF">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_INTERNET, #elem{name = <<"INTERNET">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_X400, #elem{name = <<"X400">>, xmlns = <<"vcard-temp">>, result = true}). +-xml(vcard_FAMILY, #elem{name = <<"FAMILY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_GIVEN, #elem{name = <<"GIVEN">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_MIDDLE, #elem{name = <<"MIDDLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_PREFIX, #elem{name = <<"PREFIX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_SUFFIX, #elem{name = <<"SUFFIX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_POBOX, #elem{name = <<"POBOX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_EXTADD, #elem{name = <<"EXTADD">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_STREET, #elem{name = <<"STREET">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_LOCALITY, #elem{name = <<"LOCALITY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_REGION, #elem{name = <<"REGION">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_PCODE, #elem{name = <<"PCODE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_CTRY, #elem{name = <<"CTRY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_LINE, #elem{name = <<"LINE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_NUMBER, #elem{name = <<"NUMBER">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_USERID, #elem{name = <<"USERID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_LAT, #elem{name = <<"LAT">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_LON, #elem{name = <<"LON">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_ORGNAME, #elem{name = <<"ORGNAME">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_ORGUNIT, #elem{name = <<"ORGUNIT">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_PHONETIC, #elem{name = <<"PHONETIC">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_CRED, #elem{name = <<"CRED">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_VERSION, #elem{name = <<"VERSION">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_FN, #elem{name = <<"FN">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_NICKNAME, #elem{name = <<"NICKNAME">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_BDAY, #elem{name = <<"BDAY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_JABBERID, #elem{name = <<"JABBERID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_MAILER, #elem{name = <<"MAILER">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_TZ, #elem{name = <<"TZ">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_TITLE, #elem{name = <<"TITLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_ROLE, #elem{name = <<"ROLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_KEYWORD, #elem{name = <<"KEYWORD">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_NOTE, #elem{name = <<"NOTE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_PRODID, #elem{name = <<"PRODID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_REV, #elem{name = <<"REV">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_SORT_STRING, #elem{name = <<"SORT-STRING">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_UID, #elem{name = <<"UID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_URL, #elem{name = <<"URL">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_DESC, #elem{name = <<"DESC">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_TYPE, #elem{name = <<"TYPE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_EXTVAL, #elem{name = <<"EXTVAL">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). +-xml(vcard_PUBLIC, #elem{name = <<"PUBLIC">>, xmlns = <<"vcard-temp">>, result = public}). +-xml(vcard_PRIVATE, #elem{name = <<"PRIVATE">>, xmlns = <<"vcard-temp">>, result = private}). +-xml(vcard_CONFIDENTIAL, #elem{name = <<"CONFIDENTIAL">>, xmlns = <<"vcard-temp">>, result = confidential}). + +-xml(vcard_N, + #elem{name = <<"N">>, + xmlns = <<"vcard-temp">>, + result = {vcard_name, '$family', '$given', '$middle', + '$prefix', '$suffix'}, + refs = [#ref{name = vcard_FAMILY, min = 0, max = 1, label = '$family'}, + #ref{name = vcard_GIVEN, min = 0, max = 1, label = '$given'}, + #ref{name = vcard_MIDDLE, min = 0, max = 1, label = '$middle'}, + #ref{name = vcard_PREFIX, min = 0, max = 1, label = '$prefix'}, + #ref{name = vcard_SUFFIX, min = 0, max = 1, label = '$suffix'}]}). + +-xml(vcard_ADR, + #elem{name = <<"ADR">>, + xmlns = <<"vcard-temp">>, + result = {vcard_adr, '$home', '$work', '$postal', '$parcel', + '$dom', '$intl', '$pref', '$pobox', '$extadd', '$street', + '$locality', '$region', '$pcode', '$ctry'}, + refs = [#ref{name = vcard_HOME, default = false, + min = 0, max = 1, label = '$home'}, + #ref{name = vcard_WORK, default = false, + min = 0, max = 1, label = '$work'}, + #ref{name = vcard_POSTAL, default = false, + min = 0, max = 1, label = '$postal'}, + #ref{name = vcard_PARCEL, default = false, + min = 0, max = 1, label = '$parcel'}, + #ref{name = vcard_DOM, default = false, + min = 0, max = 1, label = '$dom'}, + #ref{name = vcard_INTL, default = false, + min = 0, max = 1, label = '$intl'}, + #ref{name = vcard_PREF, default = false, + min = 0, max = 1, label = '$pref'}, + #ref{name = vcard_POBOX, min = 0, max = 1, label = '$pobox'}, + #ref{name = vcard_EXTADD, min = 0, max = 1, label = '$extadd'}, + #ref{name = vcard_STREET, min = 0, max = 1, label = '$street'}, + #ref{name = vcard_LOCALITY, min = 0, max = 1, label = '$locality'}, + #ref{name = vcard_REGION, min = 0, max = 1, label = '$region'}, + #ref{name = vcard_PCODE, min = 0, max = 1, label = '$pcode'}, + #ref{name = vcard_CTRY, min = 0, max = 1, label = '$ctry'}]}). + +-xml(vcard_LABEL, + #elem{name = <<"LABEL">>, + xmlns = <<"vcard-temp">>, + result = {vcard_label, '$home', '$work', '$postal', '$parcel', + '$dom', '$intl', '$pref', '$line'}, + refs = [#ref{name = vcard_HOME, default = false, + min = 0, max = 1, label = '$home'}, + #ref{name = vcard_WORK, default = false, + min = 0, max = 1, label = '$work'}, + #ref{name = vcard_POSTAL, default = false, + min = 0, max = 1, label = '$postal'}, + #ref{name = vcard_PARCEL, default = false, + min = 0, max = 1, label = '$parcel'}, + #ref{name = vcard_DOM, default = false, + min = 0, max = 1, label = '$dom'}, + #ref{name = vcard_INTL, default = false, + min = 0, max = 1, label = '$intl'}, + #ref{name = vcard_PREF, default = false, + min = 0, max = 1, label = '$pref'}, + #ref{name = vcard_LINE, label = '$line'}]}). + +-xml(vcard_TEL, + #elem{name = <<"TEL">>, + xmlns = <<"vcard-temp">>, + result = {vcard_tel, '$home', '$work', '$voice', '$fax', + '$pager', '$msg', '$cell', '$video', '$bbs', + '$modem', '$isdn', '$pcs', '$pref', '$number'}, + refs = [#ref{name = vcard_HOME, default = false, + min = 0, max = 1, label = '$home'}, + #ref{name = vcard_WORK, default = false, + min = 0, max = 1, label = '$work'}, + #ref{name = vcard_VOICE, default = false, + min = 0, max = 1, label = '$voice'}, + #ref{name = vcard_FAX, default = false, + min = 0, max = 1, label = '$fax'}, + #ref{name = vcard_PAGER, default = false, + min = 0, max = 1, label = '$pager'}, + #ref{name = vcard_MSG, default = false, + min = 0, max = 1, label = '$msg'}, + #ref{name = vcard_CELL, default = false, + min = 0, max = 1, label = '$cell'}, + #ref{name = vcard_VIDEO, default = false, + min = 0, max = 1, label = '$video'}, + #ref{name = vcard_BBS, default = false, + min = 0, max = 1, label = '$bbs'}, + #ref{name = vcard_MODEM, default = false, + min = 0, max = 1, label = '$modem'}, + #ref{name = vcard_ISDN, default = false, + min = 0, max = 1, label = '$isdn'}, + #ref{name = vcard_PCS, default = false, + min = 0, max = 1, label = '$pcs'}, + #ref{name = vcard_PREF, default = false, + min = 0, max = 1, label = '$pref'}, + #ref{name = vcard_NUMBER, + min = 0, max = 1, label = '$number'}]}). + +-xml(vcard_EMAIL, + #elem{name = <<"EMAIL">>, + xmlns = <<"vcard-temp">>, + result = {vcard_email, '$home', '$work', + '$internet', '$pref', '$x400', '$userid'}, + refs = [#ref{name = vcard_HOME, default = false, + min = 0, max = 1, label = '$home'}, + #ref{name = vcard_WORK, default = false, + min = 0, max = 1, label = '$work'}, + #ref{name = vcard_INTERNET, default = false, + min = 0, max = 1, label = '$internet'}, + #ref{name = vcard_PREF, default = false, + min = 0, max = 1, label = '$pref'}, + #ref{name = vcard_X400, default = false, + min = 0, max = 1, label = '$x400'}, + #ref{name = vcard_USERID, + min = 0, max = 1, label = '$userid'}]}). + +-xml(vcard_GEO, + #elem{name = <<"GEO">>, + xmlns = <<"vcard-temp">>, + result = {vcard_geo, '$lat', '$lon'}, + refs = [#ref{name = vcard_LAT, min = 0, max = 1, label = '$lat'}, + #ref{name = vcard_LON, min = 0, max = 1, label = '$lon'}]}). + +-xml(vcard_BINVAL, + #elem{name = <<"BINVAL">>, + xmlns = <<"vcard-temp">>, + cdata = #cdata{dec = {base64, decode, []}, + enc = {base64, encode, []}}, + result = '$cdata'}). + +-xml(vcard_LOGO, + #elem{name = <<"LOGO">>, + xmlns = <<"vcard-temp">>, + result = {vcard_logo, '$type', '$binval', '$extval'}, + refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, + #ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, + #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). + +-xml(vcard_PHOTO, + #elem{name = <<"PHOTO">>, + xmlns = <<"vcard-temp">>, + result = {vcard_photo, '$type', '$binval', '$extval'}, + refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, + #ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, + #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). + +-xml(vcard_ORG, + #elem{name = <<"ORG">>, + xmlns = <<"vcard-temp">>, + result = {vcard_org, '$name', '$units'}, + refs = [#ref{name = vcard_ORGNAME, + label = '$name', + min = 0, max = 1}, + #ref{name = vcard_ORGUNIT, + label = '$units'}]}). + +-xml(vcard_SOUND, + #elem{name = <<"SOUND">>, + xmlns = <<"vcard-temp">>, + result = {vcard_sound, '$phonetic', '$binval', '$extval'}, + refs = [#ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, + #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}, + #ref{name = vcard_PHONETIC, min = 0, max = 1, label = '$phonetic'}]}). + +-xml(vcard_KEY, + #elem{name = <<"KEY">>, + xmlns = <<"vcard-temp">>, + result = {vcard_key, '$type', '$cred'}, + refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, + #ref{name = vcard_CRED, min = 0, max = 1, label = '$cred'}]}). + +-xml(vcard_CATEGORIES, + #elem{name = <<"CATEGORIES">>, + xmlns = <<"vcard-temp">>, + result = '$keywords', + refs = [#ref{name = vcard_KEYWORD, label = '$keywords'}]}). + +-xml(vcard_CLASS, + #elem{name = <<"CLASS">>, + xmlns = <<"vcard-temp">>, + result = '$class', + refs = [#ref{name = vcard_PUBLIC, min = 0, max = 1, label = '$class'}, + #ref{name = vcard_PRIVATE, min = 0, max = 1, label = '$class'}, + #ref{name = vcard_CONFIDENTIAL, min = 0, max = 1, label = '$class'}]}). + +%% {vcard_AGENT, +%% #elem{name = <<"AGENT">>, +%% xmlns = <<"vcard-temp">>, +%% result = {vcard_agent, '$vcard', '$extval'}, +%% refs = [#ref{name = vcard, min = 0, max = 1, label = '$vcard'}, +%% #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). + +-xml(vcard_temp, + #elem{name = <<"vCard">>, + xmlns = <<"vcard-temp">>, + result = {vcard_temp, '$version', '$fn', '$n', '$nickname', '$photo', + '$bday', '$adr', '$label', '$tel', '$email', '$jabberid', + '$mailer', '$tz', '$geo', '$title', '$role', '$logo', + '$org', '$categories', '$note', '$prodid', %% '$agent', + '$rev', '$sort_string', '$sound', '$uid', '$url', '$class', + '$key', '$desc'}, + refs = [#ref{name = vcard_N, min = 0, max = 1, label = '$n'}, + #ref{name = vcard_ADR, label = '$adr'}, + #ref{name = vcard_LABEL, label = '$label'}, + #ref{name = vcard_TEL, label = '$tel'}, + #ref{name = vcard_EMAIL, label = '$email'}, + #ref{name = vcard_GEO, min = 0, max = 1, label = '$geo'}, + #ref{name = vcard_LOGO, min = 0, max = 1, label = '$logo'}, + #ref{name = vcard_PHOTO, min = 0, max = 1, label = '$photo'}, + #ref{name = vcard_ORG, min = 0, max = 1, label = '$org'}, + #ref{name = vcard_SOUND, min = 0, max = 1, label = '$sound'}, + #ref{name = vcard_KEY, min = 0, max = 1, label = '$key'}, + #ref{name = vcard_VERSION, min = 0, max = 1, label = '$version'}, + #ref{name = vcard_FN, min = 0, max = 1, label = '$fn'}, + #ref{name = vcard_NICKNAME, min = 0, max = 1, label = '$nickname'}, + #ref{name = vcard_BDAY, min = 0, max = 1, label = '$bday'}, + #ref{name = vcard_JABBERID, min = 0, max = 1, label = '$jabberid'}, + #ref{name = vcard_MAILER, min = 0, max = 1, label = '$mailer'}, + #ref{name = vcard_TZ, min = 0, max = 1, label = '$tz'}, + #ref{name = vcard_TITLE, min = 0, max = 1, label = '$title'}, + #ref{name = vcard_ROLE, min = 0, max = 1, label = '$role'}, + #ref{name = vcard_NOTE, min = 0, max = 1, label = '$note'}, + #ref{name = vcard_PRODID, min = 0, max = 1, label = '$prodid'}, + #ref{name = vcard_REV, min = 0, max = 1, label = '$rev'}, + %%#ref{name = vcard_AGENT, min = 0, max = 1, label = '$agent'}, + #ref{name = vcard_SORT_STRING, min = 0, max = 1, + label = '$sort_string'}, + #ref{name = vcard_UID, min = 0, max = 1, label = '$uid'}, + #ref{name = vcard_URL, min = 0, max = 1, label = '$url'}, + #ref{name = vcard_DESC, min = 0, max = 1, label = '$desc'}, + #ref{name = vcard_CATEGORIES, default = [], min = 0, max = 1, + label = '$categories'}, + #ref{name = vcard_CLASS, min = 0, max = 1, label = '$class'}]}). + +-xml(vcard_xupdate_photo, + #elem{name = <<"photo">>, + xmlns = <<"vcard-temp:x:update">>, + result = '$cdata'}). + +-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()}, + hash :: binary()}). +-type vcard_xupdate() :: #vcard_xupdate{}. + +-xml(vcard_xupdate, + #elem{name = <<"x">>, + xmlns = <<"vcard-temp:x:update">>, + result = {vcard_xupdate, '$_', '$hash'}, + refs = [#ref{name = vcard_xupdate_photo, min = 0, max = 1, + label = '$hash'}]}). + +-xml(xdata_field_required, + #elem{name = <<"required">>, + xmlns = <<"jabber:x:data">>, + result = true}). + +-xml(xdata_field_desc, + #elem{name = <<"desc">>, xmlns = <<"jabber:x:data">>, result = '$cdata'}). + +-xml(xdata_field_value, + #elem{name = <<"value">>, xmlns = <<"jabber:x:data">>, result = '$cdata'}). + +-xml(xdata_field_option, + #elem{name = <<"option">>, + xmlns = <<"jabber:x:data">>, + result = {xdata_option, '$label', '$value'}, + attrs = [#attr{name = <<"label">>}], + refs = [#ref{name = xdata_field_value, + label = '$value', + min = 1, max = 1}]}). + +-xml(xdata_field, + #elem{name = <<"field">>, + xmlns = <<"jabber:x:data">>, + result = {xdata_field, '$label', '$type', '$var', + '$required', '$desc', '$values', '$options', '$_els'}, + attrs = [#attr{name = <<"label">>}, + #attr{name = <<"type">>, + enc = {enc_enum, []}, + dec = {dec_enum, [['boolean', + 'fixed', + 'hidden', + 'jid-multi', + 'jid-single', + 'list-multi', + 'list-single', + 'text-multi', + 'text-private', + 'text-single']]}}, + #attr{name = <<"var">>}], + refs = [#ref{name = xdata_field_required, + label = '$required', + default = false, + min = 0, max = 1}, + #ref{name = xdata_field_desc, + default = <<"">>, + label = '$desc', + min = 0, max = 1}, + #ref{name = xdata_field_value, + label = '$values'}, + #ref{name = xdata_field_option, + label = '$options'}]}). + +-xml(xdata_instructions, #elem{name = <<"instructions">>, + xmlns = <<"jabber:x:data">>, + result = '$cdata'}). +-xml(xdata_title, #elem{name = <<"title">>, + xmlns = <<"jabber:x:data">>, + result = '$cdata'}). +-xml(xdata_reported, #elem{name = <<"reported">>, + xmlns = <<"jabber:x:data">>, + result = '$fields', + refs = [#ref{name = xdata_field, + label = '$fields'}]}). +-xml(xdata_item, #elem{name = <<"item">>, + xmlns = <<"jabber:x:data">>, + result = '$fields', + refs = [#ref{name = xdata_field, + label = '$fields'}]}). + +-xml(xdata, + #elem{name = <<"x">>, + xmlns = <<"jabber:x:data">>, + result = {xdata, '$type', '$instructions', '$title', + '$reported', '$items', '$fields'}, + attrs = [#attr{name = <<"type">>, + required = true, + dec = {dec_enum, [[cancel, form, result, submit]]}, + enc = {enc_enum, []}}], + refs = [#ref{name = xdata_instructions, + label = '$instructions'}, + #ref{name = xdata_title, + label = '$title', + min = 0, max = 1}, + #ref{name = xdata_reported, + label = '$reported', + min = 0, max = 1}, + #ref{name = xdata_item, + label = '$items'}, + #ref{name = xdata_field, + label = '$fields'}]}). + +-xml(pubsub_subscription, + #elem{name = <<"subscription">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = {ps_subscription, '$xmlns', '$jid', '$type', + '$node', '$subid', '$expiry'}, + attrs = [#attr{name = <<"xmlns">>}, + #attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"node">>}, + #attr{name = <<"subid">>}, + #attr{name = <<"subscription">>, + label = '$type', + dec = {dec_enum, [[none, pending, subscribed, + unconfigured]]}, + enc = {enc_enum, []}}, + #attr{name = <<"expiry">>, + dec = {dec_utc, []}, + enc = {enc_utc, []}}]}). + +-record(ps_affiliation, {xmlns = <<>> :: binary(), + node = <<>> :: binary(), + type :: member | none | outcast | + owner | publisher | publish_only, + jid :: jid:jid()}). +-type ps_affiliation() :: #ps_affiliation{}. + +-xml(pubsub_affiliation, + #elem{name = <<"affiliation">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_affiliation, '$xmlns', '$node', '$type', '$_'}, + attrs = [#attr{name = <<"node">>, required = true}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"affiliation">>, + label = '$type', + required = true, + dec = {dec_ps_aff, []}, + enc = {enc_ps_aff, []}}]}). + +-xml(pubsub_owner_affiliation, + #elem{name = <<"affiliation">>, + xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, + result = {ps_affiliation, '$xmlns', '$_', '$type', '$jid'}, + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"affiliation">>, + label = '$type', + required = true, + dec = {dec_ps_aff, []}, + enc = {enc_ps_aff, []}}]}). + +-xml(pubsub_event_configuration, + #elem{name = <<"configuration">>, + xmlns = <<"http://jabber.org/protocol/pubsub#event">>, + result = {'$node', '$xdata'}, + attrs = [#attr{name = <<"node">>, required = true}], + refs = [#ref{name = xdata, min = 0, max = 1}]}). + +-xml(pubsub_event_retract, + #elem{name = <<"retract">>, + xmlns = <<"http://jabber.org/protocol/pubsub#event">>, + result = '$id', + attrs = [#attr{name = <<"id">>, required = true}]}). + +-xml(pubsub_item, + #elem{name = <<"item">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = {ps_item, '$xmlns', '$id', '$_xmls', '$node', '$publisher'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"node">>}, + #attr{name = <<"publisher">>}]}). + +-xml(pubsub_items, + #elem{name = <<"items">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = {ps_items, '$xmlns', '$node', '$items', '$max_items', + '$subid', '$retract'}, + attrs = [#attr{name = <<"xmlns">>}, + #attr{name = <<"max_items">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"node">>, + required = true}, + #attr{name = <<"subid">>}], + refs = [#ref{name = pubsub_event_retract, label = '$retract', + min = 0, max = 1}, + #ref{name = pubsub_item, label = '$items'}]}). + +-xml(pubsub_event, + #elem{name = <<"event">>, + xmlns = <<"http://jabber.org/protocol/pubsub#event">>, + result = {ps_event, '$items', '$purge', '$subscription', '$delete', + '$create', '$configuration'}, + refs = [#ref{name = pubsub_items, label = '$items', + min = 0, max = 1}, + #ref{name = pubsub_subscription, min = 0, max = 1, + label = '$subscription'}, + #ref{name = pubsub_purge, min = 0, max = 1, + label = '$purge'}, + #ref{name = pubsub_delete, min = 0, max = 1, + label = '$delete'}, + #ref{name = pubsub_create, min = 0, max = 1, + label = '$create'}, + #ref{name = pubsub_event_configuration, min = 0, max = 1, + label = '$configuration'}]}). + +-xml(pubsub_subscriptions, + #elem{name = <<"subscriptions">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>], + result = {'$node', '$subscriptions'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = pubsub_subscription, label = '$subscriptions'}]}). + +-xml(pubsub_affiliations, + #elem{name = <<"affiliations">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {'$node', '$affiliations'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = pubsub_affiliation, label = '$affiliations'}]}). + +-xml(pubsub_owner_affiliations, + #elem{name = <<"affiliations">>, + xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, + result = {'$node', '$affiliations'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = pubsub_owner_affiliation, label = '$affiliations'}]}). + +-xml(pubsub_subscribe, + #elem{name = <<"subscribe">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_subscribe, '$node', '$jid'}, + attrs = [#attr{name = <<"node">>}, + #attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(pubsub_unsubscribe, + #elem{name = <<"unsubscribe">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_unsubscribe, '$node', '$jid', '$subid'}, + attrs = [#attr{name = <<"node">>}, + #attr{name = <<"subid">>}, + #attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(pubsub_publish, + #elem{name = <<"publish">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_publish, '$node', '$items'}, + attrs = [#attr{name = <<"node">>, + required = true}], + refs = [#ref{name = pubsub_item, label = '$items'}]}). + +-xml(pubsub_options, + #elem{name = <<"options">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_options, '$node', '$jid', '$subid', '$xdata'}, + attrs = [#attr{name = <<"node">>}, + #attr{name = <<"subid">>}, + #attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}], + refs = [#ref{name = xdata, min = 0, max = 1, + label = '$xdata'}]}). + +-xml(pubsub_retract, + #elem{name = <<"retract">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {ps_retract, '$node', '$notify', '$items'}, + attrs = [#attr{name = <<"node">>, + required = true}, + #attr{name = <<"notify">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}], + refs = [#ref{name = pubsub_item, label = '$items'}]}). + +-xml(pubsub_create, + #elem{name = <<"create">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = '$node', + attrs = [#attr{name = <<"node">>}]}). + +-xml(pubsub_configure, + #elem{name = <<"configure">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>], + result = {'$node', '$xdata'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = xdata, min = 0, max = 1}]}). + +-xml(pubsub_publish_options, + #elem{name = <<"publish-options">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = '$xdata', + refs = [#ref{name = xdata, min = 0, max = 1}]}). + +-xml(pubsub_default, + #elem{name = <<"default">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>], + result = {'$node', '$xdata'}, + attrs = [#attr{name = <<"node">>}], + refs = [#ref{name = xdata, min = 0, max = 1}]}). + +-xml(pubsub_redirect, + #elem{name = <<"redirect">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = '$uri', + attrs = [#attr{name = <<"uri">>, required = true}]}). + +-xml(pubsub_delete, + #elem{name = <<"delete">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = {'$node', '$uri'}, + attrs = [#attr{name = <<"node">>, required = true}], + refs = [#ref{name = pubsub_redirect, min = 0, max = 1, + label = '$uri', default = <<>>}]}). + +-xml(pubsub_purge, + #elem{name = <<"purge">>, + xmlns = [<<"http://jabber.org/protocol/pubsub">>, + <<"http://jabber.org/protocol/pubsub#owner">>, + <<"http://jabber.org/protocol/pubsub#event">>], + result = '$node', + attrs = [#attr{name = <<"node">>, required = true}]}). + +-xml(pubsub, + #elem{name = <<"pubsub">>, + xmlns = <<"http://jabber.org/protocol/pubsub">>, + result = {pubsub, '$subscriptions', '$subscription', + '$affiliations', '$publish', '$publish_options', + '$subscribe', '$unsubscribe', '$options', '$items', + '$retract', '$create', '$configure', '$default', '$delete', + '$purge', '$rsm'}, + refs = [#ref{name = pubsub_subscriptions, label = '$subscriptions', + min = 0, max = 1}, + #ref{name = pubsub_affiliations, label = '$affiliations', + min = 0, max = 1}, + #ref{name = pubsub_subscribe, label = '$subscribe', + min = 0, max = 1}, + #ref{name = pubsub_unsubscribe, label = '$unsubscribe', + min = 0, max = 1}, + #ref{name = pubsub_options, label = '$options', + min = 0, max = 1}, + #ref{name = pubsub_items, label = '$items', + min = 0, max = 1}, + #ref{name = pubsub_retract, label = '$retract', + min = 0, max = 1}, + #ref{name = pubsub_create, label = '$create', + min = 0, max = 1}, + #ref{name = pubsub_configure, label = '$configure', + min = 0, max = 1}, + #ref{name = pubsub_publish_options, min = 0, max = 1, + label = '$publish_options'}, + #ref{name = pubsub_default, label = '$default', + min = 0, max = 1}, + #ref{name = pubsub_delete, label = '$delete', + min = 0, max = 1}, + #ref{name = pubsub_purge, label = '$purge', + min = 0, max = 1}, + #ref{name = pubsub_subscription, label = '$subscription', + min = 0, max = 1}, + #ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}, + #ref{name = pubsub_publish, label = '$publish', + min = 0, max = 1}]}). + +-xml(pubsub_owner, + #elem{name = <<"pubsub">>, + xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, + result = {pubsub_owner, '$affiliations', '$configure', '$default', + '$delete', '$purge', '$subscriptions'}, + refs = [#ref{name = pubsub_owner_affiliations, + label = '$affiliations', min = 0, max = 1}, + #ref{name = pubsub_configure, label = '$configure', + min = 0, max = 1}, + #ref{name = pubsub_default, label = '$default', + min = 0, max = 1}, + #ref{name = pubsub_delete, label = '$delete', + min = 0, max = 1}, + #ref{name = pubsub_purge, label = '$purge', + min = 0, max = 1}, + #ref{name = pubsub_subscriptions, + label = '$subscriptions', min = 0, max = 1}]}). + +-type ps_error_type() :: 'closed-node' | 'configuration-required' | + 'invalid-jid' | 'invalid-options' | + 'invalid-payload' | 'invalid-subid' | + 'item-forbidden' | 'item-required' | 'jid-required' | + 'max-items-exceeded' | 'max-nodes-exceeded' | + 'nodeid-required' | 'not-in-roster-group' | + 'not-subscribed' | 'payload-too-big' | + 'payload-required' | 'pending-subscription' | + 'presence-subscription-required' | 'subid-required' | + 'too-many-subscriptions' | 'unsupported' | + 'unsupported-access-model'. +-type ps_feature() :: 'access-authorize' | 'access-open' | + 'access-presence' | 'access-roster' | + 'access-whitelist' | 'auto-create' | + 'auto-subscribe' | 'collections' | 'config-node' | + 'create-and-configure' | 'create-nodes' | + 'delete-items' | 'delete-nodes' | + 'filtered-notifications' | 'get-pending' | + 'instant-nodes' | 'item-ids' | 'last-published' | + 'leased-subscription' | 'manage-subscriptions' | + 'member-affiliation' | 'meta-data' | + 'modify-affiliations' | 'multi-collection' | + 'multi-subscribe' | 'outcast-affiliation' | + 'persistent-items' | 'presence-notifications' | + 'presence-subscribe' | 'publish' | + 'publish-options' | 'publish-only-affiliation' | + 'publisher-affiliation' | 'purge-nodes' | + 'retract-items' | 'retrieve-affiliations' | + 'retrieve-default' | 'retrieve-items' | + 'retrieve-subscriptions' | 'subscribe' | + 'subscription-options' | 'subscription-notifications'. +-record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}). +-type ps_error() :: #ps_error{}. + +-xml(pubsub_error_closed_node, + #elem{name = <<"closed-node">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'closed-node', '$_'}}). +-xml(pubsub_error_configuration_required, + #elem{name = <<"configuration-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'configuration-required', '$_'}}). +-xml(pubsub_error_invalid_jid, + #elem{name = <<"invalid-jid">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'invalid-jid', '$_'}}). +-xml(pubsub_error_invalid_options, + #elem{name = <<"invalid-options">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'invalid-options', '$_'}}). +-xml(pubsub_error_invalid_payload, + #elem{name = <<"invalid-payload">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'invalid-payload', '$_'}}). +-xml(pubsub_error_invalid_subid, + #elem{name = <<"invalid-subid">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'invalid-subid', '$_'}}). +-xml(pubsub_error_item_forbidden, + #elem{name = <<"item-forbidden">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'item-forbidden', '$_'}}). +-xml(pubsub_error_item_required, + #elem{name = <<"item-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'item-required', '$_'}}). +-xml(pubsub_error_jid_required, + #elem{name = <<"jid-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'jid-required', '$_'}}). +-xml(pubsub_error_max_items_exceeded, + #elem{name = <<"max-items-exceeded">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'max-items-exceeded', '$_'}}). +-xml(pubsub_error_max_nodes_exceeded, + #elem{name = <<"max-nodes-exceeded">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'max-nodes-exceeded', '$_'}}). +-xml(pubsub_error_nodeid_required, + #elem{name = <<"nodeid-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'nodeid-required', '$_'}}). +-xml(pubsub_error_not_in_roster_group, + #elem{name = <<"not-in-roster-group">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'not-in-roster-group', '$_'}}). +-xml(pubsub_error_not_subscribed, + #elem{name = <<"not-subscribed">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'not-subscribed', '$_'}}). +-xml(pubsub_error_payload_too_big, + #elem{name = <<"payload-too-big">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'payload-too-big', '$_'}}). +-xml(pubsub_error_payload_required, + #elem{name = <<"payload-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'payload-required', '$_'}}). +-xml(pubsub_error_pending_subscription, + #elem{name = <<"pending-subscription">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'pending-subscription', '$_'}}). +-xml(pubsub_error_presence_subscription_required, + #elem{name = <<"presence-subscription-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'presence-subscription-required', '$_'}}). +-xml(pubsub_error_subid_required, + #elem{name = <<"subid-required">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'subid-required', '$_'}}). +-xml(pubsub_error_too_many_subscriptions, + #elem{name = <<"too-many-subscriptions">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'too-many-subscriptions', '$_'}}). +-xml(pubsub_error_unsupported, + #elem{name = <<"unsupported">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'unsupported', '$feature'}, + attrs = [#attr{name = <<"feature">>, required = true, + dec = {dec_enum, [['access-authorize', + 'access-open', + 'access-presence', + 'access-roster', + 'access-whitelist', + 'auto-create', + 'auto-subscribe', + 'collections', + 'config-node', + 'create-and-configure', + 'create-nodes', + 'delete-items', + 'delete-nodes', + 'filtered-notifications', + 'get-pending', + 'instant-nodes', + 'item-ids', + 'last-published', + 'leased-subscription', + 'manage-subscriptions', + 'member-affiliation', + 'meta-data', + 'modify-affiliations', + 'multi-collection', + 'multi-subscribe', + 'outcast-affiliation', + 'persistent-items', + 'presence-notifications', + 'presence-subscribe', + 'publish', + 'publish-options', + 'publish-only-affiliation', + 'publisher-affiliation', + 'purge-nodes', + 'retract-items', + 'retrieve-affiliations', + 'retrieve-default', + 'retrieve-items', + 'retrieve-subscriptions', + 'subscribe', + 'subscription-options', + 'subscription-notifications']]}, + enc = {enc_enum, []}}]}). +-xml(pubsub_error_unsupported_access_model, + #elem{name = <<"unsupported-access-model">>, + xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, + result = {ps_error, 'unsupported-access-model', '$_'}}). + +-xml(shim_header, + #elem{name = <<"header">>, + xmlns = <<"http://jabber.org/protocol/shim">>, + result = {'$name', '$cdata'}, + attrs = [#attr{name = <<"name">>, + required = true}]}). + +-xml(shim_headers, + #elem{name = <<"headers">>, + xmlns = <<"http://jabber.org/protocol/shim">>, + result = {shim, '$headers'}, + refs = [#ref{name = shim_header, label = '$headers'}]}). + +-record(chatstate, {type :: active | composing | gone | inactive | paused}). +-type chatstate() :: #chatstate{}. + +-xml(chatstate_active, + #elem{name = <<"active">>, + xmlns = <<"http://jabber.org/protocol/chatstates">>, + result = {chatstate, active}}). + +-xml(chatstate_composing, + #elem{name = <<"composing">>, + xmlns = <<"http://jabber.org/protocol/chatstates">>, + result = {chatstate, composing}}). + +-xml(chatstate_gone, + #elem{name = <<"gone">>, + xmlns = <<"http://jabber.org/protocol/chatstates">>, + result = {chatstate, gone}}). + +-xml(chatstate_inactive, + #elem{name = <<"inactive">>, + xmlns = <<"http://jabber.org/protocol/chatstates">>, + result = {chatstate, inactive}}). + +-xml(chatstate_paused, + #elem{name = <<"paused">>, + xmlns = <<"http://jabber.org/protocol/chatstates">>, + result = {chatstate, paused}}). + +-xml(delay, + #elem{name = <<"delay">>, + xmlns = <<"urn:xmpp:delay">>, + result = {delay, '$stamp', '$from', '$desc'}, + cdata = #cdata{label = '$desc', default = <<"">>}, + attrs = [#attr{name = <<"stamp">>, + required = true, + dec = {dec_utc, []}, + enc = {enc_utc, []}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(bytestreams_streamhost, + #elem{name = <<"streamhost">>, + xmlns = <<"http://jabber.org/protocol/bytestreams">>, + result = {streamhost, '$jid', '$host', '$port'}, + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"host">>, + required = true}, + #attr{name = <<"port">>, + default = 1080, + dec = {dec_int, [0, 65535]}, + enc = {enc_int, []}}]}). + +-xml(bytestreams_streamhost_used, + #elem{name = <<"streamhost-used">>, + xmlns = <<"http://jabber.org/protocol/bytestreams">>, + result = '$jid', + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(bytestreams_activate, + #elem{name = <<"activate">>, + xmlns = <<"http://jabber.org/protocol/bytestreams">>, + cdata = #cdata{enc = {enc_jid, []}, dec = {dec_jid, []}}, + result = '$cdata'}). + +-xml(bytestreams, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/bytestreams">>, + result = {bytestreams, '$hosts', '$used', '$activate', + '$dstaddr', '$mode', '$sid'}, + attrs = [#attr{name = <<"dstaddr">>}, + #attr{name = <<"sid">>}, + #attr{name = <<"mode">>, + default = tcp, + dec = {dec_enum, [[tcp, udp]]}, + enc = {enc_enum, []}}], + refs = [#ref{name = bytestreams_streamhost, label = '$hosts'}, + #ref{name = bytestreams_streamhost_used, + min = 0, max = 1, label = '$used'}, + #ref{name = bytestreams_activate, + min = 0, max = 1, label = '$activate'}]}). + +-xml(muc_history, + #elem{name = <<"history">>, + xmlns = <<"http://jabber.org/protocol/muc">>, + result = {muc_history, '$maxchars', '$maxstanzas', + '$seconds', '$since'}, + attrs = [#attr{name = <<"maxchars">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"maxstanzas">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"seconds">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"since">>, + dec = {dec_utc, []}, + enc = {enc_utc, []}}]}). + +-xml(muc_reason, + #elem{name = <<"reason">>, + xmlns = [<<"http://jabber.org/protocol/muc#user">>, + <<"http://jabber.org/protocol/muc#admin">>, + <<"http://jabber.org/protocol/muc#owner">>], + result = '$cdata'}). + +-xml(muc_user_decline, + #elem{name = <<"decline">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = {muc_decline, '$reason', '$from', '$to'}, + attrs = [#attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}], + refs = [#ref{name = muc_reason, min = 0, + default = <<"">>, + max = 1, label = '$reason'}]}). + +-xml(muc_destroy, + #elem{name = <<"destroy">>, + xmlns = [<<"http://jabber.org/protocol/muc#user">>, + <<"http://jabber.org/protocol/muc#owner">>], + result = {muc_destroy, '$xmlns', '$jid', '$reason', '$password'}, + attrs = [#attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xmlns">>}], + refs = [#ref{name = muc_reason, min = 0, + default = <<"">>, + max = 1, label = '$reason'}, + #ref{name = muc_password, min = 0, max = 1, + label = '$password'}]}). + +-xml(muc_user_invite, + #elem{name = <<"invite">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = {muc_invite, '$reason', '$from', '$to', '$continue'}, + attrs = [#attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}], + refs = [#ref{name = muc_reason, min = 0, default = <<"">>, + max = 1, label = '$reason'}, + #ref{name = muc_user_continue, min = 0, max = 1, + label = '$continue'}]}). + +-xml(muc_user_actor, + #elem{name = <<"actor">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = {muc_actor, '$jid', '$nick'}, + attrs = [#attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>}]}). + +-xml(muc_user_continue, + #elem{name = <<"continue">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = '$thread', + attrs = [#attr{name = <<"thread">>}]}). + +-xml(muc_user_status, + #elem{name = <<"status">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = '$code', + attrs = [#attr{name = <<"code">>, + dec = {dec_int, [100, 999]}, + enc = {enc_int, []}}]}). + +-xml(muc_user_item, + #elem{name = <<"item">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = {muc_item, '$actor', '$continue', '$reason', + '$affiliation', '$role', '$jid', '$nick'}, + refs = [#ref{name = muc_user_actor, + min = 0, max = 1, label = '$actor'}, + #ref{name = muc_user_continue, + min = 0, max = 1, label = '$continue'}, + #ref{name = muc_reason, default = <<"">>, + min = 0, max = 1, label = '$reason'}], + attrs = [#attr{name = <<"affiliation">>, + dec = {dec_enum, [[admin, member, none, + outcast, owner]]}, + enc = {enc_enum, []}}, + #attr{name = <<"role">>, + dec = {dec_enum, [[moderator, none, + participant, visitor]]}, + enc = {enc_enum, []}}, + #attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>}]}). + +-xml(muc_user, + #elem{name = <<"x">>, + xmlns = <<"http://jabber.org/protocol/muc#user">>, + result = {muc_user, '$decline', '$destroy', '$invites', + '$items', '$status_codes', '$password'}, + refs = [#ref{name = muc_user_decline, min = 0, + max = 1, label = '$decline'}, + #ref{name = muc_destroy, min = 0, max = 1, + label = '$destroy'}, + #ref{name = muc_password, min = 0, max = 1, + label = '$password'}, + #ref{name = muc_user_invite, label = '$invites'}, + #ref{name = muc_user_item, label = '$items'}, + #ref{name = muc_user_status, label = '$status_codes'}]}). + +-xml(muc_password, + #elem{name = <<"password">>, + xmlns = [<<"http://jabber.org/protocol/muc#owner">>, + <<"http://jabber.org/protocol/muc#user">>, + <<"http://jabber.org/protocol/muc">>], + result = '$cdata'}). + +-xml(muc_owner, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/muc#owner">>, + result = {muc_owner, '$destroy', '$config', '$items'}, + refs = [#ref{name = muc_destroy, min = 0, max = 1, + label = '$destroy'}, + #ref{name = xdata, min = 0, max = 1, + label = '$config'}, + #ref{name = muc_owner_item, label = '$items'}]}). + +-xml(muc_owner_item, + #elem{name = <<"item">>, + xmlns = <<"http://jabber.org/protocol/muc#owner">>, + result = {muc_item, '$actor', '$continue', '$reason', + '$affiliation', '$role', '$jid', '$nick'}, + refs = [#ref{name = muc_admin_actor, + min = 0, max = 1, label = '$actor'}, + #ref{name = muc_admin_continue, + min = 0, max = 1, label = '$continue'}, + #ref{name = muc_reason, default = <<"">>, + min = 0, max = 1, label = '$reason'}], + attrs = [#attr{name = <<"affiliation">>, + dec = {dec_enum, [[admin, member, none, + outcast, owner]]}, + enc = {enc_enum, []}}, + #attr{name = <<"role">>, + dec = {dec_enum, [[moderator, none, + participant, visitor]]}, + enc = {enc_enum, []}}, + #attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>}]}). + +-xml(muc_admin_item, + #elem{name = <<"item">>, + xmlns = <<"http://jabber.org/protocol/muc#admin">>, + result = {muc_item, '$actor', '$continue', '$reason', + '$affiliation', '$role', '$jid', '$nick'}, + refs = [#ref{name = muc_admin_actor, + min = 0, max = 1, label = '$actor'}, + #ref{name = muc_admin_continue, + min = 0, max = 1, label = '$continue'}, + #ref{name = muc_reason, default = <<"">>, + min = 0, max = 1, label = '$reason'}], + attrs = [#attr{name = <<"affiliation">>, + dec = {dec_enum, [[admin, member, none, + outcast, owner]]}, + enc = {enc_enum, []}}, + #attr{name = <<"role">>, + dec = {dec_enum, [[moderator, none, + participant, visitor]]}, + enc = {enc_enum, []}}, + #attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>}]}). + +-xml(muc_admin_actor, + #elem{name = <<"actor">>, + xmlns = <<"http://jabber.org/protocol/muc#admin">>, + result = {muc_actor, '$jid', '$nick'}, + attrs = [#attr{name = <<"jid">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>}]}). + +-xml(muc_admin_continue, + #elem{name = <<"continue">>, + xmlns = <<"http://jabber.org/protocol/muc#admin">>, + result = '$thread', + attrs = [#attr{name = <<"thread">>}]}). + +-xml(muc_admin, + #elem{name = <<"query">>, + xmlns = <<"http://jabber.org/protocol/muc#admin">>, + result = {muc_admin, '$items'}, + refs = [#ref{name = muc_admin_item, label = '$items'}]}). + +-xml(muc, + #elem{name = <<"x">>, + xmlns = <<"http://jabber.org/protocol/muc">>, + result = {muc, '$history', '$password'}, + refs = [#ref{name = muc_history, min = 0, max = 1, + label = '$history'}, + #ref{name = muc_password, min = 0, max = 1, + label = '$password'}]}). + +-xml(muc_unique, + #elem{name = <<"unique">>, + xmlns = <<"http://jabber.org/protocol/muc#unique">>, + result = {muc_unique, '$name'}, + cdata = #cdata{default = <<"">>, + label = '$name'}}). + +-xml(x_conference, + #elem{name = <<"x">>, + xmlns = <<"jabber:x:conference">>, + result = {x_conference, '$jid', '$password', '$reason', + '$continue', '$thread'}, + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"password">>, default = <<"">>}, + #attr{name = <<"reason">>, default = <<"">>}, + #attr{name = <<"thread">>, default = <<"">>}, + #attr{name = <<"continue">>, + dec = {dec_bool, []}, + enc = {enc_bool, []}}]}). + +-xml(muc_subscription, + #elem{name = <<"subscription">>, + xmlns = <<"urn:xmpp:mucsub:0">>, + result = '$jid', + attrs = [#attr{name = <<"jid">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(muc_subscriptions, + #elem{name = <<"subscriptions">>, + xmlns = <<"urn:xmpp:mucsub:0">>, + result = {muc_subscriptions, '$list'}, + refs = [#ref{name = muc_subscription, label = '$list'}]}). + +-xml(muc_subscribe_event, + #elem{name = <<"event">>, + xmlns = <<"urn:xmpp:mucsub:0">>, + result = '$node', + attrs = [#attr{name = <<"node">>, required = true}]}). + +-xml(muc_subscribe, + #elem{name = <<"subscribe">>, + xmlns = <<"urn:xmpp:mucsub:0">>, + result = {muc_subscribe, '$nick', '$events'}, + attrs = [#attr{name = <<"nick">>, required = true}], + refs = [#ref{name = muc_subscribe_event, label = '$events'}]}). + +-xml(muc_unsubscribe, + #elem{name = <<"unsubscribe">>, + xmlns = <<"urn:xmpp:mucsub:0">>, + result = {muc_unsubscribe}}). + +-xml(rsm_after, + #elem{name = <<"after">>, + xmlns = <<"http://jabber.org/protocol/rsm">>, + result = '$cdata'}). + +-xml(rsm_before, + #elem{name = <<"before">>, + xmlns = <<"http://jabber.org/protocol/rsm">>, + cdata = #cdata{default = <<"">>}, + result = '$cdata'}). + +-xml(rsm_last, + #elem{name = <<"last">>, + xmlns = <<"http://jabber.org/protocol/rsm">>, + result = '$cdata'}). + +-xml(rsm_count, + #elem{name = <<"count">>, result = '$cdata', + xmlns = <<"http://jabber.org/protocol/rsm">>, + cdata = #cdata{dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}}). + +-xml(rsm_index, + #elem{name = <<"index">>, result = '$cdata', + xmlns = <<"http://jabber.org/protocol/rsm">>, + cdata = #cdata{dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}}). + +-xml(rsm_max, + #elem{name = <<"max">>, result = '$cdata', + xmlns = <<"http://jabber.org/protocol/rsm">>, + cdata = #cdata{dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}}). + +-xml(rsm_first, + #elem{name = <<"first">>, + xmlns = <<"http://jabber.org/protocol/rsm">>, + result = {rsm_first, '$index', '$data'}, + cdata = #cdata{label = '$data'}, + attrs = [#attr{name = <<"index">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}]}). + +-xml(rsm_set, + #elem{name = <<"set">>, + xmlns = <<"http://jabber.org/protocol/rsm">>, + result = {rsm_set, '$after', '$before', '$count', + '$first', '$index', '$last', '$max'}, + refs = [#ref{name = rsm_after, label = '$after', min = 0, max = 1}, + #ref{name = rsm_before, label = '$before', min = 0, max = 1}, + #ref{name = rsm_count, label = '$count', min = 0, max = 1}, + #ref{name = rsm_first, label = '$first', min = 0, max = 1}, + #ref{name = rsm_index, label = '$index', min = 0, max = 1}, + #ref{name = rsm_last, label = '$last', min = 0, max = 1}, + #ref{name = rsm_max, label = '$max', min = 0, max = 1}]}). + +-xml(mam_start, + #elem{name = <<"start">>, + xmlns = <<"urn:xmpp:mam:tmp">>, + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_utc, []}, + enc = {enc_utc, []}}}). + +-xml(mam_end, + #elem{name = <<"end">>, + xmlns = <<"urn:xmpp:mam:tmp">>, + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_utc, []}, + enc = {enc_utc, []}}}). + +-xml(mam_with, + #elem{name = <<"with">>, + xmlns = <<"urn:xmpp:mam:tmp">>, + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}}). + +-xml(mam_withtext, + #elem{name = <<"withtext">>, + xmlns = <<"urn:xmpp:mam:tmp">>, + result = '$cdata', + cdata = #cdata{required = true}}). + +-xml(mam_query, + #elem{name = <<"query">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = {mam_query, '$xmlns', '$id', '$start', '$end', '$with', + '$withtext', '$rsm', '$xdata'}, + attrs = [#attr{name = <<"queryid">>, label = '$id'}, + #attr{name = <<"xmlns">>}], + refs = [#ref{name = mam_start, min = 0, max = 1, label = '$start'}, + #ref{name = mam_end, min = 0, max = 1, label = '$end'}, + #ref{name = mam_with, min = 0, max = 1, label = '$with'}, + #ref{name = mam_withtext, min = 0, max = 1, label = '$withtext'}, + #ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}, + #ref{name = xdata, min = 0, max = 1, label = '$xdata'}]}). + +-xml(mam_archived, + #elem{name = <<"archived">>, + xmlns = <<"urn:xmpp:mam:tmp">>, + result = {mam_archived, '$by', '$id'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"by">>, + required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}]}). + +-xml(mam_result, + #elem{name = <<"result">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = {mam_result, '$xmlns', '$queryid', '$id', '$_els'}, + attrs = [#attr{name = <<"queryid">>}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"id">>}]}). + +-xml(mam_jid, + #elem{name = <<"jid">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_jid, []}, + enc = {enc_jid, []}}}). + +-xml(mam_never, + #elem{name = <<"never">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = '$jids', + refs = [#ref{name = mam_jid, label = '$jids'}]}). + +-xml(mam_always, + #elem{name = <<"always">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = '$jids', + refs = [#ref{name = mam_jid, label = '$jids'}]}). + +-xml(mam_prefs, + #elem{name = <<"prefs">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], + result = {mam_prefs, '$xmlns', '$default', '$always', '$never'}, + attrs = [#attr{name = <<"default">>, + dec = {dec_enum, [[always, never, roster]]}, + enc = {enc_enum, []}}, + #attr{name = <<"xmlns">>}], + refs = [#ref{name = mam_always, label = '$always', + min = 0, max = 1}, + #ref{name = mam_never, label = '$never', + min = 0, max = 1}]}). + +-xml(mam_fin, + #elem{name = <<"fin">>, + xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>], + result = {mam_fin, '$xmlns', '$id', '$rsm', '$stable', '$complete'}, + attrs = [#attr{name = <<"queryid">>, label = '$id'}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"stable">>, label = '$stable', + dec = {dec_bool, []}, + enc = {enc_bool, []}}, + #attr{name = <<"complete">>, label = '$complete', + dec = {dec_bool, []}, + enc = {enc_bool, []}}], + refs = [#ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}]}). + +-xml(forwarded, + #elem{name = <<"forwarded">>, + xmlns = <<"urn:xmpp:forward:0">>, + result = {forwarded, '$delay', '$_els'}, + refs = [#ref{name = delay, min = 0, + max = 1, label = '$delay'}]}). + +-xml(carbons_disable, + #elem{name = <<"disable">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_disable}}). + +-xml(carbons_enable, + #elem{name = <<"enable">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_enable}}). + +-xml(carbons_private, + #elem{name = <<"private">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_private}}). + +-xml(carbons_received, + #elem{name = <<"received">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_received, '$forwarded'}, + refs = [#ref{name = forwarded, min = 1, + max = 1, label = '$forwarded'}]}). + +-xml(carbons_sent, + #elem{name = <<"sent">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_sent, '$forwarded'}, + refs = [#ref{name = forwarded, min = 1, + max = 1, label = '$forwarded'}]}). + +-xml(feature_csi, + #elem{name = <<"csi">>, + xmlns = <<"urn:xmpp:csi:0">>, + result = {feature_csi, '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}]}). + +-record(csi, {type :: active | inactive}). +-type csi() :: #csi{}. + +-xml(csi_active, + #elem{name = <<"active">>, + xmlns = <<"urn:xmpp:csi:0">>, + result = {csi, active}}). + +-xml(csi_inactive, + #elem{name = <<"inactive">>, + xmlns = <<"urn:xmpp:csi:0">>, + result = {csi, inactive}}). + +-xml(feature_sm, + #elem{name = <<"sm">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {feature_sm, '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}]}). + +-xml(sm_enable, + #elem{name = <<"enable">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_enable, '$max', '$resume', '$xmlns'}, + attrs = [#attr{name = <<"max">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"resume">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}]}). + +-xml(sm_enabled, + #elem{name = <<"enabled">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_enabled, '$id', '$location', '$max', '$resume', '$xmlns'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"location">>}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"max">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"resume">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}]}). + +-xml(sm_resume, + #elem{name = <<"resume">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_resume, '$h', '$previd', '$xmlns'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"previd">>, + required = true}]}). + +-xml(sm_resumed, + #elem{name = <<"resumed">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_resumed, '$h', '$previd', '$xmlns'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"previd">>, + required = true}]}). + +-xml(sm_r, + #elem{name = <<"r">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_r, '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}]}). + +-xml(sm_a, + #elem{name = <<"a">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_a, '$h', '$xmlns'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"xmlns">>}]}). + +-xml(sm_failed, + #elem{name = <<"failed">>, + xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], + result = {sm_failed, '$reason', '$h', '$xmlns'}, + attrs = [#attr{name = <<"h">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"xmlns">>}], + refs = [#ref{name = error_bad_request, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_conflict, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_feature_not_implemented, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_forbidden, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_gone, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_internal_server_error, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_item_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_jid_malformed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_acceptable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_allowed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_authorized, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_policy_violation, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_recipient_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_redirect, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_registration_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_timeout, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_resource_constraint, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_service_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_subscription_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_undefined_condition, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_unexpected_request, + min = 0, max = 1, label = '$reason'}]}). + +-xml(offline_purge, + #elem{name = <<"purge">>, + xmlns = <<"http://jabber.org/protocol/offline">>, + result = true}). + +-xml(offline_fetch, + #elem{name = <<"fetch">>, + xmlns = <<"http://jabber.org/protocol/offline">>, + result = true}). + +-xml(offline_item, + #elem{name = <<"item">>, + xmlns = <<"http://jabber.org/protocol/offline">>, + result = {offline_item, '$node', '$action'}, + attrs = [#attr{name = <<"node">>}, + #attr{name = <<"action">>, + dec = {dec_enum, [[view, remove]]}, + enc = {enc_enum, []}}]}). + +-xml(offline, + #elem{name = <<"offline">>, + xmlns = <<"http://jabber.org/protocol/offline">>, + result = {offline, '$items', '$purge', '$fetch'}, + refs = [#ref{name = offline_purge, min = 0, max = 1, + label = '$purge', default = false}, + #ref{name = offline_fetch, min = 0, max = 1, + label = '$fetch', default = false}, + #ref{name = offline_item, min = 0, label = '$items'}]}). + +-xml(mix_subscribe, + #elem{name = <<"subscribe">>, + xmlns = <<"urn:xmpp:mix:0">>, + result = '$node', + attrs = [#attr{name = <<"node">>, + required = true, + label = '$node'}]}). + +-xml(mix_join, + #elem{name = <<"join">>, + xmlns = <<"urn:xmpp:mix:0">>, + result = {mix_join, '$jid', '$subscribe'}, + attrs = [#attr{name = <<"jid">>, + label = '$jid', + dec = {dec_jid, []}, + enc = {enc_jid, []}}], + refs = [#ref{name = mix_subscribe, min = 0, label = '$subscribe'}]}). + +-xml(mix_leave, + #elem{name = <<"leave">>, + xmlns = <<"urn:xmpp:mix:0">>, + result = {mix_leave}}). + +-xml(mix_participant, + #elem{name = <<"participant">>, + xmlns = <<"urn:xmpp:mix:0">>, + result = {mix_participant, '$jid', '$nick'}, + attrs = [#attr{name = <<"jid">>, + required = true, + label = '$jid', + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"nick">>, + label = '$nick'}]}). + +-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' | + 'no-permanent-store' | 'no-permanent-storage'}). +-type hint() :: #hint{}. + +-xml(hint_no_copy, + #elem{name = <<"no-copy">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'no-copy'}}). + +-xml(hint_no_store, + #elem{name = <<"no-store">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'no-store'}}). + +-xml(hint_no_storage, + #elem{name = <<"no-storage">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'no-storage'}}). + +-xml(hint_store, + #elem{name = <<"store">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'store'}}). + +-xml(hint_no_permanent_store, + #elem{name = <<"no-permanent-store">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'no-permanent-store'}}). + +-xml(hint_no_permanent_storage, + #elem{name = <<"no-permanent-storage">>, + xmlns = <<"urn:xmpp:hints">>, + result = {hint, 'no-permanent-storage'}}). + +-xml(search_instructions, + #elem{name = <<"instructions">>, + xmlns = <<"jabber:iq:search">>, + result = '$cdata'}). + +-xml(search_first, + #elem{name = <<"first">>, + xmlns = <<"jabber:iq:search">>, + cdata = #cdata{default = <<"">>}, + result = '$cdata'}). +-xml(search_last, + #elem{name = <<"last">>, + xmlns = <<"jabber:iq:search">>, + cdata = #cdata{default = <<"">>}, + result = '$cdata'}). +-xml(search_nick, + #elem{name = <<"nick">>, + xmlns = <<"jabber:iq:search">>, + cdata = #cdata{default = <<"">>}, + result = '$cdata'}). +-xml(search_email, + #elem{name = <<"email">>, + xmlns = <<"jabber:iq:search">>, + cdata = #cdata{default = <<"">>}, + result = '$cdata'}). + +-xml(search_item, + #elem{name = <<"item">>, + xmlns = <<"jabber:iq:search">>, + result = {search_item, '$jid', '$first', '$last', '$nick', '$email'}, + attrs = [#attr{name = <<"jid">>, + required = true, + enc = {enc_jid, []}, + dec = {dec_jid, []}}], + refs = [#ref{name = search_first, min = 0, max = 1, + label = '$first'}, + #ref{name = search_last, min = 0, max = 1, + label = '$last'}, + #ref{name = search_nick, min = 0, max = 1, + label = '$nick'}, + #ref{name = search_email, min = 0, max = 1, + label = '$email'}]}). + +-xml(search, + #elem{name = <<"query">>, + xmlns = <<"jabber:iq:search">>, + result = {search, '$instructions', '$first', '$last', + '$nick', '$email', '$items', '$xdata'}, + refs = [#ref{name = search_instructions, min = 0, max = 1, + label = '$instructions'}, + #ref{name = search_first, min = 0, max = 1, + label = '$first'}, + #ref{name = search_last, min = 0, max = 1, + label = '$last'}, + #ref{name = search_nick, min = 0, max = 1, + label = '$nick'}, + #ref{name = search_email, min = 0, max = 1, + label = '$email'}, + #ref{name = search_item, label = '$items'}, + #ref{name = xdata, min = 0, max = 1, + label = '$xdata'}]}). + +-xml(xevent_offline, + #elem{name = <<"offline">>, + xmlns = <<"jabber:x:event">>, + result = true}). +-xml(xevent_delivered, + #elem{name = <<"delivered">>, + xmlns = <<"jabber:x:event">>, + result = true}). +-xml(xevent_displayed, + #elem{name = <<"displayed">>, + xmlns = <<"jabber:x:event">>, + result = true}). +-xml(xevent_composing, + #elem{name = <<"composing">>, + xmlns = <<"jabber:x:event">>, + result = true}). +-xml(xevent_id, + #elem{name = <<"id">>, + xmlns = <<"jabber:x:event">>, + cdata = #cdata{}, + result = '$cdata'}). + +-xml(xevent, + #elem{name = <<"x">>, + xmlns = <<"jabber:x:event">>, + result = {xevent, '$offline', '$delivered', '$displayed', + '$composing', '$id'}, + refs = [#ref{name = xevent_offline, min = 0, max = 1, + label = '$offline', default = false}, + #ref{name = xevent_delivered, min = 0, max = 1, + label = '$delivered', default = false}, + #ref{name = xevent_displayed, min = 0, max = 1, + label = '$displayed', default = false}, + #ref{name = xevent_composing, min = 0, max = 1, + label = '$composing', default = false}, + #ref{name = xevent_id, min = 0, max = 1, + label = '$id'}]}). + +-xml(expire, + #elem{name = <<"x">>, + xmlns = <<"jabber:x:expire">>, + result = {expire, '$seconds', '$stored'}, + attrs = [#attr{name = <<"seconds">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"stored">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}]}). + +-xml(nick, + #elem{name = <<"nick">>, + xmlns = <<"http://jabber.org/protocol/nick">>, + result = {nick, '$name'}, + cdata = #cdata{label = '$name', + required = true}}). + +-xml(address, + #elem{name = <<"address">>, + xmlns = <<"http://jabber.org/protocol/address">>, + result = {address, '$type', '$jid', '$desc', '$node', '$delivered'}, + attrs = [#attr{name = <<"type">>, + required = true, + dec = {dec_enum, [[bcc, cc, noreply, ofrom, + replyroom, replyto, to]]}, + enc = {enc_enum, []}}, + #attr{name = <<"jid">>, + enc = {enc_jid, []}, + dec = {dec_jid, []}}, + #attr{name = <<"desc">>}, + #attr{name = <<"node">>}, + #attr{name = <<"delivered">>, + enc = {enc_bool, []}, + dec = {dec_bool, []}}]}). + +-xml(addresses, + #elem{name = <<"addresses">>, + xmlns = <<"http://jabber.org/protocol/address">>, + result = {addresses, '$list'}, + %% TODO: 'min' should be '1', but this is not implemented + refs = [#ref{name = address, label = '$list'}]}). + +-xml(stanza_id, + #elem{name = <<"stanza-id">>, + xmlns = <<"urn:xmpp:sid:0">>, + result = {stanza_id, '$by', '$id'}, + attrs = [#attr{name = <<"id">>, required = true}, + #attr{name = <<"by">>, required = true, + enc = {enc_jid, []}, + dec = {dec_jid, []}}]}). + +-xml(client_id, + #elem{name = <<"client-id">>, + xmlns = <<"urn:xmpp:sid:0">>, + result = {client_id, '$id'}, + attrs = [#attr{name = <<"id">>, required = true}]}). + +-xml(adhoc_command_prev, + #elem{name = <<"prev">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = true}). +-xml(adhoc_command_next, + #elem{name = <<"next">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = true}). +-xml(adhoc_command_complete, + #elem{name = <<"complete">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = true}). + +-xml(adhoc_command_actions, + #elem{name = <<"actions">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = {adhoc_actions, '$execute', '$prev', '$next', '$complete'}, + attrs = [#attr{name = <<"execute">>, + dec = {dec_enum, [[complete, next, prev]]}, + enc = {enc_enum, []}}], + refs = [#ref{name = adhoc_command_prev, min = 0, max = 1, + default = false, label = '$prev'}, + #ref{name = adhoc_command_next, min = 0, max = 1, + default = false, label = '$next'}, + #ref{name = adhoc_command_complete, min = 0, max = 1, + default = false, label = '$complete'}]}). + +-xml(adhoc_command_notes, + #elem{name = <<"note">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = {adhoc_note, '$type', '$data'}, + attrs = [#attr{name = <<"type">>, default = info, + dec = {dec_enum, [[info, warn, error]]}, + enc = {enc_enum, []}}], + cdata = #cdata{default = <<"">>, label = '$data'}}). + +-xml(adhoc_command, + #elem{name = <<"command">>, + xmlns = <<"http://jabber.org/protocol/commands">>, + result = {adhoc_command, '$node', '$action', '$sid', + '$status', '$lang', '$actions', '$notes', '$xdata'}, + attrs = [#attr{name = <<"node">>, required = true}, + #attr{name = <<"xml:lang">>, label = '$lang'}, + #attr{name = <<"sessionid">>, label = '$sid'}, + #attr{name = <<"status">>, + dec = {dec_enum, [[canceled, completed, executing]]}, + enc = {enc_enum, []}}, + #attr{name = <<"action">>, default = execute, + dec = {dec_enum, [[cancel, complete, + execute, next, prev]]}, + enc = {enc_enum, []}}], + refs = [#ref{name = adhoc_command_actions, min = 0, max = 1, + label = '$actions'}, + #ref{name = xdata, min = 0, max = 1}, + #ref{name = adhoc_command_notes, label = '$notes'}]}). + +-xml(db_result, + #elem{name = <<"db:result">>, + xmlns = <<"jabber:server">>, + result = {db_result, '$from', '$to', '$type', '$key', '$_els'}, + cdata = #cdata{default = <<"">>, label = '$key'}, + attrs = [#attr{name = <<"from">>, required = true, + dec = {nameprep, []}, enc = {nameprep, []}}, + #attr{name = <<"to">>, required = true, + dec = {nameprep, []}, enc = {nameprep, []}}, + #attr{name = <<"type">>, + dec = {dec_enum, [[valid, invalid, error]]}, + enc = {enc_enum, []}}]}). + +-xml(db_verify, + #elem{name = <<"db:verify">>, + xmlns = <<"jabber:server">>, + result = {db_verify, '$from', '$to', '$id', '$type', '$key', '$_els'}, + cdata = #cdata{default = <<"">>, label = '$key'}, + attrs = [#attr{name = <<"from">>, required = true, + dec = {nameprep, []}, enc = {nameprep, []}}, + #attr{name = <<"to">>, required = true, + dec = {nameprep, []}, enc = {nameprep, []}}, + #attr{name = <<"id">>, required = true}, + #attr{name = <<"type">>, + dec = {dec_enum, [[valid, invalid, error]]}, + enc = {enc_enum, []}}]}). + +-xml(handshake, + #elem{name = <<"handshake">>, + xmlns = <<"jabber:component:accept">>, + result = {handshake, '$data'}, + cdata = #cdata{default = <<"">>, label = '$data'}}). + +-xml(stream_start, + #elem{name = <<"stream:stream">>, + xmlns = [<<"jabber:client">>, <<"jabber:server">>, + <<"jabber:component:accept">>], + result = {stream_start, '$from', '$to', '$id', + '$version', '$xmlns', '$stream_xmlns', + '$db_xmlns', '$lang'}, + attrs = [#attr{name = <<"from">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"to">>, + dec = {dec_jid, []}, + enc = {enc_jid, []}}, + #attr{name = <<"xmlns">>}, + #attr{name = <<"xmlns:stream">>, + label = '$stream_xmlns', + default = <<"">>}, + #attr{name = <<"xmlns:db">>, + label = '$db_xmlns', + default = <<"">>}, + #attr{name = <<"xml:lang">>, label = '$lang', + default = <<"">>}, + #attr{name = <<"version">>, + dec = {dec_version, []}, + enc = {enc_version, []}}, + #attr{name = <<"id">>, default = <<"">>}]}). + +-xml(bob_data, + #elem{name = <<"data">>, + xmlns = <<"urn:xmpp:bob">>, + result = {bob_data, '$cid', '$max-age', '$type', '$data'}, + attrs = [#attr{name = <<"cid">>, required = true}, + #attr{name = <<"max-age">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"type">>}], + cdata = #cdata{label = '$data', default = <<"">>, + dec = {base64, decode, []}, + enc = {base64, encode, []}}}). + +-xml(captcha, + #elem{name = <<"captcha">>, + xmlns = <<"urn:xmpp:captcha">>, + result = {xcaptcha, '$xdata'}, + refs = [#ref{name = xdata, min = 1, max = 1}]}). + +-xml(media_uri, + #elem{name = <<"uri">>, + xmlns = <<"urn:xmpp:media-element">>, + result = {media_uri, '$type', '$uri'}, + attrs = [#attr{name = <<"type">>, required = true}], + cdata = #cdata{label = '$uri', default = <<"">>}}). + +-xml(media, + #elem{name = <<"media">>, + xmlns = <<"urn:xmpp:media-element">>, + result = {media, '$height', '$width', '$uri'}, + attrs = [#attr{name = <<"height">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"width">>, + dec = {dec_int, [0, inifinity]}, + enc = {enc_int, []}}], + refs = [#ref{name = media_uri, label = '$uri'}]}). + +-xml(oob_url, + #elem{name = <<"url">>, + xmlns = <<"jabber:x:oob">>, + result = '$cdata', + cdata = #cdata{required = true}}). + +-xml(oob_desc, + #elem{name = <<"desc">>, + xmlns = <<"jabber:x:oob">>, + result = '$cdata', + cdata = #cdata{default = <<"">>}}). + +-xml(oob_x, + #elem{name = <<"x">>, + xmlns = <<"jabber:x:oob">>, + result = {oob_x, '$url', '$desc', '$sid'}, + attrs = [#attr{name = <<"sid">>, default = <<"">>}], + refs = [#ref{name = oob_url, min = 1, max = 1, + label = '$url'}, + #ref{name = oob_desc, default = <<"">>, + min = 0, max = 1, label = '$desc'}]}). + +-xml(sic_ip, + #elem{name = <<"ip">>, + xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_ip, []}, + enc = {enc_ip, []}}}). + +-xml(sip_port, + #elem{name = <<"port">>, + xmlns = <<"urn:xmpp:sic:1">>, + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_int, [0, 65535]}, + enc = {enc_int, []}}}). + +-xml(sic, + #elem{name = <<"address">>, + xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], + result = {sic, '$ip', '$port', '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}], + refs = [#ref{name = sic_ip, min = 0, max = 1, label = '$ip'}, + #ref{name = sip_port, min = 0, max = 1, label = '$port'}]}). + +-xml(upload_filename, + #elem{name = <<"filename">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = '$cdata', + cdata = #cdata{required = true}}). + +-xml(upload_size, + #elem{name = <<"size">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}}). + +-xml(upload_content_type, + #elem{name = <<"content-type">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = '$cdata', + cdata = #cdata{default = <<"">>}}). + +-xml(upload_request, + #elem{name = <<"request">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = {upload_request, '$filename', '$size', + '$content-type', '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}], + refs = [#ref{name = upload_filename, label = '$filename', + min = 1, max = 1}, + #ref{name = upload_size, label = '$size', min = 1, max = 1}, + #ref{name = upload_content_type, label = '$content-type', + min = 0, max = 1, default = <<"">>}]}). + +-xml(upload_get, + #elem{name = <<"get">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = '$cdata', + cdata = #cdata{required = true}}). + +-xml(upload_put, + #elem{name = <<"put">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = '$cdata', + cdata = #cdata{required = true}}). + +-xml(upload_slot, + #elem{name = <<"slot">>, + xmlns = [<<"urn:xmpp:http:upload">>, + <<"eu:siacs:conversations:http:upload">>], + result = {upload_slot, '$get', '$put', '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}], + refs = [#ref{name = upload_get, min = 0, max = 1, label = '$get'}, + #ref{name = upload_put, min = 0, max = 1, label = '$put'}]}). + +-xml(thumbnail, + #elem{name = <<"thumbnail">>, + xmlns = <<"urn:xmpp:thumbs:1">>, + result = {thumbnail, '$uri', '$media-type', '$width', '$height'}, + attrs = [#attr{name = <<"uri">>, required = true}, + #attr{name = <<"media-type">>, default = <<"">>}, + #attr{name = <<"width">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"height">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}]}). + +-spec dec_tzo(_) -> {integer(), integer()}. +dec_tzo(Val) -> + [H1, M1] = str:tokens(Val, <<":">>), + H = binary_to_integer(H1), + M = binary_to_integer(M1), + if H >= -12, H =< 12, M >= 0, M < 60 -> + {H, M} + end. + +enc_tzo({H, M}) -> + Sign = if H >= 0 -> + <<>>; + true -> + <<"-">> + end, + list_to_binary([Sign, io_lib:format("~2..0w:~2..0w", [H, M])]). + +-spec dec_utc(_) -> erlang:timestamp(). +dec_utc(Val) -> + {_, _, _} = jlib:datetime_string_to_timestamp(Val). + +enc_utc(Val) -> + jlib:now_to_utc_string(Val). + +-spec dec_jid(_) -> jid:jid(). +dec_jid(Val) -> + case jid:from_string(Val) of + error -> + erlang:error(badarg); + J -> + J + end. + +enc_jid(J) -> + jid:to_string(J). + +-spec resourceprep(_) -> binary(). +resourceprep(R) -> + case jid:resourceprep(R) of + error -> + erlang:error(badarg); + R1 -> + R1 + end. + +-spec nameprep(_) -> binary(). +nameprep(S) -> + case jid:nameprep(S) of + error -> + erlang:error(badarg); + S1 -> + S1 + end. + +-spec dec_bool(_) -> boolean(). +dec_bool(<<"false">>) -> false; +dec_bool(<<"0">>) -> false; +dec_bool(<<"true">>) -> true; +dec_bool(<<"1">>) -> true. + +enc_bool(false) -> <<"false">>; +enc_bool(true) -> <<"true">>. + +join([], _Sep) -> <<>>; +join([H | T], Sep) -> + <> || X <- T >>)/binary>>. + +-spec dec_ip(_) -> inet:ip_address(). +dec_ip(S) -> + {ok, Addr} = inet_parse:address(binary_to_list(S)), + Addr. + +enc_ip({0,0,0,0,0,16#ffff,A,B}) -> + enc_ip({(A bsr 8) band 16#ff, A band 16#ff, + (B bsr 8) band 16#ff, B band 16#ff}); +enc_ip(Addr) -> + list_to_binary(inet_parse:ntoa(Addr)). + +-spec re:split(_, _) -> [binary()]. +-spec base64:decode(_) -> binary(). + +-spec dec_host_port(_) -> binary() | inet:ip_address() | + {binary() | inet:ip_address(), non_neg_integer()}. +dec_host_port(<<$[, T/binary>>) -> + [IP, <<$:, Port/binary>>] = binary:split(T, <<$]>>), + {dec_ip(IP), dec_int(Port, 0, 65535)}; +dec_host_port(S) -> + case binary:split(S, <<$:>>) of + [S] -> + try dec_ip(S) catch _:_ -> S end; + [S, P] -> + {try dec_ip(S) catch _:_ -> S end, dec_int(P, 0, 65535)} + end. + +enc_host_port(Host) when is_binary(Host) -> + Host; +enc_host_port({{_,_,_,_,_,_,_,_} = IPv6, Port}) -> + enc_host_port({<<$[, (enc_ip(IPv6))/binary, $]>>, Port}); +enc_host_port({{_,_,_,_} = IPv4, Port}) -> + enc_host_port({enc_ip(IPv4), Port}); +enc_host_port({Host, Port}) -> + <>; +enc_host_port(Addr) -> + enc_ip(Addr). + +-spec dec_version(_) -> {non_neg_integer(), non_neg_integer()}. +dec_version(S) -> + [Major, Minor] = binary:split(S, <<$.>>), + {binary_to_integer(Major), binary_to_integer(Minor)}. + +enc_version({Maj, Min}) -> + <<(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: +%% mode: erlang +%% End: +%% vim: set filetype=erlang tabstop=8: diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 9650f3773..a122eda8e 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -73,6 +73,7 @@ captcha_text(Lang) -> mk_ocr_field(Lang, CID, Type) -> URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>}, #xdata_field{var = <<"ocr">>, + type = 'text-single', label = captcha_text(Lang), required = true, sub_els = [#media{uri = [URI]}]}. diff --git a/src/flex_offline.erl b/src/flex_offline.erl new file mode 100644 index 000000000..090ab3ddf --- /dev/null +++ b/src/flex_offline.erl @@ -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">>)}. diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 4d9bdd61f..65a745d2d 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -488,14 +488,11 @@ concat_identities(#disco_info{identities = Identities}) -> -spec concat_info(disco_info()) -> iolist(). concat_info(#disco_info{xdata = Xs}) -> lists:sort( - [concat_xdata_fields(Fs) || #xdata{type = result, fields = Fs} <- Xs]). - --spec concat_xdata_fields([xdata_field()]) -> iolist(). -concat_xdata_fields(Fields) -> - Form = case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var, Fields) of - #xdata_field{values = Values} -> Values; - false -> [] - end, + [concat_xdata_fields(X) || #xdata{type = result} = X <- Xs]). + +-spec concat_xdata_fields(xdata()) -> iolist(). +concat_xdata_fields(#xdata{fields = Fields} = X) -> + Form = xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X), Res = [[Var, $<, lists:sort([[Val, $<] || Val <- Values])] || #xdata_field{var = Var, values = Values} <- Fields, is_binary(Var), Var /= <<"FORM_TYPE">>], diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 8569ee020..1daae5aa2 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -37,7 +37,7 @@ 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, 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("logger.hrl"). @@ -191,39 +191,17 @@ remove_room(LServer, Name, Host) -> Mod:remove_room(LServer, LName, LHost), ok. --spec get_room_config([xdata_field()], mod_muc_room:state(), jid(), binary()) - -> [xdata_field()]. -get_room_config(XFields, RoomState, _From, Lang) -> +-spec get_room_config([muc_roomconfig:property()], mod_muc_room:state(), + jid(), binary()) -> [muc_roomconfig:property()]. +get_room_config(Fields, RoomState, _From, _Lang) -> Config = RoomState#state.config, - Label = <<"Enable message archiving">>, - 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]. + Fields ++ [{mam, Config#config.mam}]. --spec set_room_option({pos_integer(), _}, binary(), [binary()], binary()) +-spec set_room_option({pos_integer(), _}, muc_roomconfig:property(), binary()) -> {pos_integer(), _}. -set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) -> - try - Val = case Vals of - [<<"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) -> +set_room_option(_Acc, {mam, Val}, _Lang) -> + {#config.mam, Val}; +set_room_option(Acc, _Property, _Lang) -> Acc. -spec user_receive_packet(stanza(), ejabberd_c2s:state(), jid(), jid(), jid()) -> stanza(). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 66604394b..b189508a8 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -668,19 +668,18 @@ get_nick(ServerHost, Host, From) -> Mod:get_nick(LServer, Host, From). iq_get_register_info(ServerHost, Host, From, Lang) -> - {Nick, NickVals, Registered} = case get_nick(ServerHost, Host, From) of - error -> {<<"">>, [], false}; - N -> {N, [N], true} - end, + {Nick, Registered} = case get_nick(ServerHost, Host, From) of + error -> {<<"">>, false}; + N -> {N, true} + end, Title = <<(translate:translate( Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>, Inst = translate:translate(Lang, <<"Enter nickname you want to register">>), - Field = #xdata_field{type = 'text-single', - label = translate:translate(Lang, <<"Nickname">>), - var = <<"nick">>, - values = NickVals}, + Fields = muc_register:encode( + [{roomnick, Nick}], + fun(T) -> translate:translate(Lang, T) end), X = #xdata{type = form, title = Title, - instructions = [Inst], fields = [Field]}, + instructions = [Inst], fields = Fields}, #register{nick = Nick, registered = Registered, instructions = @@ -717,12 +716,13 @@ process_iq_register_set(ServerHost, Host, From, #register{nick = Nick, xdata = XData}, Lang) -> case XData of #xdata{type = submit, fields = Fs} -> - case lists:keyfind(<<"nick">>, #xdata_field.var, Fs) of - #xdata_field{values = [N]} -> - iq_set_register_info(ServerHost, Host, From, N, Lang); - _ -> - ErrText = <<"You must fill in field \"Nickname\" in the form">>, - {error, xmpp:err_not_acceptable(ErrText, Lang)} + try + Options = muc_register:decode(Fs), + N = proplists:get_value(roomnick, Options), + iq_set_register_info(ServerHost, Host, From, N, Lang) + catch _:{muc_register, Why} -> + ErrText = muc_register:format_error(Why), + {error, xmpp:err_bad_request(ErrText, Lang)} end; #xdata{} -> Txt = <<"Incorrect data form">>, diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 339b85ecb..52401f835 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -754,100 +754,131 @@ process_groupchat_message(From, #message{lang = Lang} = Packet, StateData) -> -spec process_normal_message(jid(), message(), state()) -> state(). process_normal_message(From, #message{lang = Lang} = Pkt, StateData) -> - IsInvitation = is_invitation(Pkt), - IsVoiceRequest = is_voice_request(Pkt) and - is_visitor(From, StateData), - IsVoiceApprovement = is_voice_approvement(Pkt) and - not is_visitor(From, StateData), - if IsInvitation -> - case check_invitation(From, Pkt, StateData) of - {error, Error} -> - ejabberd_router:route_error(StateData#state.jid, From, Pkt, Error), - StateData; - IJID -> - Config = StateData#state.config, - case Config#config.members_only of - true -> - case get_affiliation(IJID, StateData) of - none -> - NSD = set_affiliation(IJID, member, StateData), - send_affiliation(IJID, member, StateData), - store_room(NSD), - NSD; - _ -> - StateData - end; - false -> - StateData - end - end; - IsVoiceRequest -> - case (StateData#state.config)#config.allow_voice_requests of + Action = lists:foldl( + fun(_, {error, _} = Err) -> + Err; + (#muc_user{invites = [#muc_invite{to = undefined}]}, _) -> + Txt = <<"No 'to' attribute found">>, + {error, xmpp:err_bad_request(Txt, Lang)}; + (#muc_user{invites = [I]}, _) -> + {ok, I}; + (#muc_user{invites = [_|_]}, _) -> + Txt = <<"Multiple elements are not allowed">>, + {error, xmpp:err_resource_constraint(Txt, Lang)}; + (#xdata{type = submit, fields = Fs}, _) -> + try {ok, muc_request:decode(Fs)} + catch _:{muc_request, Why} -> + Txt = muc_request:format_error(Why), + {error, xmpp:err_bad_request(Txt, Lang)} + end; + (_, Acc) -> + Acc + end, ok, xmpp:get_els(Pkt)), + case Action of + {ok, #muc_invite{} = Invitation} -> + process_invitation(From, Pkt, Invitation, StateData); + {ok, [{role, participant}]} -> + process_voice_request(From, Pkt, StateData); + {ok, VoiceApproval} -> + process_voice_approval(From, Pkt, VoiceApproval, StateData); + {error, Err} -> + ejabberd_router:route_error(StateData#state.jid, From, Pkt, Err), + StateData; + ok -> + StateData + end. + +-spec process_invitation(jid(), message(), muc_invite(), state()) -> state(). +process_invitation(From, Pkt, Invitation, StateData) -> + Lang = xmpp:get_lang(Pkt), + case check_invitation(From, Invitation, Lang, StateData) of + {error, Error} -> + ejabberd_router:route_error(StateData#state.jid, From, Pkt, Error), + StateData; + IJID -> + Config = StateData#state.config, + case Config#config.members_only of true -> - MinInterval = (StateData#state.config)#config.voice_request_min_interval, - BareFrom = jid:remove_resource(jid:tolower(From)), - NowPriority = -p1_time_compat:system_time(micro_seconds), - CleanPriority = NowPriority + MinInterval * 1000000, - Times = clean_treap(StateData#state.last_voice_request_time, - CleanPriority), - case treap:lookup(BareFrom, Times) of - error -> - Times1 = treap:insert(BareFrom, - NowPriority, - true, Times), - NSD = StateData#state{last_voice_request_time = Times1}, - send_voice_request(From, Lang, NSD), + case get_affiliation(IJID, StateData) of + none -> + NSD = set_affiliation(IJID, member, StateData), + send_affiliation(IJID, member, StateData), + store_room(NSD), NSD; - {ok, _, _} -> - ErrText = <<"Please, wait for a while before sending " - "new voice request">>, - Err = xmpp:err_not_acceptable(ErrText, Lang), - ejabberd_router:route_error( - StateData#state.jid, From, Pkt, Err), - StateData#state{last_voice_request_time = Times} + _ -> + StateData end; false -> - ErrText = <<"Voice requests are disabled in this conference">>, - Err = xmpp:err_forbidden(ErrText, Lang), + StateData + end + end. + +-spec process_voice_request(jid(), message(), state()) -> state(). +process_voice_request(From, Pkt, StateData) -> + Lang = xmpp:get_lang(Pkt), + case (StateData#state.config)#config.allow_voice_requests of + true -> + MinInterval = (StateData#state.config)#config.voice_request_min_interval, + BareFrom = jid:remove_resource(jid:tolower(From)), + NowPriority = -p1_time_compat:system_time(micro_seconds), + CleanPriority = NowPriority + MinInterval * 1000000, + Times = clean_treap(StateData#state.last_voice_request_time, + CleanPriority), + case treap:lookup(BareFrom, Times) of + error -> + Times1 = treap:insert(BareFrom, + NowPriority, + true, Times), + NSD = StateData#state{last_voice_request_time = Times1}, + send_voice_request(From, Lang, NSD), + NSD; + {ok, _, _} -> + ErrText = <<"Please, wait for a while before sending " + "new voice request">>, + Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error( StateData#state.jid, From, Pkt, Err), - StateData + StateData#state{last_voice_request_time = Times} end; - IsVoiceApprovement -> - case is_moderator(From, StateData) of - true -> - case extract_jid_from_voice_approvement(Pkt) of - error -> - ErrText = <<"Failed to extract JID from your voice " - "request approval">>, - Err = xmpp:err_bad_request(ErrText, Lang), - ejabberd_router:route_error( - StateData#state.jid, From, Pkt, Err), - StateData; - TargetJid -> - case is_visitor(TargetJid, StateData) of - true -> - Reason = <<>>, - NSD = set_role(TargetJid, - participant, - StateData), - catch send_new_presence(TargetJid, - Reason, - NSD, - StateData), - NSD; - _ -> - StateData - end + false -> + ErrText = <<"Voice requests are disabled in this conference">>, + Err = xmpp:err_forbidden(ErrText, Lang), + ejabberd_router:route_error( + StateData#state.jid, From, Pkt, Err), + StateData + end. + +-spec process_voice_approval(jid(), message(), [muc_request:property()], state()) -> state(). +process_voice_approval(From, Pkt, VoiceApproval, StateData) -> + Lang = xmpp:get_lang(Pkt), + case is_moderator(From, StateData) of + true -> + case lists:keyfind(jid, 1, VoiceApproval) of + {_, TargetJid} -> + Allow = proplists:get_bool(request_allow, VoiceApproval), + case is_visitor(TargetJid, StateData) of + true when Allow -> + Reason = <<>>, + NSD = set_role(TargetJid, participant, StateData), + catch send_new_presence( + TargetJid, Reason, NSD, StateData), + NSD; + _ -> + StateData end; - _ -> - ErrText = <<"Only moderators can approve voice requests">>, - Err = xmpp:err_not_allowed(ErrText, Lang), + false -> + ErrText = <<"Failed to extract JID from your voice " + "request approval">>, + Err = xmpp:err_bad_request(ErrText, Lang), ejabberd_router:route_error( StateData#state.jid, From, Pkt, Err), StateData end; - true -> + false -> + ErrText = <<"Only moderators can approve voice requests">>, + Err = xmpp:err_not_allowed(ErrText, Lang), + ejabberd_router:route_error( + StateData#state.jid, From, Pkt, Err), StateData end. @@ -3090,34 +3121,6 @@ is_password_settings_correct(XData, StateData) -> _ -> true end. --define(XFIELD(Type, Label, Var, Vals), - #xdata_field{type = Type, - label = translate:translate(Lang, Label), - var = Var, - values = Vals}). - --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(PRIVATEXFIELD(Label, Var, Val), - ?XFIELD('text-private', Label, Var, [Val])). - --define(JIDMULTIXFIELD(Label, Var, JIDList), - ?XFIELD('jid-multi', Label, Var, - [jid:to_string(JID) || JID <- JIDList])). - --spec make_options([{binary(), binary()}], binary()) -> [xdata_option()]. -make_options(Options, Lang) -> - [#xdata_option{label = translate:translate(Lang, Label), - value = Value} || {Label, Value} <- Options]. - -spec get_default_room_maxusers(state()) -> non_neg_integer(). get_default_room_maxusers(RoomState) -> DefRoomOpts = @@ -3138,424 +3141,156 @@ get_config(Lang, StateData, From) -> {MaxUsersRoomInteger, MaxUsersRoomString} = case get_max_users(StateData) of N when is_integer(N) -> - {N, integer_to_binary(N)}; - _ -> {0, <<"none">>} + {N, N}; + _ -> {0, none} end, Title = iolist_to_binary( io_lib:format( translate:translate(Lang, <<"Configuration of room ~s">>), [jid:to_string(StateData#state.jid)])), - Fs = [#xdata_field{type = hidden, - var = <<"FORM_TYPE">>, - values = [<<"http://jabber.org/protocol/muc#roomconfig">>]}, - ?STRINGXFIELD(<<"Room title">>, - <<"muc#roomconfig_roomname">>, (Config#config.title)), - ?STRINGXFIELD(<<"Room description">>, - <<"muc#roomconfig_roomdesc">>, - (Config#config.description))] ++ + Fs = [{roomname, Config#config.title}, + {roomdesc, Config#config.description}] ++ case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of - allow -> - [?BOOLXFIELD(<<"Make room persistent">>, - <<"muc#roomconfig_persistentroom">>, - (Config#config.persistent))]; + allow -> [{persistentroom, Config#config.persistent}]; deny -> [] end ++ - [?BOOLXFIELD(<<"Make room public searchable">>, - <<"muc#roomconfig_publicroom">>, - (Config#config.public)), - ?BOOLXFIELD(<<"Make participants list public">>, - <<"public_list">>, (Config#config.public_list)), - ?BOOLXFIELD(<<"Make room password protected">>, - <<"muc#roomconfig_passwordprotectedroom">>, - (Config#config.password_protected)), - ?PRIVATEXFIELD(<<"Password">>, - <<"muc#roomconfig_roomsecret">>, - case Config#config.password_protected of - true -> Config#config.password; - false -> <<"">> - end), - #xdata_field{type = 'list-single', - label = translate:translate( - Lang, <<"Maximum Number of Occupants">>), - var = <<"muc#roomconfig_maxusers">>, - values = [MaxUsersRoomString], - options = - if is_integer(ServiceMaxUsers) -> []; - true -> make_options( - [{<<"No limit">>, <<"none">>}], - Lang) - end ++ - make_options( - [{integer_to_binary(N), integer_to_binary(N)} - || N <- lists:usort([ServiceMaxUsers, - DefaultRoomMaxUsers, - MaxUsersRoomInteger - | ?MAX_USERS_DEFAULT_LIST]), - N =< ServiceMaxUsers], - Lang)}, - #xdata_field{type = 'list-single', - label = translate:translate( - Lang, <<"Present real Jabber IDs to">>), - var = <<"muc#roomconfig_whois">>, - values = [if Config#config.anonymous -> <<"moderators">>; - true -> <<"anyone">> - end], - options = make_options( - [{<<"moderators only">>, <<"moderators">>}, - {<<"anyone">>, <<"anyone">>}], - Lang)}, - #xdata_field{type = 'list-multi', - label = translate:translate( - Lang, - <<"Roles for which Presence is Broadcasted">>), - var = <<"muc#roomconfig_presencebroadcast">>, - values = [atom_to_binary(Role, utf8) - || Role <- Config#config.presence_broadcast], - options = make_options( - [{<<"Moderator">>, <<"moderator">>}, - {<<"Participant">>, <<"participant">>}, - {<<"Visitor">>, <<"visitor">>}], - Lang)}, - ?BOOLXFIELD(<<"Make room members-only">>, - <<"muc#roomconfig_membersonly">>, - (Config#config.members_only)), - ?BOOLXFIELD(<<"Make room moderated">>, - <<"muc#roomconfig_moderatedroom">>, - (Config#config.moderated)), - ?BOOLXFIELD(<<"Default users as participants">>, - <<"members_by_default">>, - (Config#config.members_by_default)), - ?BOOLXFIELD(<<"Allow users to change the subject">>, - <<"muc#roomconfig_changesubject">>, - (Config#config.allow_change_subj)), - ?BOOLXFIELD(<<"Allow users to send private messages">>, - <<"allow_private_messages">>, - (Config#config.allow_private_messages)), - #xdata_field{type = 'list-single', - label = translate:translate( - Lang, - <<"Allow visitors to send private messages to">>), - var = <<"allow_private_messages_from_visitors">>, - values = [case Config#config.allow_private_messages_from_visitors of - anyone -> <<"anyone">>; - moderators -> <<"moderators">>; - nobody -> <<"nobody">> - end], - options = make_options( - [{<<"nobody">>, <<"nobody">>}, - {<<"moderators only">>, <<"moderators">>}, - {<<"anyone">>, <<"anyone">>}], - Lang)}, - ?BOOLXFIELD(<<"Allow users to query other users">>, - <<"allow_query_users">>, - (Config#config.allow_query_users)), - ?BOOLXFIELD(<<"Allow users to send invites">>, - <<"muc#roomconfig_allowinvites">>, - (Config#config.allow_user_invites)), - ?BOOLXFIELD(<<"Allow visitors to send status text in " - "presence updates">>, - <<"muc#roomconfig_allowvisitorstatus">>, - (Config#config.allow_visitor_status)), - ?BOOLXFIELD(<<"Allow visitors to change nickname">>, - <<"muc#roomconfig_allowvisitornickchange">>, - (Config#config.allow_visitor_nickchange)), - ?BOOLXFIELD(<<"Allow visitors to send voice requests">>, - <<"muc#roomconfig_allowvoicerequests">>, - (Config#config.allow_voice_requests)), - ?BOOLXFIELD(<<"Allow subscription">>, - <<"muc#roomconfig_allow_subscription">>, - (Config#config.allow_subscription)), - ?STRINGXFIELD(<<"Minimum interval between voice requests " - "(in seconds)">>, - <<"muc#roomconfig_voicerequestmininterval">>, - integer_to_binary(Config#config.voice_request_min_interval))] + [{publicroom, Config#config.public}, + {public_list, Config#config.public_list}, + {passwordprotectedroom, Config#config.password_protected}, + {roomsecret, case Config#config.password_protected of + true -> Config#config.password; + false -> <<"">> + end}, + {maxusers, MaxUsersRoomString, + [if is_integer(ServiceMaxUsers) -> []; + true -> [{<<"No limit">>, <<"none">>}] + end] ++ [{integer_to_binary(N), N} + || N <- lists:usort([ServiceMaxUsers, + DefaultRoomMaxUsers, + MaxUsersRoomInteger + | ?MAX_USERS_DEFAULT_LIST]), + N =< ServiceMaxUsers]}, + {whois, if Config#config.anonymous -> moderators; + true -> anyone + end}, + {presencebroadcast, Config#config.presence_broadcast}, + {membersonly, Config#config.members_only}, + {moderatedroom, Config#config.moderated}, + {members_by_default, Config#config.members_by_default}, + {changesubject, Config#config.allow_change_subj}, + {allow_private_messages, Config#config.allow_private_messages}, + {allow_private_messages_from_visitors, + Config#config.allow_private_messages_from_visitors}, + {allow_query_users, Config#config.allow_query_users}, + {allowinvites, Config#config.allow_user_invites}, + {allow_visitor_status, Config#config.allow_visitor_status}, + {allow_visitor_nickchange, Config#config.allow_visitor_nickchange}, + {allow_voice_requests, Config#config.allow_voice_requests}, + {allow_subscription, Config#config.allow_subscription}, + {voice_request_min_interval, Config#config.voice_request_min_interval}] ++ case ejabberd_captcha:is_feature_available() of - true -> - [?BOOLXFIELD(<<"Make room CAPTCHA protected">>, - <<"captcha_protected">>, - (Config#config.captcha_protected))]; + true -> [{captcha_protected, Config#config.captcha_protected}]; false -> [] end ++ - [?JIDMULTIXFIELD(<<"Exclude Jabber IDs from CAPTCHA challenge">>, - <<"muc#roomconfig_captcha_whitelist">>, - ((?SETS):to_list(Config#config.captcha_whitelist)))] + [{captcha_whitelist, + lists:map(fun jid:make/1, ?SETS:to_list(Config#config.captcha_whitelist))}] ++ case mod_muc_log:check_access_log(StateData#state.server_host, From) of - allow -> - [?BOOLXFIELD(<<"Enable logging">>, - <<"muc#roomconfig_enablelogging">>, - (Config#config.logging))]; + allow -> [{enablelogging, Config#config.logging}]; deny -> [] end, Fields = ejabberd_hooks:run_fold(get_room_config, StateData#state.server_host, Fs, [StateData, From, Lang]), - #xdata{type = form, title = Title, fields = Fields}. + #xdata{type = form, title = Title, + fields = muc_roomconfig:encode( + Fields, fun(T) -> translate:translate(Lang, T) end)}. -spec set_config(xdata(), state(), binary()) -> {error, stanza_error()} | {result, undefined, state()}. set_config(#xdata{fields = Fields}, StateData, Lang) -> - Options = [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- Fields], - case set_xoption(Options, StateData#state.config, - StateData#state.server_host, Lang) of - #config{} = Config -> - Res = change_config(Config, StateData), - {result, _, NSD} = Res, - Type = case {(StateData#state.config)#config.logging, - Config#config.logging} - of - {true, false} -> roomconfig_change_disabledlogging; - {false, true} -> roomconfig_change_enabledlogging; - {_, _} -> roomconfig_change - end, - Users = [{U#user.jid, U#user.nick, U#user.role} - || {_, U} <- (?DICT):to_list(StateData#state.users)], - add_to_log(Type, Users, NSD), - Res; - Err -> Err + try + Options = muc_roomconfig:decode(Fields), + #config{} = Config = set_config(Options, StateData#state.config, + StateData#state.server_host, Lang), + {result, _, NSD} = Res = change_config(Config, StateData), + Type = case {(StateData#state.config)#config.logging, + Config#config.logging} + of + {true, false} -> roomconfig_change_disabledlogging; + {false, true} -> roomconfig_change_enabledlogging; + {_, _} -> roomconfig_change + end, + Users = [{U#user.jid, U#user.nick, U#user.role} + || {_, U} <- (?DICT):to_list(StateData#state.users)], + add_to_log(Type, Users, NSD), + Res + catch _:{muc_roomconfig, Why} -> + Txt = muc_roomconfig:format_error(Why), + {error, xmpp:err_bad_request(Txt, Lang)}; + _:{badmatch, {error, #stanza_error{}} = Err} -> + Err end. get_config_opt_name(Pos) -> Fs = [config|record_info(fields, config)], lists:nth(Pos, Fs). --define(SET_BOOL_XOPT(Opt, Val), - case Val of - <<"0">> -> - set_xoption(Opts, setelement(Opt, Config, false), ServerHost, Lang); - <<"false">> -> - set_xoption(Opts, setelement(Opt, Config, false), ServerHost, Lang); - <<"1">> -> set_xoption(Opts, setelement(Opt, Config, true), ServerHost, Lang); - <<"true">> -> - set_xoption(Opts, setelement(Opt, Config, true), ServerHost, Lang); - _ -> - Txt = <<"Value of '~s' should be boolean">>, - OptName = get_config_opt_name(Opt), - ErrTxt = iolist_to_binary(io_lib:format(Txt, [OptName])), - {error, xmpp:err_bad_request(ErrTxt, Lang)} - end). - --define(SET_NAT_XOPT(Opt, Val), - case catch binary_to_integer(Val) of - I when is_integer(I), I > 0 -> - set_xoption(Opts, setelement(Opt, Config, I), ServerHost, Lang); - _ -> - Txt = <<"Value of '~s' should be integer">>, - OptName = get_config_opt_name(Opt), - ErrTxt = iolist_to_binary(io_lib:format(Txt, [OptName])), - {error, xmpp:err_bad_request(ErrTxt, Lang)} - end). - --define(SET_STRING_XOPT(Opt, Vals), - try - V = case Vals of - [] -> <<"">>; - [Val] -> Val; - _ when is_atom(Vals) -> Vals - end, - set_xoption(Opts, setelement(Opt, Config, V), ServerHost, Lang) - catch _:_ -> - Txt = <<"Incorrect value of option '~s'">>, - OptName = get_config_opt_name(Opt), - ErrTxt = iolist_to_binary(io_lib:format(Txt, [OptName])), - {error, xmpp:err_bad_request(ErrTxt, Lang)} - end). - --define(SET_JIDMULTI_XOPT(Opt, Vals), - begin - Set = lists:foldl(fun ({U, S, R}, Set1) -> - (?SETS):add_element({U, S, R}, Set1); - (#jid{luser = U, lserver = S, lresource = R}, - Set1) -> - (?SETS):add_element({U, S, R}, Set1); - (_, Set1) -> Set1 - end, - (?SETS):empty(), Vals), - set_xoption(Opts, setelement(Opt, Config, Set), ServerHost, Lang) - end). - --spec set_xoption([{binary(), [binary()]}], #config{}, +-spec set_config([muc_roomconfig:property()], #config{}, binary(), binary()) -> #config{} | {error, stanza_error()}. -set_xoption([], Config, _ServerHost, _Lang) -> Config; -set_xoption([{<<"muc#roomconfig_roomname">>, Vals} - | Opts], - Config, ServerHost, Lang) -> - ?SET_STRING_XOPT(#config.title, Vals); -set_xoption([{<<"muc#roomconfig_roomdesc">>, Vals} - | Opts], - Config, ServerHost, Lang) -> - ?SET_STRING_XOPT(#config.description, Vals); -set_xoption([{<<"muc#roomconfig_changesubject">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_change_subj, Val); -set_xoption([{<<"allow_query_users">>, [Val]} | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_query_users, Val); -set_xoption([{<<"allow_private_messages">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_private_messages, Val); -set_xoption([{<<"allow_private_messages_from_visitors">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - case Val of - <<"anyone">> -> - ?SET_STRING_XOPT(#config.allow_private_messages_from_visitors, - anyone); - <<"moderators">> -> - ?SET_STRING_XOPT(#config.allow_private_messages_from_visitors, - moderators); - <<"nobody">> -> - ?SET_STRING_XOPT(#config.allow_private_messages_from_visitors, - nobody); - _ -> - Txt = <<"Value of 'allow_private_messages_from_visitors' " - "should be anyone|moderators|nobody">>, - {error, xmpp:err_bad_request(Txt, Lang)} - end; -set_xoption([{<<"muc#roomconfig_allowvisitorstatus">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_visitor_status, Val); -set_xoption([{<<"muc#roomconfig_allowvisitornickchange">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_visitor_nickchange, Val); -set_xoption([{<<"muc#roomconfig_publicroom">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.public, Val); -set_xoption([{<<"public_list">>, [Val]} | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.public_list, Val); -set_xoption([{<<"muc#roomconfig_persistentroom">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.persistent, Val); -set_xoption([{<<"muc#roomconfig_moderatedroom">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.moderated, Val); -set_xoption([{<<"members_by_default">>, [Val]} | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.members_by_default, Val); -set_xoption([{<<"muc#roomconfig_membersonly">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.members_only, Val); -set_xoption([{<<"captcha_protected">>, [Val]} | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.captcha_protected, Val); -set_xoption([{<<"muc#roomconfig_allowinvites">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_user_invites, Val); -set_xoption([{<<"muc#roomconfig_allow_subscription">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_subscription, Val); -set_xoption([{<<"muc#roomconfig_passwordprotectedroom">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.password_protected, Val); -set_xoption([{<<"muc#roomconfig_roomsecret">>, Vals} - | Opts], - Config, ServerHost, Lang) -> - ?SET_STRING_XOPT(#config.password, Vals); -set_xoption([{<<"anonymous">>, [Val]} | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.anonymous, Val); -set_xoption([{<<"muc#roomconfig_presencebroadcast">>, Vals} | Opts], - Config, ServerHost, Lang) -> - Roles = - lists:foldl( - fun(_S, error) -> error; - (S, {M, P, V}) -> - case S of - <<"moderator">> -> {true, P, V}; - <<"participant">> -> {M, true, V}; - <<"visitor">> -> {M, P, true}; - _ -> error - end - end, {false, false, false}, Vals), - case Roles of - error -> - Txt = <<"Value of 'muc#roomconfig_presencebroadcast' should " - "be moderator|participant|visitor">>, - {error, xmpp:err_bad_request(Txt, Lang)}; - {M, P, V} -> - Res = - if M -> [moderator]; true -> [] end ++ - if P -> [participant]; true -> [] end ++ - if V -> [visitor]; true -> [] end, - set_xoption(Opts, Config#config{presence_broadcast = Res}, - ServerHost, Lang) - end; -set_xoption([{<<"muc#roomconfig_allowvoicerequests">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.allow_voice_requests, Val); -set_xoption([{<<"muc#roomconfig_voicerequestmininterval">>, - [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_NAT_XOPT(#config.voice_request_min_interval, Val); -set_xoption([{<<"muc#roomconfig_whois">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - case Val of - <<"moderators">> -> - ?SET_BOOL_XOPT(#config.anonymous, - (iolist_to_binary(integer_to_list(1)))); - <<"anyone">> -> - ?SET_BOOL_XOPT(#config.anonymous, - (iolist_to_binary(integer_to_list(0)))); - _ -> - Txt = <<"Value of 'muc#roomconfig_whois' should be " - "moderators|anyone">>, - {error, xmpp:err_bad_request(Txt, Lang)} - end; -set_xoption([{<<"muc#roomconfig_maxusers">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - case Val of - <<"none">> -> ?SET_STRING_XOPT(#config.max_users, none); - _ -> ?SET_NAT_XOPT(#config.max_users, Val) - end; -set_xoption([{<<"muc#roomconfig_enablelogging">>, [Val]} - | Opts], - Config, ServerHost, Lang) -> - ?SET_BOOL_XOPT(#config.logging, Val); -set_xoption([{<<"muc#roomconfig_captcha_whitelist">>, - Vals} - | Opts], - Config, ServerHost, Lang) -> - JIDs = [jid:from_string(Val) || Val <- Vals], - ?SET_JIDMULTI_XOPT(#config.captcha_whitelist, JIDs); -set_xoption([{<<"FORM_TYPE">>, _} | Opts], Config, ServerHost, Lang) -> - set_xoption(Opts, Config, ServerHost, Lang); -set_xoption([{Opt, Vals} | Opts], Config, ServerHost, Lang) -> - Txt = <<"Unknown option '~s'">>, - ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), - Err = {error, xmpp:err_bad_request(ErrTxt, Lang)}, - case ejabberd_hooks:run_fold(set_room_option, - ServerHost, - Err, - [Opt, Vals, Lang]) of - {error, Reason} -> - {error, Reason}; - {Pos, Val} -> - set_xoption(Opts, setelement(Pos, Config, Val), ServerHost, Lang) - end. +set_config(Opts, Config, ServerHost, Lang) -> + lists:foldl( + fun(_, {error, _} = Err) -> Err; + ({roomname, Title}, C) -> C#config{title = Title}; + ({roomdesc, Desc}, C) -> C#config{description = Desc}; + ({changesubject, V}, C) -> C#config{allow_change_subj = V}; + ({allow_query_users, V}, C) -> C#config{allow_query_users = V}; + ({allow_private_messages, V}, C) -> + C#config{allow_private_messages = V}; + ({allow_private_messages_from_visitors, V}, C) -> + C#config{allow_private_messages_from_visitors = V}; + ({allow_visitor_status, V}, C) -> C#config{allow_visitor_status = V}; + ({allow_visitor_nickchange, V}, C) -> + C#config{allow_visitor_nickchange = V}; + ({publicroom, V}, C) -> C#config{public = V}; + ({public_list, V}, C) -> C#config{public_list = V}; + ({persistentroom, V}, C) -> C#config{persistent = V}; + ({moderatedroom, V}, C) -> C#config{moderated = V}; + ({members_by_default, V}, C) -> C#config{members_by_default = V}; + ({membersonly, V}, C) -> C#config{members_only = V}; + ({captcha_protected, V}, C) -> C#config{captcha_protected = V}; + ({allowinvites, V}, C) -> C#config{allow_user_invites = V}; + ({allow_subscription, V}, C) -> C#config{allow_subscription = V}; + ({passwordprotectedroom, V}, C) -> C#config{password_protected = V}; + ({roomsecret, V}, C) -> C#config{password = V}; + ({anonymous, V}, C) -> C#config{anonymous = V}; + ({presencebroadcast, V}, C) -> C#config{presence_broadcast = V}; + ({allow_voice_requests, V}, C) -> C#config{allow_voice_requests = V}; + ({voice_request_min_interval, V}, C) -> + C#config{voice_request_min_interval = V}; + ({whois, moderators}, C) -> C#config{anonymous = true}; + ({whois, anyone}, C) -> C#config{anonymous = false}; + ({maxusers, V}, C) -> C#config{max_users = V}; + ({enablelogging, V}, C) -> C#config{logging = V}; + ({captcha_whitelist, Js}, C) -> + LJIDs = [jid:tolower(J) || J <- Js], + C#config{captcha_whitelist = ?SETS:from_list(LJIDs)}; + ({O, V} = Opt, C) -> + case ejabberd_hooks:run_fold(set_room_option, + ServerHost, + {0, undefined}, + [Opt, Lang]) of + {0, undefined} -> + ?ERROR_MSG("set_room_option hook failed for " + "option '~s' with value ~p", [O, V]), + Txt = <<"Failed to process option '", O/binary, "'">>, + {error, xmpp:err_internal_server_error(Txt, Lang)}; + {Pos, Val} -> + setelement(Pos, C, Val) + end + end, Config, Opts). -spec change_config(#config{}, state()) -> {result, undefined, state()}. change_config(Config, StateData) -> @@ -3872,33 +3607,13 @@ process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) -> name = get_title(StateData)}], features = Feats}}. --spec mk_rfieldt('boolean' | 'fixed' | 'hidden' | - 'jid-multi' | 'jid-single' | 'list-multi' | - 'list-single' | 'text-multi' | 'text-private' | - 'text-single', binary(), binary()) -> xdata_field(). -mk_rfieldt(Type, Var, Val) -> - #xdata_field{type = Type, var = Var, values = [Val]}. - --spec mk_rfield(binary(), binary(), binary(), binary()) -> xdata_field(). -mk_rfield(Label, Var, Val, Lang) -> - #xdata_field{type = 'text-single', - label = translate:translate(Lang, Label), - var = Var, - values = [Val]}. - -spec iq_disco_info_extras(binary(), state()) -> xdata(). iq_disco_info_extras(Lang, StateData) -> - Len = (?DICT):size(StateData#state.users), - RoomDescription = (StateData#state.config)#config.description, + Fs = [{description, (StateData#state.config)#config.description}, + {occupants, ?DICT:size(StateData#state.users)}], #xdata{type = result, - fields = [mk_rfieldt(hidden, <<"FORM_TYPE">>, - "http://jabber.org/protocol/muc#roominfo"), - mk_rfield(<<"Room description">>, - <<"muc#roominfo_description">>, - RoomDescription, Lang), - mk_rfield(<<"Number of occupants">>, - <<"muc#roominfo_occupants">>, - integer_to_binary(Len), Lang)]}. + fields = muc_roominfo:encode( + Fs, fun(T) -> translate:translate(Lang, T) end)}. -spec process_iq_disco_items(jid(), iq(), state()) -> {error, stanza_error()} | {result, disco_items()}. @@ -4105,39 +3820,16 @@ get_mucroom_disco_items(StateData) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support --spec is_voice_request(message()) -> boolean(). -is_voice_request(Packet) -> - Els = xmpp:get_els(Packet), - lists:any( - fun(#xdata{} = X) -> - case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X), - xmpp_util:get_xdata_values(<<"muc#role">>, X)} of - {[<<"http://jabber.org/protocol/muc#request">>], - [<<"participant">>]} -> - true; - _ -> - false - end; - (_) -> - false - end, Els). - -spec prepare_request_form(jid(), binary(), binary()) -> message(). prepare_request_form(Requester, Nick, Lang) -> Title = translate:translate(Lang, <<"Voice request">>), Instruction = translate:translate( Lang, <<"Either approve or decline the voice request.">>), - Fs = [#xdata_field{var = <<"FORM_TYPE">>, - type = hidden, - values = [<<"http://jabber.org/protocol/muc#request">>]}, - #xdata_field{var = <<"muc#role">>, - type = hidden, - values = [<<"participant">>]}, - ?STRINGXFIELD(<<"User JID">>, <<"muc#jid">>, - jid:to_string(Requester)), - ?STRINGXFIELD(<<"Nickname">>, <<"muc#roomnick">>, Nick), - ?BOOLXFIELD(<<"Grant voice to this person?">>, - <<"muc#request_allow">>, false)], + Fs = muc_request:encode([{role, participant}, + {jid, Requester}, + {roomnick, Nick}, + {request_allow, false}], + fun(T) -> translate:translate(Lang, T) end), #message{type = normal, sub_els = [#xdata{type = form, title = Title, @@ -4155,59 +3847,11 @@ send_voice_request(From, Lang, StateData) -> end, Moderators). --spec is_voice_approvement(message()) -> boolean(). -is_voice_approvement(Packet) -> - Els = xmpp:get_els(Packet), - lists:any( - fun(#xdata{} = X) -> - case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X), - xmpp_util:get_xdata_values(<<"muc#role">>, X), - xmpp_util:get_xdata_values(<<"muc#request_allow">>, X)} of - {[<<"http://jabber.org/protocol/muc#request">>], - [<<"participant">>], [Flag]} when Flag == <<"true">>; - Flag == <<"1">> -> - true; - _ -> - false - end; - (_) -> - false - end, Els). - --spec extract_jid_from_voice_approvement(message()) -> jid() | error. -extract_jid_from_voice_approvement(Packet) -> - Els = xmpp:get_els(Packet), - lists:foldl( - fun(#xdata{} = X, error) -> - case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X), - xmpp_util:get_xdata_values(<<"muc#role">>, X), - xmpp_util:get_xdata_values(<<"muc#request_allow">>, X), - xmpp_util:get_xdata_values(<<"muc#jid">>, X)} of - {[<<"http://jabber.org/protocol/muc#request">>], - [<<"participant">>], [Flag], [J]} when Flag == <<"true">>; - Flag == <<"1">> -> - jid:from_string(J); - _ -> - error - end; - (_, Acc) -> - Acc - end, error, Els). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support --spec is_invitation(message()) -> boolean(). -is_invitation(Packet) -> - Els = xmpp:get_els(Packet), - lists:any( - fun(#muc_user{invites = [_|_]}) -> true; - (_) -> false - end, Els). - --spec check_invitation(jid(), message(), state()) -> {error, stanza_error()} | jid(). -check_invitation(From, Packet, StateData) -> - Lang = xmpp:get_lang(Packet), +-spec check_invitation(jid(), muc_invite(), binary(), state()) -> {error, stanza_error()} | jid(). +check_invitation(From, Invitation, Lang, StateData) -> FAffiliation = get_affiliation(From, StateData), CanInvite = (StateData#state.config)#config.allow_user_invites orelse @@ -4217,57 +3861,46 @@ check_invitation(From, Packet, StateData) -> Txt = <<"Invitations are not allowed in this conference">>, {error, xmpp:err_not_allowed(Txt, Lang)}; true -> - case xmpp:get_subtag(Packet, #muc_user{}) of - #muc_user{invites = [#muc_invite{to = undefined}]} -> - Txt = <<"No 'to' attribute found">>, - {error, xmpp:err_bad_request(Txt, Lang)}; - #muc_user{invites = [#muc_invite{to = JID, reason = Reason} = I]} -> - Invite = I#muc_invite{to = undefined, from = From}, - Password = case (StateData#state.config)#config.password_protected of - true -> - (StateData#state.config)#config.password; - false -> - undefined - end, - XUser = #muc_user{password = Password, invites = [Invite]}, - XConference = #x_conference{jid = jid:make(StateData#state.room, - StateData#state.host), - reason = Reason}, - Body = iolist_to_binary( - [io_lib:format( - translate:translate( - Lang, - <<"~s invites you to the room ~s">>), - [jid:to_string(From), - jid:to_string({StateData#state.room, - StateData#state.host, - <<"">>})]), - case (StateData#state.config)#config.password_protected of - true -> - <<", ", - (translate:translate( - Lang, <<"the password is">>))/binary, - " '", - ((StateData#state.config)#config.password)/binary, - "'">>; - _ -> <<"">> - end, - case Reason of - <<"">> -> <<"">>; - _ -> <<" (", Reason/binary, ") ">> - end]), - Msg = #message{type = normal, - body = xmpp:mk_text(Body), - sub_els = [XUser, XConference]}, - ejabberd_router:route(StateData#state.jid, JID, Msg), - JID; - #muc_user{invites = [_|_]} -> - Txt = <<"Multiple elements are not allowed">>, - {error, xmpp:err_forbidden(Txt, Lang)}; - _ -> - Txt = <<"No element found">>, - {error, xmpp:err_bad_request(Txt, Lang)} - end + #muc_invite{to = JID, reason = Reason} = Invitation, + Invite = Invitation#muc_invite{to = undefined, from = From}, + Password = case (StateData#state.config)#config.password_protected of + true -> + (StateData#state.config)#config.password; + false -> + undefined + end, + XUser = #muc_user{password = Password, invites = [Invite]}, + XConference = #x_conference{jid = jid:make(StateData#state.room, + StateData#state.host), + reason = Reason}, + Body = iolist_to_binary( + [io_lib:format( + translate:translate( + Lang, + <<"~s invites you to the room ~s">>), + [jid:to_string(From), + jid:to_string({StateData#state.room, + StateData#state.host, + <<"">>})]), + case (StateData#state.config)#config.password_protected of + true -> + <<", ", + (translate:translate( + Lang, <<"the password is">>))/binary, + " '", + ((StateData#state.config)#config.password)/binary, + "'">>; + _ -> <<"">> + end, + case Reason of + <<"">> -> <<"">>; + _ -> <<" (", Reason/binary, ") ">> + end]), + Msg = #message{type = normal, + body = xmpp:mk_text(Body), + sub_els = [XUser, XConference]}, + ejabberd_router:route(StateData#state.jid, JID, Msg), + JID end. %% Handle a message sent to the room by a non-participant. diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 258c97d4f..240650234 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -300,8 +300,7 @@ get_sm_items(Acc, _From, _To, _Node, _Lang) -> -spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()]; ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()]. get_info(_Acc, #jid{luser = U, lserver = S, lresource = R}, - #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) -> - N = integer_to_binary(count_offline_messages(U, S)), + #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, Lang) -> case ejabberd_sm:get_session_pid(U, S, R) of Pid when is_pid(Pid) -> Pid ! dont_ask_offline; @@ -309,11 +308,9 @@ get_info(_Acc, #jid{luser = U, lserver = S, lresource = R}, ok end, [#xdata{type = result, - fields = [#xdata_field{var = <<"FORM_TYPE">>, - type = hidden, - values = [?NS_FLEX_OFFLINE]}, - #xdata_field{var = <<"number_of_messages">>, - values = [N]}]}]; + fields = flex_offline:encode( + [{number_of_messages, count_offline_messages(U, S)}], + fun(T) -> translate:translate(Lang, T) end)}]; get_info(Acc, _From, _To, _Node, _Lang) -> Acc. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index b55115c9a..e3fe64c16 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -1028,8 +1028,11 @@ do_route(Host, From, To, Packet) -> case find_authorization_response(Packet) of undefined -> ok; - XData -> - handle_authorization_response(Host, From, To, Packet, XData) + {error, Err} -> + ejabberd_router:route_error(To, From, Packet, Err); + AuthResponse -> + handle_authorization_response( + Host, From, To, Packet, AuthResponse) end; _ -> Err = xmpp:err_service_unavailable(), @@ -1200,19 +1203,28 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, ServerHost = serverhost(Host), Plugins = config(ServerHost, plugins), Config = case Configure of - {_, XData} -> get_xdata_fields(XData); + {_, XData} -> decode_node_config(XData, Host, Lang); undefined -> [] end, 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}, publish_options = XData, _ = undefined}} -> ServerHost = serverhost(Host), case Items of [#ps_item{id = ItemId, xml_els = Payload}] -> - PubOpts = get_xdata_fields(XData), - publish_item(Host, ServerHost, Node, From, ItemId, - Payload, PubOpts, Access); + case decode_publish_options(XData, Lang) of + {error, _} = Err -> + Err; + PubOpts -> + publish_item(Host, ServerHost, Node, From, ItemId, + Payload, PubOpts, Access) + end; [] -> {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}, options = Options, _ = undefined}} -> Config = case Options of - #ps_options{xdata = XData} -> get_xdata_fields(XData); - _ -> [] + #ps_options{xdata = XData} -> + decode_subscribe_options(XData, Lang); + _ -> + [] 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}, _ = undefined}} -> 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, jid = JID, xdata = XData}, _ = 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{}} -> {error, xmpp:err_bad_request()}; _ -> @@ -1284,8 +1308,12 @@ iq_pubsub_owner(Host, #iq{type = IQType, from = From, #xdata{type = cancel} -> {result, #pubsub_owner{}}; #xdata{type = submit} -> - Config = get_xdata_fields(XData), - set_configure(Host, Node, From, Config, Lang); + case decode_node_config(XData, Host, Lang) of + {error, _} = Err -> + Err; + Config -> + set_configure(Host, Node, From, Config, Lang) + end; #xdata{} -> {error, xmpp:err_bad_request(<<"Incorrect data form">>, Lang)} end; @@ -1318,19 +1346,20 @@ adhoc_request(Host, _ServerHost, Owner, send_pending_node_form(Host, Owner, Lang, Plugins); adhoc_request(Host, _ServerHost, Owner, #adhoc_command{node = ?NS_PUBSUB_GET_PENDING, lang = Lang, - action = execute, xdata = #xdata{} = XData}, + action = execute, xdata = #xdata{} = XData} = Request, _Access, _Plugins) -> - Config = get_xdata_fields(XData), - case set_xoption(Host, Config, []) of - XForm when is_list(XForm) -> - case lists:keysearch(node, 1, XForm) of - {value, {_, Node}} -> - send_pending_auth_events(Host, Node, Owner, Lang); - false -> - {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())} - end; - Err -> - Err + case decode_get_pending(XData, Lang) of + {error, _} = Err -> + Err; + Config -> + Node = proplists:get_value(node, Config), + case send_pending_auth_events(Host, Node, Owner, Lang) of + ok -> + xmpp_util:make_adhoc_response( + Request, #adhoc_command{action = completed}); + Err -> + Err + end end; adhoc_request(_Host, _ServerHost, _Owner, #adhoc_command{action = cancel}, _Access, _Plugins) -> @@ -1353,12 +1382,9 @@ send_pending_node_form(Host, Owner, _Lang, Plugins) -> Ps -> case get_pending_nodes(Host, Owner, Ps) of {ok, Nodes} -> - XOpts = [#xdata_option{value = Node} || Node <- Nodes], XForm = #xdata{type = form, - fields = [#xdata_field{ - type = 'list-single', - var = <<"pubsub#node">>, - options = lists:usort(XOpts)}]}, + fields = pubsub_get_pending:encode( + [{node, Nodes}])}, #adhoc_command{status = executing, action = execute, xdata = XForm}; Err -> @@ -1423,24 +1449,11 @@ send_authorization_request(#pubsub_node{nodeid = {Host, Node}, Subscriber) -> %% TODO: pass lang to this function Lang = <<"en">>, - Fs = [#xdata_field{var = <<"FORM_TYPE">>, - type = hidden, - values = [?NS_PUBSUB_SUB_AUTH]}, - #xdata_field{var = <<"pubsub#node">>, - type = 'text-single', - 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">>]}], + Fs = pubsub_subscribe_authorization:encode( + [{node, Node}, + {subscriber_jid, Subscriber}, + {allow, false}], + fun(T) -> translate:translate(Lang, T) end), X = #xdata{type = form, title = translate:translate( 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) 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) -> case xmpp:get_subtag(Packet, #xdata{}) of - #xdata{type = submit} = X -> - case xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X) of - [?NS_PUBSUB_SUB_AUTH] -> X; - _ -> undefined + #xdata{type = cancel} -> + undefined; + #xdata{type = submit, fields = Fs} -> + 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; - _ -> + #xdata{} -> + {error, xmpp:err_bad_request()}; + false -> undefined end. @@ -1477,43 +1499,33 @@ send_authorization_approval(Host, JID, SNode, Subscription) -> Stanza = #message{sub_els = [Event]}, ejabberd_router:route(service_jid(Host), JID, Stanza). --spec handle_authorization_response(binary(), jid(), jid(), message(), xdata()) -> ok. -handle_authorization_response(Host, From, To, Packet, X) -> +-spec handle_authorization_response(binary(), jid(), jid(), message(), + 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), - case {xmpp_util:get_xdata_values(<<"pubsub#node">>, X), - xmpp_util:get_xdata_values(<<"pubsub#subscriber_jid">>, X), - xmpp_util:get_xdata_values(<<"pubsub#allow">>, X)} of - {[Node], [SSubscriber], [SAllow]} -> - FromLJID = jid:tolower(jid:remove_resource(From)), - Subscriber = jid:from_string(SSubscriber), - Allow = case SAllow of - <<"1">> -> true; - <<"true">> -> true; - _ -> false - end, - Action = - fun(#pubsub_node{type = Type, id = Nidx, owners = O}) -> - Owners = node_owners_call(Host, Type, Nidx, O), - case lists:member(FromLJID, Owners) of - true -> - {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]), - update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs); - 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; + FromLJID = jid:tolower(jid:remove_resource(From)), + Action = + fun(#pubsub_node{type = Type, id = Nidx, owners = O}) -> + Owners = node_owners_call(Host, Type, Nidx, O), + case lists:member(FromLJID, Owners) of + true -> + {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]), + update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs); + 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_not_acceptable(<<"Incorrect data form">>, Lang), + Err = xmpp:err_internal_server_error(), ejabberd_router:route_error(To, From, Packet, Err) end. @@ -1539,45 +1551,6 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) -> {error, xmpp:err_unexpected_request(Txt, ?MYLANG)} 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

Create new pubsub nodes

%%

In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:

%%
    @@ -1617,70 +1590,66 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) -> end; create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> Type = select_type(ServerHost, Host, Node, GivenType), - case set_xoption(Host, Configuration, node_options(Host, Type)) of - NodeOptions when is_list(NodeOptions) -> - CreateNode = - fun() -> - Parent = case node_call(Host, Type, node_to_path, [Node]) of - {result, [Node]} -> - <<>>; - {result, Path} -> - element(2, node_call(Host, Type, path_to_node, - [lists:sublist(Path, length(Path)-1)])) - end, - Parents = case Parent of - <<>> -> []; - _ -> [Parent] - end, - case node_call(Host, Type, create_node_permission, - [Host, ServerHost, Node, Parent, Owner, Access]) of - {result, true} -> - case tree_call(Host, create_node, - [Host, Node, Type, Owner, NodeOptions, Parents]) - of - {ok, Nidx} -> - SubsByDepth = get_node_subs_by_depth(Host, Node, Owner), - case node_call(Host, Type, create_node, [Nidx, Owner]) of - {result, Result} -> {result, {Nidx, SubsByDepth, Result}}; - 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 + NodeOptions = merge_config(Configuration, node_options(Host, Type)), + CreateNode = + fun() -> + Parent = case node_call(Host, Type, node_to_path, [Node]) of + {result, [Node]} -> + <<>>; + {result, Path} -> + element(2, node_call(Host, Type, path_to_node, + [lists:sublist(Path, length(Path)-1)])) + end, + Parents = case Parent of + <<>> -> []; + _ -> [Parent] + end, + case node_call(Host, Type, create_node_permission, + [Host, ServerHost, Node, Parent, Owner, Access]) of + {result, true} -> + case tree_call(Host, create_node, + [Host, Node, Type, Owner, NodeOptions, Parents]) + of + {ok, Nidx} -> + SubsByDepth = get_node_subs_by_depth(Host, Node, Owner), + case node_call(Host, Type, create_node, [Nidx, Owner]) of + {result, Result} -> {result, {Nidx, SubsByDepth, Result}}; + Error -> Error end; - _ -> - Txt = <<"You're not allowed to create nodes">>, - {error, xmpp:err_forbidden(Txt, ?MYLANG)} - end - end, - Reply = #pubsub{create = Node}, - case transaction(Host, CreateNode, transaction) of - {result, {Nidx, SubsByDepth, {Result, broadcast}}} -> - broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth), - ejabberd_hooks:run(pubsub_create_node, ServerHost, - [ServerHost, Host, Node, Nidx, NodeOptions]), - case Result of - default -> {result, Reply}; - _ -> {result, Result} - end; - {result, {Nidx, _SubsByDepth, Result}} -> - ejabberd_hooks:run(pubsub_create_node, ServerHost, - [ServerHost, Host, Node, Nidx, NodeOptions]), - case Result of - default -> {result, Reply}; - _ -> {result, Result} - end; - 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, {virtual, Nidx}} -> + case node_call(Host, Type, create_node, [Nidx, Owner]) of + {result, Result} -> {result, {Nidx, [], Result}}; + Error -> Error + end; + Error -> + Error + end; + _ -> + Txt = <<"You're not allowed to create nodes">>, + {error, xmpp:err_forbidden(Txt, ?MYLANG)} + end + end, + Reply = #pubsub{create = Node}, + case transaction(Host, CreateNode, transaction) of + {result, {Nidx, SubsByDepth, {Result, broadcast}}} -> + broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth), + ejabberd_hooks:run(pubsub_create_node, ServerHost, + [ServerHost, Host, Node, Nidx, NodeOptions]), + case Result of + default -> {result, Reply}; + _ -> {result, Result} + end; + {result, {Nidx, _SubsByDepth, Result}} -> + ejabberd_hooks:run(pubsub_create_node, ServerHost, + [ServerHost, Host, Node, Nidx, NodeOptions]), + case Result of + default -> {result, Reply}; + _ -> {result, Result} end; 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 end. @@ -2636,7 +2605,7 @@ set_subscriptions(Host, Node, From, Entities) -> Owner = jid:tolower(jid:remove_resource(From)), Notify = fun(#ps_subscription{jid = JID, type = Sub}) -> Stanza = #message{ - sub_els = [#pubsub{ + sub_els = [#ps_event{ subscription = #ps_subscription{ jid = JID, type = Sub, @@ -3266,83 +3235,17 @@ max_items(Host, Options) -> end end. --define(BOOL_CONFIG_FIELD(Label, Var), - ?BOOLXFIELD(Label, - <<"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, []))). - +-spec get_configure_xfields(_, pubsub_node_config:result(), + binary(), [binary()]) -> [xdata_field()]. get_configure_xfields(_Type, Options, Lang, Groups) -> - [?XFIELD(hidden, <<>>, <<"FORM_TYPE">>, ?NS_PUBSUB_NODE_CONFIG), - ?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>, - deliver_payloads), - ?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>, - deliver_notifications), - ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node configuration changes">>, - notify_config), - ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node is deleted">>, - 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])]. + pubsub_node_config:encode( + lists:map( + fun({roster_groups_allowed, Value}) -> + {roster_groups_allowed, Value, Groups}; + (Opt) -> + Opt + end, Options), + fun(Txt) -> translate:translate(Lang, Txt) end). %%

    There are several reasons why the node configuration request might fail:

    %%
      @@ -3365,18 +3268,13 @@ set_configure(Host, Node, From, Config, Lang) -> [] -> node_options(Host, Type); _ -> Options end, - case set_xoption(Host, Config, OldOpts) of - NewOpts when is_list(NewOpts) -> - case tree_call(Host, - set_node, - [N#pubsub_node{options = NewOpts}]) - of - {result, Nidx} -> {result, ok}; - ok -> {result, ok}; - Err -> Err - end; - Error -> - Error + NewOpts = merge_config(Config, OldOpts), + case tree_call(Host, + set_node, + [N#pubsub_node{options = NewOpts}]) of + {result, Nidx} -> {result, ok}; + ok -> {result, ok}; + Err -> Err end; _ -> {error, xmpp:err_forbidden( @@ -3394,119 +3292,82 @@ set_configure(Host, Node, From, Config, Lang) -> Other end. --spec add_opt(atom(), any(), [{atom(), any()}]) -> [{atom(), any()}]. -add_opt(Key, Value, Opts) -> - lists:keystore(Key, 1, Opts, {Key, Value}). +-spec merge_config([proplists:property()], [proplists:property()]) -> [proplists:property()]. +merge_config(Config1, Config2) -> + lists:foldl( + fun({Opt, Val}, Acc) -> + lists:keystore(Opt, 1, Acc, {Opt, Val}) + end, Config2, Config1). --define(SET_BOOL_XOPT(Opt, Val), - BoolVal = case Val of - <<"0">> -> false; - <<"1">> -> true; - <<"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). +-spec decode_node_config(undefined | xdata(), binary(), binary()) -> + pubsub_node_config:result() | + {error, stanza_error()}. +decode_node_config(undefined, _, _) -> + []; +decode_node_config(#xdata{fields = Fs}, Host, Lang) -> + 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. --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) -> +-spec decode_publish_options(undefined | xdata(), binary()) -> + pubsub_publish_options:result() | + {error, stanza_error()}. +decode_publish_options(undefined, _) -> []; -get_xdata_fields(#xdata{fields = Fs}) -> - [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- Fs]. +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(). get_max_items_node(Host) -> diff --git a/src/muc_register.erl b/src/muc_register.erl new file mode 100644 index 000000000..cddce2b98 --- /dev/null +++ b/src/muc_register.erl @@ -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">>)}. diff --git a/src/muc_request.erl b/src/muc_request.erl new file mode 100644 index 000000000..4c7becd2e --- /dev/null +++ b/src/muc_request.erl @@ -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?">>)}. diff --git a/src/muc_roomconfig.erl b/src/muc_roomconfig.erl new file mode 100644 index 000000000..73ceb649e --- /dev/null +++ b/src/muc_roomconfig.erl @@ -0,0 +1,1675 @@ +%% Created automatically by xdata generator (xdata_codec.erl) +%% Source: muc_roomconfig.xdata +%% Form type: http://jabber.org/protocol/muc#roomconfig +%% Document: XEP-0045 + +-module(muc_roomconfig). + +-export([decode/1, decode/2, encode/1, encode/2, + format_error/1]). + +-include("xmpp_codec.hrl"). + +-include("muc_roomconfig.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_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, 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. + +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#roomconfig">>]} -> + decode(Fs, Acc, []); + _ -> + erlang:error({?MODULE, + {form_type_mismatch, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + 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}); + {allowpm, Val} -> + [encode_allowpm(Val, default, Translate)]; + {allowpm, Val, Opts} -> + [encode_allowpm(Val, Opts, Translate)]; + {allow_private_messages, Val} -> + [encode_allow_private_messages(Val, Translate)]; + {allow_private_messages, _, _} -> + erlang:error({badarg, Opt}); + {allow_private_messages_from_visitors, Val} -> + [encode_allow_private_messages_from_visitors(Val, + default, + Translate)]; + {allow_private_messages_from_visitors, Val, Opts} -> + [encode_allow_private_messages_from_visitors(Val, Opts, + Translate)]; + {allow_visitor_status, Val} -> + [encode_allow_visitor_status(Val, Translate)]; + {allow_visitor_status, _, _} -> + erlang:error({badarg, Opt}); + {allow_visitor_nickchange, Val} -> + [encode_allow_visitor_nickchange(Val, Translate)]; + {allow_visitor_nickchange, _, _} -> + erlang:error({badarg, Opt}); + {allow_voice_requests, Val} -> + [encode_allow_voice_requests(Val, Translate)]; + {allow_voice_requests, _, _} -> + erlang:error({badarg, Opt}); + {allow_subscription, Val} -> + [encode_allow_subscription(Val, Translate)]; + {allow_subscription, _, _} -> + erlang:error({badarg, Opt}); + {voice_request_min_interval, Val} -> + [encode_voice_request_min_interval(Val, Translate)]; + {voice_request_min_interval, _, _} -> + erlang:error({badarg, Opt}); + {captcha_protected, Val} -> + [encode_captcha_protected(Val, Translate)]; + {captcha_protected, _, _} -> + erlang:error({badarg, Opt}); + {captcha_whitelist, Val} -> + [encode_captcha_whitelist(Val, Translate)]; + {captcha_whitelist, _, _} -> + erlang:error({badarg, Opt}); + {allow_query_users, Val} -> + [encode_allow_query_users(Val, Translate)]; + {allow_query_users, _, _} -> + erlang:error({badarg, Opt}); + {allowinvites, Val} -> + [encode_allowinvites(Val, Translate)]; + {allowinvites, _, _} -> erlang:error({badarg, Opt}); + {changesubject, Val} -> + [encode_changesubject(Val, Translate)]; + {changesubject, _, _} -> erlang:error({badarg, Opt}); + {enablelogging, Val} -> + [encode_enablelogging(Val, Translate)]; + {enablelogging, _, _} -> erlang:error({badarg, Opt}); + {getmemberlist, Val} -> + [encode_getmemberlist(Val, default, Translate)]; + {getmemberlist, Val, Opts} -> + [encode_getmemberlist(Val, Opts, Translate)]; + {lang, Val} -> [encode_lang(Val, Translate)]; + {lang, _, _} -> erlang:error({badarg, Opt}); + {pubsub, Val} -> [encode_pubsub(Val, Translate)]; + {pubsub, _, _} -> erlang:error({badarg, Opt}); + {maxusers, Val} -> + [encode_maxusers(Val, default, Translate)]; + {maxusers, Val, Opts} -> + [encode_maxusers(Val, Opts, Translate)]; + {membersonly, Val} -> + [encode_membersonly(Val, Translate)]; + {membersonly, _, _} -> erlang:error({badarg, Opt}); + {moderatedroom, Val} -> + [encode_moderatedroom(Val, Translate)]; + {moderatedroom, _, _} -> erlang:error({badarg, Opt}); + {members_by_default, Val} -> + [encode_members_by_default(Val, Translate)]; + {members_by_default, _, _} -> + erlang:error({badarg, Opt}); + {passwordprotectedroom, Val} -> + [encode_passwordprotectedroom(Val, Translate)]; + {passwordprotectedroom, _, _} -> + erlang:error({badarg, Opt}); + {persistentroom, Val} -> + [encode_persistentroom(Val, Translate)]; + {persistentroom, _, _} -> erlang:error({badarg, Opt}); + {presencebroadcast, Val} -> + [encode_presencebroadcast(Val, default, Translate)]; + {presencebroadcast, Val, Opts} -> + [encode_presencebroadcast(Val, Opts, Translate)]; + {publicroom, Val} -> + [encode_publicroom(Val, Translate)]; + {publicroom, _, _} -> erlang:error({badarg, Opt}); + {public_list, Val} -> + [encode_public_list(Val, Translate)]; + {public_list, _, _} -> erlang:error({badarg, Opt}); + {roomadmins, Val} -> + [encode_roomadmins(Val, Translate)]; + {roomadmins, _, _} -> erlang:error({badarg, Opt}); + {roomdesc, Val} -> [encode_roomdesc(Val, Translate)]; + {roomdesc, _, _} -> erlang:error({badarg, Opt}); + {roomname, Val} -> [encode_roomname(Val, Translate)]; + {roomname, _, _} -> erlang:error({badarg, Opt}); + {roomowners, Val} -> + [encode_roomowners(Val, Translate)]; + {roomowners, _, _} -> erlang:error({badarg, Opt}); + {roomsecret, Val} -> + [encode_roomsecret(Val, Translate)]; + {roomsecret, _, _} -> erlang:error({badarg, Opt}); + {whois, Val} -> [encode_whois(Val, default, Translate)]; + {whois, Val, Opts} -> + [encode_whois(Val, Opts, Translate)]; + {mam, Val} -> [encode_mam(Val, Translate)]; + {mam, _, _} -> erlang:error({badarg, Opt}); + #xdata_field{} -> [Opt]; + _ -> [] + end + || Opt <- List], + FormType = #xdata_field{var = <<"FORM_TYPE">>, + type = hidden, + values = + [<<"http://jabber.org/protocol/muc#roomconfig">>]}, + [FormType | lists:flatten(Fs)]. + +decode([#xdata_field{var = <<"muc#maxhistoryfetch">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value 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#roomconfig">>}}) + 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#roomconfig">>}}); +decode([#xdata_field{var = <<"muc#roomconfig_allowpm">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{allowpm, Result} | Acc], + lists:delete(<<"muc#roomconfig_allowpm">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_allowpm">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"muc#roomconfig_allowpm">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_allowpm">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"muc#roomconfig_allowpm">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_allowpm">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"allow_private_messages">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_private_messages, Result} | Acc], + lists:delete(<<"allow_private_messages">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_private_messages">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_private_messages">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"allow_private_messages">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"allow_private_messages">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_private_messages">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"allow_private_messages_from_visitors">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [nobody, moderators, anyone]) of + Result -> + decode(Fs, + [{allow_private_messages_from_visitors, Result} | Acc], + lists:delete(<<"allow_private_messages_from_visitors">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, + <<"allow_private_messages_from_visitors">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"allow_private_messages_from_visitors">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"allow_private_messages_from_visitors">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"allow_private_messages_from_visitors">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, + <<"allow_private_messages_from_visitors">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"allow_visitor_status">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_visitor_status, Result} | Acc], + lists:delete(<<"allow_visitor_status">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_visitor_status">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_visitor_status">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"allow_visitor_status">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"allow_visitor_status">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_visitor_status">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"allow_visitor_nickchange">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_visitor_nickchange, Result} | Acc], + lists:delete(<<"allow_visitor_nickchange">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_visitor_nickchange">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"allow_visitor_nickchange">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"allow_visitor_nickchange">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"allow_visitor_nickchange">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_visitor_nickchange">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"allow_voice_requests">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_voice_requests, Result} | Acc], + lists:delete(<<"allow_voice_requests">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_voice_requests">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_voice_requests">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"allow_voice_requests">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"allow_voice_requests">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_voice_requests">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"allow_subscription">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_subscription, Result} | Acc], + lists:delete(<<"allow_subscription">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_subscription">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_subscription">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"allow_subscription">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"allow_subscription">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_subscription">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"voice_request_min_interval">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_int(Value, 0, infinity) of + Result -> + decode(Fs, [{voice_request_min_interval, Result} | Acc], + lists:delete(<<"voice_request_min_interval">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"voice_request_min_interval">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"voice_request_min_interval">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"voice_request_min_interval">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"voice_request_min_interval">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"voice_request_min_interval">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"captcha_protected">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{captcha_protected, Result} | Acc], + lists:delete(<<"captcha_protected">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"captcha_protected">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"captcha_protected">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"captcha_protected">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"captcha_protected">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"captcha_protected">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"captcha_whitelist">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_jid(Value) || Value <- Values] of + Result -> + decode(Fs, [{captcha_whitelist, Result} | Acc], + lists:delete(<<"captcha_whitelist">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"captcha_whitelist">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_query_users">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allow_query_users, Result} | Acc], + lists:delete(<<"allow_query_users">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"allow_query_users">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"allow_query_users">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"allow_query_users">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"allow_query_users">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"allow_query_users">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_allowinvites">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{allowinvites, Result} | Acc], + lists:delete(<<"muc#roomconfig_allowinvites">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_allowinvites">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_allowinvites">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_allowinvites">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_allowinvites">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_allowinvites">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_changesubject">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{changesubject, Result} | Acc], + lists:delete(<<"muc#roomconfig_changesubject">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_changesubject">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_changesubject">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_changesubject">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_changesubject">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_changesubject">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_enablelogging">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{enablelogging, Result} | Acc], + lists:delete(<<"muc#roomconfig_enablelogging">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_enablelogging">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_enablelogging">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_enablelogging">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_enablelogging">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_enablelogging">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_getmemberlist">>, + values = Values} + | Fs], + Acc, Required) -> + try [Value || Value <- Values] of + Result -> + decode(Fs, [{getmemberlist, Result} | Acc], + lists:delete(<<"muc#roomconfig_getmemberlist">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_getmemberlist">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"muc#roomconfig_lang">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{lang, Result} | Acc], + lists:delete(<<"muc#roomconfig_lang">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_lang">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"muc#roomconfig_lang">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"muc#roomconfig_lang">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"muc#roomconfig_lang">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_lang">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"muc#roomconfig_pubsub">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{pubsub, Result} | Acc], + lists:delete(<<"muc#roomconfig_pubsub">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_pubsub">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"muc#roomconfig_pubsub">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"muc#roomconfig_pubsub">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"muc#roomconfig_pubsub">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_pubsub">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_maxusers">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum_int(Value, [none], 0, infinity) of + Result -> + decode(Fs, [{maxusers, Result} | Acc], + lists:delete(<<"muc#roomconfig_maxusers">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_maxusers">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_maxusers">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_maxusers">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_maxusers">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_maxusers">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_membersonly">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{membersonly, Result} | Acc], + lists:delete(<<"muc#roomconfig_membersonly">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_membersonly">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_membersonly">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_membersonly">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_membersonly">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_membersonly">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_moderatedroom">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{moderatedroom, Result} | Acc], + lists:delete(<<"muc#roomconfig_moderatedroom">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_moderatedroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_moderatedroom">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_moderatedroom">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_moderatedroom">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_moderatedroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"members_by_default">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{members_by_default, Result} | Acc], + lists:delete(<<"members_by_default">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"members_by_default">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"members_by_default">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"members_by_default">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"members_by_default">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"members_by_default">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_passwordprotectedroom">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{passwordprotectedroom, Result} | Acc], + lists:delete(<<"muc#roomconfig_passwordprotectedroom">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, + <<"muc#roomconfig_passwordprotectedroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_passwordprotectedroom">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_passwordprotectedroom">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_passwordprotectedroom">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, + <<"muc#roomconfig_passwordprotectedroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_persistentroom">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{persistentroom, Result} | Acc], + lists:delete(<<"muc#roomconfig_persistentroom">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_persistentroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_persistentroom">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_persistentroom">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_persistentroom">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_persistentroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_presencebroadcast">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_enum(Value, [moderator, participant, visitor]) + || Value <- Values] + of + Result -> + decode(Fs, [{presencebroadcast, Result} | Acc], + lists:delete(<<"muc#roomconfig_presencebroadcast">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_presencebroadcast">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_publicroom">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{publicroom, Result} | Acc], + lists:delete(<<"muc#roomconfig_publicroom">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_publicroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_publicroom">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_publicroom">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_publicroom">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_publicroom">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"public_list">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{public_list, Result} | Acc], + lists:delete(<<"public_list">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"public_list">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"public_list">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"public_list">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"public_list">>} | _], _, + _) -> + erlang:error({?MODULE, + {too_many_values, <<"public_list">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_roomadmins">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_jid(Value) || Value <- Values] of + Result -> + decode(Fs, [{roomadmins, Result} | Acc], + lists:delete(<<"muc#roomconfig_roomadmins">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_roomadmins">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_roomdesc">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{roomdesc, Result} | Acc], + lists:delete(<<"muc#roomconfig_roomdesc">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_roomdesc">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_roomdesc">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_roomdesc">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_roomdesc">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_roomdesc">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_roomname">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{roomname, Result} | Acc], + lists:delete(<<"muc#roomconfig_roomname">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_roomname">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_roomname">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_roomname">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_roomname">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_roomname">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = + <<"muc#roomconfig_roomowners">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_jid(Value) || Value <- Values] of + Result -> + decode(Fs, [{roomowners, Result} | Acc], + lists:delete(<<"muc#roomconfig_roomowners">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_roomowners">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_roomsecret">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{roomsecret, Result} | Acc], + lists:delete(<<"muc#roomconfig_roomsecret">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_roomsecret">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = + <<"muc#roomconfig_roomsecret">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"muc#roomconfig_roomsecret">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"muc#roomconfig_roomsecret">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_roomsecret">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"muc#roomconfig_whois">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [moderators, anyone]) of + Result -> + decode(Fs, [{whois, Result} | Acc], + lists:delete(<<"muc#roomconfig_whois">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"muc#roomconfig_whois">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"muc#roomconfig_whois">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"muc#roomconfig_whois">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"muc#roomconfig_whois">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"muc#roomconfig_whois">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = <<"mam">>, values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{mam, Result} | Acc], + lists:delete(<<"mam">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"mam">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}) + end; +decode([#xdata_field{var = <<"mam">>, values = []} = F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"mam">>, values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"mam">>} | _], _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"mam">>, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([#xdata_field{var = Var} | Fs], Acc, Required) -> + if Var /= <<"FORM_TYPE">> -> + erlang:error({?MODULE, + {unknown_var, Var, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); + true -> decode(Fs, Acc, Required) + end; +decode([], _, [Var | _]) -> + erlang:error({?MODULE, + {missing_required_var, Var, + <<"http://jabber.org/protocol/muc#roomconfig">>}}); +decode([], Acc, []) -> Acc. + +encode_maxhistoryfetch(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [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_allowpm(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 = <<"muc#roomconfig_allowpm">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Roles that May Send Private Messages">>)}. + +encode_allow_private_messages(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_private_messages">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow users to send private messages">>)}. + +encode_allow_private_messages_from_visitors(Value, + Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = Translate(<<"nobody">>), + value = <<"nobody">>}, + #xdata_option{label = Translate(<<"moderators only">>), + value = <<"moderators">>}, + #xdata_option{label = Translate(<<"anyone">>), + value = <<"anyone">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = + <<"allow_private_messages_from_visitors">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Allow visitors to send private messages to">>)}. + +encode_allow_visitor_status(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_visitor_status">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow visitors to send status text in " + "presence updates">>)}. + +encode_allow_visitor_nickchange(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_visitor_nickchange">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow visitors to change nickname">>)}. + +encode_allow_voice_requests(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_voice_requests">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow visitors to send voice requests">>)}. + +encode_allow_subscription(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_subscription">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Allow subscription">>)}. + +encode_voice_request_min_interval(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_int(Value)] + end, + Opts = [], + #xdata_field{var = <<"voice_request_min_interval">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Minimum interval between voice requests " + "(in seconds)">>)}. + +encode_captcha_protected(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"captcha_protected">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room CAPTCHA protected">>)}. + +encode_captcha_whitelist(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_jid(V) || V <- Value] + end, + Opts = [], + #xdata_field{var = <<"captcha_whitelist">>, + values = Values, required = false, type = 'jid-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"Exclude Jabber IDs from CAPTCHA challenge">>)}. + +encode_allow_query_users(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"allow_query_users">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow users to query other users">>)}. + +encode_allowinvites(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_allowinvites">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Allow users to send invites">>)}. + +encode_changesubject(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_changesubject">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Allow users to change the subject">>)}. + +encode_enablelogging(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_enablelogging">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Enable logging">>)}. + +encode_getmemberlist(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 = <<"muc#roomconfig_getmemberlist">>, + values = Values, required = false, type = 'list-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"Roles and Affiliations that May Retrieve " + "Member List">>)}. + +encode_lang(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_lang">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Natural Language for Room Discussions">>)}. + +encode_pubsub(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_pubsub">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"XMPP URI of Associated Publish-Subscribe " + "Node">>)}. + +encode_maxusers(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum_int(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = Translate(<<"No limit">>), + value = <<"none">>}, + #xdata_option{value = <<"5">>}, + #xdata_option{value = <<"10">>}, + #xdata_option{value = <<"20">>}, + #xdata_option{value = <<"30">>}, + #xdata_option{value = <<"50">>}, + #xdata_option{value = <<"100">>}, + #xdata_option{value = <<"200">>}, + #xdata_option{value = <<"500">>}, + #xdata_option{value = <<"1000">>}, + #xdata_option{value = <<"2000">>}, + #xdata_option{value = <<"5000">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum_int(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"muc#roomconfig_maxusers">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = Translate(<<"Maximum Number of Occupants">>)}. + +encode_membersonly(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_membersonly">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room members-only">>)}. + +encode_moderatedroom(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_moderatedroom">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room moderated">>)}. + +encode_members_by_default(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"members_by_default">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Default users as participants">>)}. + +encode_passwordprotectedroom(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = + <<"muc#roomconfig_passwordprotectedroom">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room password protected">>)}. + +encode_persistentroom(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_persistentroom">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room persistent">>)}. + +encode_presencebroadcast(Value, Options, Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_enum(V) || V <- Value] + end, + Opts = if Options == default -> + [#xdata_option{label = Translate(<<"Moderator">>), + value = <<"moderator">>}, + #xdata_option{label = Translate(<<"Participant">>), + value = <<"participant">>}, + #xdata_option{label = Translate(<<"Visitor">>), + value = <<"visitor">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = + <<"muc#roomconfig_presencebroadcast">>, + values = Values, required = false, type = 'list-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"Roles for which Presence is Broadcasted">>)}. + +encode_publicroom(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_publicroom">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Make room public searchable">>)}. + +encode_public_list(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"public_list">>, values = Values, + required = false, type = boolean, options = Opts, + desc = <<>>, + label = Translate(<<"Make participants list public">>)}. + +encode_roomadmins(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_jid(V) || V <- Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_roomadmins">>, + values = Values, required = false, type = 'jid-multi', + options = Opts, desc = <<>>, + label = Translate(<<"Full List of Room Admins">>)}. + +encode_roomdesc(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_roomdesc">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = Translate(<<"Room description">>)}. + +encode_roomname(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_roomname">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = Translate(<<"Room title">>)}. + +encode_roomowners(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_jid(V) || V <- Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_roomowners">>, + values = Values, required = false, type = 'jid-multi', + options = Opts, desc = <<>>, + label = Translate(<<"Full List of Room Owners">>)}. + +encode_roomsecret(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"muc#roomconfig_roomsecret">>, + values = Values, required = false, + type = 'text-private', options = Opts, desc = <<>>, + label = Translate(<<"Password">>)}. + +encode_whois(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = Translate(<<"moderators only">>), + value = <<"moderators">>}, + #xdata_option{label = Translate(<<"anyone">>), + value = <<"anyone">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"muc#roomconfig_whois">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = Translate(<<"Present real Jabber IDs to">>)}. + +encode_mam(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"mam">>, values = Values, + required = false, type = boolean, options = Opts, + desc = <<>>, + label = Translate(<<"Enable message archiving">>)}. diff --git a/src/muc_roominfo.erl b/src/muc_roominfo.erl new file mode 100644 index 000000000..809dcef5b --- /dev/null +++ b/src/muc_roominfo.erl @@ -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">>)}. diff --git a/src/node_flat.erl b/src/node_flat.erl index e3170a263..3afa49f22 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -84,6 +84,7 @@ options() -> {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, + {title, <<>>}, {presence_based_delivery, false}, {itemreply, none}]. @@ -452,7 +453,7 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) -> end, {error, xmpp:err_item_not_found()}, States); _ -> - {error, xmpp:err_item_not_found()} + {error, xmpp:err_forbidden()} end end end. diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index bd084333b..c1dfd0e81 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -901,7 +901,7 @@ first_in_list(Pred, [H | T]) -> itemids(Nidx, {_U, _S, _R} = JID) -> SJID = encode_jid(JID), - SJIDLike = <<(ejabberd_sql:escape(encode_jid_like(JID)))/binary, "/%">>, + SJIDLike = <<(encode_jid_like(JID))/binary, "/%">>, case catch ejabberd_sql:sql_query_t( ?SQL("select @(itemid)s from pubsub_item where " diff --git a/src/pubsub_get_pending.erl b/src/pubsub_get_pending.erl new file mode 100644 index 000000000..1a7de6a2d --- /dev/null +++ b/src/pubsub_get_pending.erl @@ -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">>)}. diff --git a/src/pubsub_node_config.erl b/src/pubsub_node_config.erl new file mode 100644 index 000000000..47ed10b49 --- /dev/null +++ b/src/pubsub_node_config.erl @@ -0,0 +1,1666 @@ +%% 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 + +-module(pubsub_node_config). + +-export([decode/1, decode/2, encode/1, encode/2, + format_error/1]). + +-include("xmpp_codec.hrl"). + +-include("pubsub_node_config.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_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, []); + #xdata_field{values = + [<<"http://jabber.org/protocol/pubsub#node_config">>]} -> + decode(Fs, Acc, []); + _ -> + erlang:error({?MODULE, + {form_type_mismatch, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + 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)]; + {body_xslt, Val} -> [encode_body_xslt(Val, Translate)]; + {body_xslt, _, _} -> erlang:error({badarg, Opt}); + {children_association_policy, Val} -> + [encode_children_association_policy(Val, default, + Translate)]; + {children_association_policy, Val, Opts} -> + [encode_children_association_policy(Val, Opts, + Translate)]; + {children_association_whitelist, Val} -> + [encode_children_association_whitelist(Val, Translate)]; + {children_association_whitelist, _, _} -> + erlang:error({badarg, Opt}); + {children, Val} -> [encode_children(Val, Translate)]; + {children, _, _} -> erlang:error({badarg, Opt}); + {children_max, Val} -> + [encode_children_max(Val, Translate)]; + {children_max, _, _} -> erlang:error({badarg, Opt}); + {collection, Val} -> + [encode_collection(Val, Translate)]; + {collection, _, _} -> erlang:error({badarg, Opt}); + {contact, Val} -> [encode_contact(Val, Translate)]; + {contact, _, _} -> erlang:error({badarg, Opt}); + {dataform_xslt, Val} -> + [encode_dataform_xslt(Val, Translate)]; + {dataform_xslt, _, _} -> erlang:error({badarg, Opt}); + {deliver_notifications, Val} -> + [encode_deliver_notifications(Val, Translate)]; + {deliver_notifications, _, _} -> + erlang:error({badarg, Opt}); + {deliver_payloads, Val} -> + [encode_deliver_payloads(Val, Translate)]; + {deliver_payloads, _, _} -> erlang:error({badarg, Opt}); + {description, Val} -> + [encode_description(Val, Translate)]; + {description, _, _} -> erlang:error({badarg, Opt}); + {item_expire, Val} -> + [encode_item_expire(Val, Translate)]; + {item_expire, _, _} -> erlang:error({badarg, Opt}); + {itemreply, Val} -> + [encode_itemreply(Val, default, Translate)]; + {itemreply, Val, Opts} -> + [encode_itemreply(Val, Opts, Translate)]; + {language, Val} -> + [encode_language(Val, default, Translate)]; + {language, Val, Opts} -> + [encode_language(Val, Opts, Translate)]; + {max_items, Val} -> [encode_max_items(Val, Translate)]; + {max_items, _, _} -> erlang:error({badarg, Opt}); + {max_payload_size, Val} -> + [encode_max_payload_size(Val, Translate)]; + {max_payload_size, _, _} -> erlang:error({badarg, Opt}); + {node_type, Val} -> + [encode_node_type(Val, default, Translate)]; + {node_type, Val, Opts} -> + [encode_node_type(Val, Opts, Translate)]; + {notification_type, Val} -> + [encode_notification_type(Val, default, Translate)]; + {notification_type, Val, Opts} -> + [encode_notification_type(Val, Opts, Translate)]; + {notify_config, Val} -> + [encode_notify_config(Val, Translate)]; + {notify_config, _, _} -> erlang:error({badarg, Opt}); + {notify_delete, Val} -> + [encode_notify_delete(Val, Translate)]; + {notify_delete, _, _} -> erlang:error({badarg, Opt}); + {notify_retract, Val} -> + [encode_notify_retract(Val, Translate)]; + {notify_retract, _, _} -> erlang:error({badarg, Opt}); + {notify_sub, Val} -> + [encode_notify_sub(Val, Translate)]; + {notify_sub, _, _} -> erlang:error({badarg, Opt}); + {persist_items, Val} -> + [encode_persist_items(Val, Translate)]; + {persist_items, _, _} -> erlang:error({badarg, Opt}); + {presence_based_delivery, Val} -> + [encode_presence_based_delivery(Val, Translate)]; + {presence_based_delivery, _, _} -> + erlang:error({badarg, Opt}); + {publish_model, Val} -> + [encode_publish_model(Val, default, Translate)]; + {publish_model, Val, Opts} -> + [encode_publish_model(Val, Opts, Translate)]; + {purge_offline, Val} -> + [encode_purge_offline(Val, Translate)]; + {purge_offline, _, _} -> erlang:error({badarg, Opt}); + {roster_groups_allowed, Val} -> + [encode_roster_groups_allowed(Val, default, Translate)]; + {roster_groups_allowed, Val, Opts} -> + [encode_roster_groups_allowed(Val, Opts, Translate)]; + {send_last_published_item, Val} -> + [encode_send_last_published_item(Val, default, + Translate)]; + {send_last_published_item, Val, Opts} -> + [encode_send_last_published_item(Val, Opts, Translate)]; + {tempsub, Val} -> [encode_tempsub(Val, Translate)]; + {tempsub, _, _} -> erlang:error({badarg, Opt}); + {subscribe, Val} -> [encode_subscribe(Val, Translate)]; + {subscribe, _, _} -> erlang:error({badarg, Opt}); + {title, Val} -> [encode_title(Val, Translate)]; + {title, _, _} -> erlang:error({badarg, Opt}); + {type, Val} -> [encode_type(Val, Translate)]; + {type, _, _} -> erlang:error({badarg, Opt}); + #xdata_field{} -> [Opt]; + _ -> [] + end + || Opt <- List], + FormType = #xdata_field{var = <<"FORM_TYPE">>, + type = hidden, + values = + [<<"http://jabber.org/protocol/pubsub#node_config">>]}, + [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#node_config">>}}) + 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#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#body_xslt">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{body_xslt, Result} | Acc], + lists:delete(<<"pubsub#body_xslt">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#body_xslt">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#body_xslt">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#body_xslt">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#body_xslt">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#body_xslt">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#children_association_policy">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [all, owners, whitelist]) of + Result -> + decode(Fs, + [{children_association_policy, Result} | Acc], + lists:delete(<<"pubsub#children_association_policy">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, + <<"pubsub#children_association_policy">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#children_association_policy">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#children_association_policy">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#children_association_policy">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, + <<"pubsub#children_association_policy">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#children_association_whitelist">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_jid(Value) || Value <- Values] of + Result -> + decode(Fs, + [{children_association_whitelist, Result} | Acc], + lists:delete(<<"pubsub#children_association_whitelist">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, + <<"pubsub#children_association_whitelist">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#children">>, + values = Values} + | Fs], + Acc, Required) -> + try [Value || Value <- Values] of + Result -> + decode(Fs, [{children, Result} | Acc], + lists:delete(<<"pubsub#children">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#children">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#children_max">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{children_max, Result} | Acc], + lists:delete(<<"pubsub#children_max">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#children_max">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#children_max">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#children_max">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#children_max">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#children_max">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#collection">>, + values = Values} + | Fs], + Acc, Required) -> + try [Value || Value <- Values] of + Result -> + decode(Fs, [{collection, Result} | Acc], + lists:delete(<<"pubsub#collection">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#collection">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#contact">>, + values = Values} + | Fs], + Acc, Required) -> + try [dec_jid(Value) || Value <- Values] of + Result -> + decode(Fs, [{contact, Result} | Acc], + lists:delete(<<"pubsub#contact">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#contact">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#dataform_xslt">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{dataform_xslt, Result} | Acc], + lists:delete(<<"pubsub#dataform_xslt">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#dataform_xslt">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#dataform_xslt">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#dataform_xslt">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#dataform_xslt">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#dataform_xslt">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#deliver_notifications">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{deliver_notifications, Result} | Acc], + lists:delete(<<"pubsub#deliver_notifications">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#deliver_notifications">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#deliver_notifications">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#deliver_notifications">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#deliver_notifications">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#deliver_notifications">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#deliver_payloads">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{deliver_payloads, Result} | Acc], + lists:delete(<<"pubsub#deliver_payloads">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#deliver_payloads">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#deliver_payloads">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#deliver_payloads">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#deliver_payloads">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#deliver_payloads">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#description">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{description, Result} | Acc], + lists:delete(<<"pubsub#description">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#description">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#description">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#description">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#description">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#description">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#item_expire">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{item_expire, Result} | Acc], + lists:delete(<<"pubsub#item_expire">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#item_expire">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#item_expire">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#item_expire">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#item_expire">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#item_expire">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#itemreply">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [owner, publisher, none]) of + Result -> + decode(Fs, [{itemreply, Result} | Acc], + lists:delete(<<"pubsub#itemreply">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#itemreply">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#itemreply">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#itemreply">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#itemreply">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#itemreply">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#language">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{language, Result} | Acc], + lists:delete(<<"pubsub#language">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#language">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#language">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#language">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#language">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#language">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#max_items">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_int(Value, 0, infinity) of + Result -> + decode(Fs, [{max_items, Result} | Acc], + lists:delete(<<"pubsub#max_items">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#max_items">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#max_items">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#max_items">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#max_items">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#max_items">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#max_payload_size">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_int(Value, 0, infinity) of + Result -> + decode(Fs, [{max_payload_size, Result} | Acc], + lists:delete(<<"pubsub#max_payload_size">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#max_payload_size">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#max_payload_size">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#max_payload_size">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#max_payload_size">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#max_payload_size">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#node_type">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [leaf, collection]) of + Result -> + decode(Fs, [{node_type, Result} | Acc], + lists:delete(<<"pubsub#node_type">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#node_type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#node_type">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#node_type">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#node_type">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#node_type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#notification_type">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [normal, headline]) of + Result -> + decode(Fs, [{notification_type, Result} | Acc], + lists:delete(<<"pubsub#notification_type">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#notification_type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#notification_type">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#notification_type">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#notification_type">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#notification_type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#notify_config">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{notify_config, Result} | Acc], + lists:delete(<<"pubsub#notify_config">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#notify_config">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#notify_config">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#notify_config">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#notify_config">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#notify_config">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#notify_delete">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{notify_delete, Result} | Acc], + lists:delete(<<"pubsub#notify_delete">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#notify_delete">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#notify_delete">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#notify_delete">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#notify_delete">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#notify_delete">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#notify_retract">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{notify_retract, Result} | Acc], + lists:delete(<<"pubsub#notify_retract">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#notify_retract">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#notify_retract">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#notify_retract">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#notify_retract">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#notify_retract">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#notify_sub">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{notify_sub, Result} | Acc], + lists:delete(<<"pubsub#notify_sub">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#notify_sub">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#notify_sub">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#notify_sub">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#notify_sub">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#notify_sub">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#persist_items">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{persist_items, Result} | Acc], + lists:delete(<<"pubsub#persist_items">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#persist_items">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#persist_items">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#persist_items">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#persist_items">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#persist_items">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#presence_based_delivery">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{presence_based_delivery, Result} | Acc], + lists:delete(<<"pubsub#presence_based_delivery">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#presence_based_delivery">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#presence_based_delivery">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#presence_based_delivery">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#presence_based_delivery">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#presence_based_delivery">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#publish_model">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, [publishers, subscribers, open]) of + Result -> + decode(Fs, [{publish_model, Result} | Acc], + lists:delete(<<"pubsub#publish_model">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#publish_model">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#publish_model">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#publish_model">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#publish_model">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#publish_model">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#purge_offline">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{purge_offline, Result} | Acc], + lists:delete(<<"pubsub#purge_offline">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#purge_offline">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#purge_offline">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#purge_offline">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#purge_offline">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#purge_offline">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = + <<"pubsub#roster_groups_allowed">>, + values = Values} + | Fs], + Acc, Required) -> + try [Value || Value <- Values] of + Result -> + decode(Fs, [{roster_groups_allowed, Result} | Acc], + lists:delete(<<"pubsub#roster_groups_allowed">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#roster_groups_allowed">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#send_last_published_item">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_enum(Value, + [never, on_sub, on_sub_and_presence]) + of + Result -> + decode(Fs, [{send_last_published_item, Result} | Acc], + lists:delete(<<"pubsub#send_last_published_item">>, + Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#send_last_published_item">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = + <<"pubsub#send_last_published_item">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = + <<"pubsub#send_last_published_item">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = + <<"pubsub#send_last_published_item">>} + | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#send_last_published_item">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#tempsub">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{tempsub, Result} | Acc], + lists:delete(<<"pubsub#tempsub">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#tempsub">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#tempsub">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#tempsub">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#tempsub">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#tempsub">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#subscribe">>, + values = [Value]} + | Fs], + Acc, Required) -> + try dec_bool(Value) of + Result -> + decode(Fs, [{subscribe, Result} | Acc], + lists:delete(<<"pubsub#subscribe">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#subscribe">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#subscribe">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#subscribe">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#subscribe">>} | _], + _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#subscribe">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#title">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{title, Result} | Acc], + lists:delete(<<"pubsub#title">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#title">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#title">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#title">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#title">>} | _], _, + _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#title">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = <<"pubsub#type">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{type, Result} | Acc], + lists:delete(<<"pubsub#type">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"pubsub#type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}) + end; +decode([#xdata_field{var = <<"pubsub#type">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"pubsub#type">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"pubsub#type">>} | _], _, + _) -> + erlang:error({?MODULE, + {too_many_values, <<"pubsub#type">>, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +decode([#xdata_field{var = Var} | Fs], Acc, Required) -> + if Var /= <<"FORM_TYPE">> -> + erlang:error({?MODULE, + {unknown_var, Var, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); + true -> decode(Fs, Acc, Required) + end; +decode([], _, [Var | _]) -> + erlang:error({?MODULE, + {missing_required_var, Var, + <<"http://jabber.org/protocol/pubsub#node_config">>}}); +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(<<"Subscription requests must be approved " + "and only subscribers may retrieve items">>), + value = <<"authorize">>}, + #xdata_option{label = + Translate(<<"Anyone may subscribe and retrieve items">>), + value = <<"open">>}, + #xdata_option{label = + Translate(<<"Anyone with a presence subscription " + "of both or from may subscribe and retrieve " + "items">>), + value = <<"presence">>}, + #xdata_option{label = + Translate(<<"Anyone in the specified roster group(s) " + "may subscribe and retrieve items">>), + value = <<"roster">>}, + #xdata_option{label = + Translate(<<"Only those on a whitelist may subscribe " + "and retrieve items">>), + 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">>)}. + +encode_body_xslt(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#body_xslt">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"The URL of an XSL transformation which " + "can be applied to payloads in order " + "to generate an appropriate message body " + "element.">>)}. + +encode_children_association_policy(Value, Options, + Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = + Translate(<<"Anyone may associate leaf nodes with " + "the collection">>), + value = <<"all">>}, + #xdata_option{label = + Translate(<<"Only collection node owners may associate " + "leaf nodes with the collection">>), + value = <<"owners">>}, + #xdata_option{label = + Translate(<<"Only those on a whitelist may associate " + "leaf nodes with the collection">>), + value = <<"whitelist">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = + <<"pubsub#children_association_policy">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Who may associate leaf nodes with a " + "collection">>)}. + +encode_children_association_whitelist(Value, + Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_jid(V) || V <- Value] + end, + Opts = [], + #xdata_field{var = + <<"pubsub#children_association_whitelist">>, + values = Values, required = false, type = 'jid-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"The list of JIDs that may associate " + "leaf nodes with a collection">>)}. + +encode_children(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#children">>, + values = Values, required = false, type = 'text-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"The child nodes (leaf or collection) " + "associated with a collection">>)}. + +encode_children_max(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#children_max">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"The maximum number of child nodes that " + "can be associated with a collection">>)}. + +encode_collection(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#collection">>, + values = Values, required = false, type = 'text-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"The collections with which a node is " + "affiliated">>)}. + +encode_contact(Value, Translate) -> + Values = case Value of + [] -> []; + Value -> [enc_jid(V) || V <- Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#contact">>, + values = Values, required = false, type = 'jid-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"The JIDs of those to contact with questions">>)}. + +encode_dataform_xslt(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#dataform_xslt">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"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">>)}. + +encode_deliver_notifications(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#deliver_notifications">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Deliver event notifications">>)}. + +encode_deliver_payloads(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#deliver_payloads">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Deliver payloads with event notifications">>)}. + +encode_description(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#description">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = Translate(<<"A description of the node">>)}. + +encode_item_expire(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#item_expire">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Number of seconds after which to automaticall" + "y purge items">>)}. + +encode_itemreply(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = + Translate(<<"Statically specify a replyto of the " + "node owner(s)">>), + value = <<"owner">>}, + #xdata_option{label = + Translate(<<"Dynamically specify a replyto of the " + "item publisher">>), + value = <<"publisher">>}, + #xdata_option{value = <<"none">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"pubsub#itemreply">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Whether owners or publisher should receive " + "replies to items">>)}. + +encode_language(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#language">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"The default language of the node">>)}. + +encode_max_items(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_int(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#max_items">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = Translate(<<"Max # of items to persist">>)}. + +encode_max_payload_size(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_int(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#max_payload_size">>, + values = Values, required = false, type = 'text-single', + options = Opts, desc = <<>>, + label = Translate(<<"Max payload size in bytes">>)}. + +encode_node_type(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = + Translate(<<"The node is a leaf node (default)">>), + value = <<"leaf">>}, + #xdata_option{label = + Translate(<<"The node is a collection node">>), + value = <<"collection">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"pubsub#node_type">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Whether the node is a leaf (default) " + "or a collection">>)}. + +encode_notification_type(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = + Translate(<<"Messages of type normal">>), + value = <<"normal">>}, + #xdata_option{label = + Translate(<<"Messages of type headline">>), + value = <<"headline">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"pubsub#notification_type">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"Specify the event message type">>)}. + +encode_notify_config(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#notify_config">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Notify subscribers when the node configuratio" + "n changes">>)}. + +encode_notify_delete(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#notify_delete">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Notify subscribers when the node is " + "deleted">>)}. + +encode_notify_retract(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#notify_retract">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Notify subscribers when items are removed " + "from the node">>)}. + +encode_notify_sub(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#notify_sub">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Whether to notify owners about new subscriber" + "s and unsubscribes">>)}. + +encode_persist_items(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#persist_items">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = Translate(<<"Persist items to storage">>)}. + +encode_presence_based_delivery(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#presence_based_delivery">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Only deliver notifications to available " + "users">>)}. + +encode_publish_model(Value, Options, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = + Translate(<<"Only publishers may publish">>), + value = <<"publishers">>}, + #xdata_option{label = + Translate(<<"Subscribers may publish">>), + value = <<"subscribers">>}, + #xdata_option{label = + Translate(<<"Anyone may publish">>), + value = <<"open">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = <<"pubsub#publish_model">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = Translate(<<"Specify the publisher model">>)}. + +encode_purge_offline(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#purge_offline">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Purge all items when the relevant publisher " + "goes offline">>)}. + +encode_roster_groups_allowed(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#roster_groups_allowed">>, + values = Values, required = false, type = 'list-multi', + options = Opts, desc = <<>>, + label = + Translate(<<"Roster groups allowed to subscribe">>)}. + +encode_send_last_published_item(Value, Options, + Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_enum(Value)] + end, + Opts = if Options == default -> + [#xdata_option{label = Translate(<<"Never">>), + value = <<"never">>}, + #xdata_option{label = + Translate(<<"When a new subscription is processed">>), + value = <<"on_sub">>}, + #xdata_option{label = + Translate(<<"When a new subscription is processed " + "and whenever a subscriber comes online">>), + value = <<"on_sub_and_presence">>}]; + true -> + [#xdata_option{label = Translate(L), + value = enc_enum(V)} + || {L, V} <- Options] + end, + #xdata_field{var = + <<"pubsub#send_last_published_item">>, + values = Values, required = false, type = 'list-single', + options = Opts, desc = <<>>, + label = + Translate(<<"When to send the last published item">>)}. + +encode_tempsub(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#tempsub">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Whether to make all subscriptions temporary, " + "based on subscriber presence">>)}. + +encode_subscribe(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_bool(Value)] + end, + Opts = [], + #xdata_field{var = <<"pubsub#subscribe">>, + values = Values, required = false, type = boolean, + options = Opts, desc = <<>>, + label = + Translate(<<"Whether to allow subscriptions">>)}. + +encode_title(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#title">>, values = Values, + required = false, type = 'text-single', options = Opts, + desc = <<>>, + label = Translate(<<"A friendly name for the node">>)}. + +encode_type(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"pubsub#type">>, values = Values, + required = false, type = 'text-single', options = Opts, + desc = <<>>, + label = + Translate(<<"The type of node data, usually specified " + "by the namespace of the payload (if " + "any)">>)}. diff --git a/src/pubsub_publish_options.erl b/src/pubsub_publish_options.erl new file mode 100644 index 000000000..8d0229071 --- /dev/null +++ b/src/pubsub_publish_options.erl @@ -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">>)}. diff --git a/src/pubsub_subscribe_authorization.erl b/src/pubsub_subscribe_authorization.erl new file mode 100644 index 000000000..e019ed6b9 --- /dev/null +++ b/src/pubsub_subscribe_authorization.erl @@ -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">>)}. diff --git a/src/pubsub_subscribe_options.erl b/src/pubsub_subscribe_options.erl new file mode 100644 index 000000000..446a84a00 --- /dev/null +++ b/src/pubsub_subscribe_options.erl @@ -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 = <<>>}. diff --git a/src/xdata_codec.erl b/src/xdata_codec.erl new file mode 100644 index 000000000..223f24a1b --- /dev/null +++ b/src/xdata_codec.erl @@ -0,0 +1,648 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 27 Sep 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-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. diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index f8f8b205f..f230dc489 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -6602,6 +6602,20 @@ pp(upload_slot, 3) -> [get, put, xmlns]; pp(thumbnail, 4) -> [uri, 'media-type', width, height]; 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}) -> <<(integer_to_binary(Maj))/binary, $., (integer_to_binary(Min))/binary>>. @@ -19203,10 +19217,7 @@ decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS, __TopXMLNS}}); decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS, _val) -> - case catch dec_enum(_val, - [member, none, outcast, owner, publisher, - 'publish-only']) - of + case catch dec_ps_aff(_val) of {'EXIT', _} -> erlang:error({xmpp_codec, {bad_attr_value, <<"affiliation">>, <<"affiliation">>, @@ -19216,7 +19227,7 @@ decode_pubsub_owner_affiliation_attr_affiliation(__TopXMLNS, encode_pubsub_owner_affiliation_attr_affiliation(_val, _acc) -> - [{<<"affiliation">>, enc_enum(_val)} | _acc]. + [{<<"affiliation">>, enc_ps_aff(_val)} | _acc]. decode_pubsub_affiliation(__TopXMLNS, __IgnoreEls, {xmlel, <<"affiliation">>, _attrs, _els}) -> @@ -19290,10 +19301,7 @@ decode_pubsub_affiliation_attr_affiliation(__TopXMLNS, __TopXMLNS}}); decode_pubsub_affiliation_attr_affiliation(__TopXMLNS, _val) -> - case catch dec_enum(_val, - [member, none, outcast, owner, publisher, - 'publish-only']) - of + case catch dec_ps_aff(_val) of {'EXIT', _} -> erlang:error({xmpp_codec, {bad_attr_value, <<"affiliation">>, <<"affiliation">>, @@ -19303,7 +19311,7 @@ decode_pubsub_affiliation_attr_affiliation(__TopXMLNS, encode_pubsub_affiliation_attr_affiliation(_val, _acc) -> - [{<<"affiliation">>, enc_enum(_val)} | _acc]. + [{<<"affiliation">>, enc_ps_aff(_val)} | _acc]. decode_pubsub_subscription(__TopXMLNS, __IgnoreEls, {xmlel, <<"subscription">>, _attrs, _els}) -> @@ -19826,7 +19834,7 @@ decode_xdata_field(__TopXMLNS, __IgnoreEls, {xmlel, <<"field">>, _attrs, _els}) -> {Options, Values, Desc, Required, __Els} = decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els, - [], [], undefined, false, []), + [], [], <<>>, false, []), {Label, Type, Var} = decode_xdata_field_attrs(__TopXMLNS, _attrs, undefined, undefined, undefined), @@ -20003,8 +20011,7 @@ encode_xdata_field({xdata_field, Label, Type, Var, [encode_xdata_field_value(Values, __TopXMLNS) | _acc]). -'encode_xdata_field_$desc'(undefined, __TopXMLNS, - _acc) -> +'encode_xdata_field_$desc'(<<>>, __TopXMLNS, _acc) -> _acc; 'encode_xdata_field_$desc'(Desc, __TopXMLNS, _acc) -> [encode_xdata_field_desc(Desc, __TopXMLNS) | _acc]. diff --git a/src/xmpp_util.erl b/src/xmpp_util.erl index 43178e86f..102d88412 100644 --- a/src/xmpp_util.erl +++ b/src/xmpp_util.erl @@ -11,7 +11,8 @@ %% API -export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1, 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"). @@ -95,11 +96,66 @@ make_adhoc_response(#adhoc_command{lang = Lang, node = Node, sid = SID}, -spec make_adhoc_response(adhoc_command()) -> adhoc_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}; make_adhoc_response(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 %%%=================================================================== +try_decode_timestamp(<>) -> + 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(<>) -> + try_decode_timestamp(<>). + +try_decode_fraction(<<$., T/binary>>) -> + {match, [V]} = re:run(T, <<"^[0-9]+">>, [{capture, [0], binary}]), + Size = size(V), + <> = 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. diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 81e35d52a..b4249bbdf 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -26,7 +26,7 @@ -include("suite.hrl"). suite() -> - [{timetrap, {seconds,30}}]. + [{timetrap, {seconds,60}}]. init_per_suite(Config) -> NewConfig = init_config(Config), @@ -325,6 +325,36 @@ no_db_tests() -> {replaced, [parallel], [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) -> %% No support for mod_pubsub [{single_user, [sequence], @@ -340,7 +370,7 @@ db_tests(riak) -> blocking, vcard, test_unregister]}, - {test_muc_register, [sequence], + {test_muc_register, [parallel], [muc_register_master, muc_register_slave]}, {test_roster_subscribe, [parallel], [roster_subscribe_master, @@ -372,9 +402,10 @@ db_tests(DB) when DB == mnesia; DB == redis -> privacy, blocking, vcard, - pubsub, + pubsub_single_tests(), test_unregister]}, - {test_muc_register, [sequence], + pubsub_multiple_tests(), + {test_muc_register, [parallel], [muc_register_master, muc_register_slave]}, {test_mix, [parallel], [mix_master, mix_slave]}, @@ -409,19 +440,20 @@ db_tests(_) -> [{single_user, [sequence], [test_register, legacy_auth_tests(), - auth_plain, - auth_md5, - presence_broadcast, - last, - roster_get, - roster_ver, - private, - privacy, - blocking, - vcard, - pubsub, - test_unregister]}, - {test_muc_register, [sequence], + auth_plain, + auth_md5, + presence_broadcast, + last, + roster_get, + roster_ver, + private, + privacy, + blocking, + vcard, + pubsub_single_tests(), + test_unregister]}, + pubsub_multiple_tests(), + {test_muc_register, [parallel], [muc_register_master, muc_register_slave]}, {test_mix, [parallel], [mix_master, mix_slave]}, @@ -512,17 +544,17 @@ groups() -> {riak, [sequence], db_tests(riak)}]. all() -> - [%%{group, ldap}, + [{group, ldap}, {group, no_db}, - %% {group, mnesia}, - %% {group, redis}, - %% {group, mysql}, - %% {group, pgsql}, - %% {group, sqlite}, - %% {group, extauth}, - %% {group, riak}, - %% {group, component}, - %% {group, s2s}, + {group, mnesia}, + {group, redis}, + {group, mysql}, + {group, pgsql}, + {group, sqlite}, + {group, extauth}, + {group, riak}, + {group, component}, + {group, s2s}, stop_ejabberd]. stop_ejabberd(Config) -> @@ -943,16 +975,22 @@ disco(Config) -> end, Items), disconnect(Config). -replaced_master(Config0) -> - Config = bind(Config0), - wait_for_slave(Config), - ?recv1(#stream_error{reason = conflict}), - ?recv1({xmlstreamend, <<"stream:stream">>}), - close_socket(Config). +%% replaced_master(Config0) -> +%% Config = bind(Config0), +%% wait_for_slave(Config), +%% ?recv1(#stream_error{reason = conflict}), +%% ?recv1({xmlstreamend, <<"stream:stream">>}), +%% close_socket(Config). + +%% replaced_slave(Config0) -> +%% wait_for_master(Config0), +%% Config = bind(Config0), +%% disconnect(Config). -replaced_slave(Config0) -> - wait_for_master(Config0), - Config = bind(Config0), +replaced_master(Config) -> + disconnect(Config). + +replaced_slave(Config) -> disconnect(Config). sm(Config) -> @@ -1226,78 +1264,663 @@ stats(Config) -> end, Stats), disconnect(Config). -pubsub(Config) -> - Features = get_features(Config, pubsub_jid(Config)), - true = lists:member(?NS_PUBSUB, Features), - %% Publish element within node "presence" - ItemID = randoms:get_string(), - Node = <<"presence!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>, - Item = #ps_item{id = ItemID, - xml_els = [xmpp:encode(#presence{})]}, - #iq{type = result, - sub_els = [#pubsub{publish = #ps_publish{ - node = Node, - items = [#ps_item{id = ItemID}]}}]} = - send_recv(Config, - #iq{type = set, to = pubsub_jid(Config), - sub_els = [#pubsub{publish = #ps_publish{ - node = Node, - items = [Item]}}]}), - %% Subscribe to node "presence" - I1 = send(Config, - #iq{type = set, to = pubsub_jid(Config), - sub_els = [#pubsub{subscribe = #ps_subscribe{ - node = Node, - jid = my_jid(Config)}}]}), - ?recv2( - #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)}}]}), +test_pubsub_features(Config) -> + PJID = pubsub_jid(Config), + AllFeatures = sets:from_list(get_features(Config, PJID)), + NeededFeatures = sets:from_list( + [?NS_PUBSUB, + ?PUBSUB("access-open"), + ?PUBSUB("access-authorize"), + ?PUBSUB("create-nodes"), + ?PUBSUB("instant-nodes"), + ?PUBSUB("config-node"), + ?PUBSUB("retrieve-default"), + ?PUBSUB("create-and-configure"), + ?PUBSUB("publish"), + ?PUBSUB("auto-create"), + ?PUBSUB("retrieve-items"), + ?PUBSUB("delete-items"), + ?PUBSUB("subscribe"), + ?PUBSUB("retrieve-affiliations"), + ?PUBSUB("modify-affiliations"), + ?PUBSUB("retrieve-subscriptions"), + ?PUBSUB("manage-subscriptions"), + ?PUBSUB("purge-nodes"), + ?PUBSUB("delete-nodes")]), + true = sets:is_subset(NeededFeatures, AllFeatures), + 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 = mix_jid(Config), Room = mix_room_jid(Config), @@ -1627,7 +2250,7 @@ muc_mam_master(Config) -> to = Room}), %% Find the MAM field in the config and enable it NewFields = lists:flatmap( - fun(#xdata_field{var = <<"muc#roomconfig_mam">> = Var}) -> + fun(#xdata_field{var = <<"mam">> = Var}) -> [#xdata_field{var = Var, values = [<<"1">>]}]; (_) -> [] @@ -1934,39 +2557,36 @@ muc_slave(Config) -> disconnect(Config). muc_register_nick(Config, MUC, PrevNick, Nick) -> - {Registered, PrevNickVals} = if PrevNick /= <<"">> -> - {true, [PrevNick]}; - true -> - {false, []} - end, + PrevRegistered = if PrevNick /= <<"">> -> true; + true -> false + end, + NewRegistered = if Nick /= <<"">> -> true; + true -> false + end, %% Request register form #iq{type = result, - sub_els = [#register{registered = Registered, + sub_els = [#register{registered = PrevRegistered, xdata = #xdata{type = form, fields = FsWithoutNick}}]} = send_recv(Config, #iq{type = get, to = MUC, sub_els = [#register{}]}), - %% Check if 'nick' field presents - #xdata_field{type = 'text-single', - var = <<"nick">>, - values = PrevNickVals} = - lists:keyfind(<<"nick">>, #xdata_field.var, FsWithoutNick), - X = #xdata{type = submit, - fields = [#xdata_field{var = <<"nick">>, values = [Nick]}]}, + %% Check if previous nick is registered + PrevNick = proplists:get_value( + roomnick, muc_register:decode(FsWithoutNick)), + X = #xdata{type = submit, fields = muc_register:encode([{roomnick, Nick}])}, %% Submitting form #iq{type = result, sub_els = []} = send_recv(Config, #iq{type = set, to = MUC, sub_els = [#register{xdata = X}]}), - %% Check if the nick was registered + %% Check if new nick was registered #iq{type = result, - sub_els = [#register{registered = true, + sub_els = [#register{registered = NewRegistered, xdata = #xdata{type = form, fields = FsWithNick}}]} = send_recv(Config, #iq{type = get, to = MUC, sub_els = [#register{}]}), - #xdata_field{type = 'text-single', var = <<"nick">>, - values = [Nick]} = - lists:keyfind(<<"nick">>, #xdata_field.var, FsWithNick). + Nick = proplists:get_value( + roomnick, muc_register:decode(FsWithNick)). muc_register_master(Config) -> MUC = muc_jid(Config), @@ -1980,17 +2600,23 @@ muc_register_master(Config) -> muc_register_nick(Config, MUC, <<"">>, <<"master2">>), %% Now register nick "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). muc_register_slave(Config) -> MUC = muc_jid(Config), + wait_for_master(Config), %% Trying to register occupied nick "master" - X = #xdata{type = submit, - fields = [#xdata_field{var = <<"nick">>, - values = [<<"master">>]}]}, + Fs = muc_register:encode([{roomnick, <<"master">>}]), + X = #xdata{type = submit, fields = Fs}, #iq{type = error} = send_recv(Config, #iq{type = set, to = MUC, sub_els = [#register{xdata = X}]}), + wait_for_master(Config), disconnect(Config). announce_master(Config) -> @@ -2741,6 +3367,12 @@ socks5_send(Sock, Data) -> socks5_recv(Sock, 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 %%%=================================================================== diff --git a/test/suite.erl b/test/suite.erl index 42c5dcfbe..ed1cbd83d 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -86,6 +86,8 @@ init_config(Config) -> {lang, <<"en">>}, {base_dir, BaseDir}, {socket, undefined}, + {pubsub_node, <<"node!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, + {pubsub_node_title, <<"title!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, diff --git a/test/suite.hrl b/test/suite.hrl index 4110da0df..00239f8cf 100644 --- a/test/suite.hrl +++ b/test/suite.hrl @@ -66,6 +66,14 @@ end end)()). +-define(match(Pattern, Result), + case Result of + Pattern -> + Pattern; + Mismatch -> + suite:match_failure([Mismatch], [??Pattern]) + end). + -define(COMMON_VHOST, <<"localhost">>). -define(MNESIA_VHOST, <<"mnesia.localhost">>). -define(REDIS_VHOST, <<"redis.localhost">>). diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec deleted file mode 100644 index 1f9b50066..000000000 --- a/tools/xmpp_codec.spec +++ /dev/null @@ -1,3507 +0,0 @@ --xml(last, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:last">>, - result = {last, '$seconds', '$status'}, - attrs = [#attr{name = <<"seconds">>, - enc = {enc_int, []}, - dec = {dec_int, [0, infinity]}}], - cdata = #cdata{default = <<"">>, label = '$status'}}). - --xml(version_name, - #elem{name = <<"name">>, - xmlns = <<"jabber:iq:version">>, - result = '$cdata', - cdata = #cdata{label = '$cdata', required = true}}). - --xml(version_ver, - #elem{name = <<"version">>, - xmlns = <<"jabber:iq:version">>, - result = '$cdata', - cdata = #cdata{label = '$cdata', required = true}}). - --xml(version_os, - #elem{name = <<"os">>, - xmlns = <<"jabber:iq:version">>, - result = '$cdata', - cdata = #cdata{label = '$cdata', required = true}}). - --xml(version, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:version">>, - result = {version, '$name', '$ver', '$os'}, - refs = [#ref{name = version_name, - label = '$name', - min = 0, max = 1}, - #ref{name = version_ver, - label = '$ver', - min = 0, max = 1}, - #ref{name = version_os, - label = '$os', - min = 0, max = 1}]}). - --xml(roster_group, - #elem{name = <<"group">>, - xmlns = <<"jabber:iq:roster">>, - result = '$cdata', - cdata = #cdata{required = true, label = '$cdata'}}). - --xml(roster_item, - #elem{name = <<"item">>, - xmlns = <<"jabber:iq:roster">>, - result = {roster_item, '$jid', '$name', - '$groups', '$subscription', '$ask'}, - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"name">>, - default = <<"">>}, - #attr{name = <<"subscription">>, - default = none, - enc = {enc_enum, []}, - dec = {dec_enum, [[none,to,from,both,remove]]}}, - #attr{name = <<"ask">>, - enc = {enc_enum, []}, - dec = {dec_enum, [[subscribe]]}}], - refs = [#ref{name = roster_group, label = '$groups'}]}). - --xml(roster_query, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:roster">>, - result = {roster_query, '$items', '$ver'}, - attrs = [#attr{name = <<"ver">>, default = undefined}], - refs = [#ref{name = roster_item, label = '$items'}]}). - --xml(rosterver_feature, - #elem{name = <<"ver">>, - xmlns = <<"urn:xmpp:features:rosterver">>, - result = {rosterver_feature}}). - --xml(privacy_message, #elem{name = <<"message">>, xmlns = <<"jabber:iq:privacy">>, - result = true}). --xml(privacy_iq, #elem{name = <<"iq">>, xmlns = <<"jabber:iq:privacy">>, - result = true}). --xml(privacy_presence_in, #elem{name = <<"presence-in">>, - xmlns = <<"jabber:iq:privacy">>, - result = true}). --xml(privacy_presence_out, #elem{name = <<"presence-out">>, - xmlns = <<"jabber:iq:privacy">>, - result = true}). - --xml(privacy_item, - #elem{name = <<"item">>, - xmlns = <<"jabber:iq:privacy">>, - result = {privacy_item, '$order', '$action', '$type', '$value', - '$message', '$iq', '$presence_in', '$presence_out'}, - attrs = [#attr{name = <<"action">>, - required = true, - dec = {dec_enum, [[allow, deny]]}, - enc = {enc_enum, []}}, - #attr{name = <<"order">>, - required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"type">>, - dec = {dec_enum, [[group, jid, subscription]]}, - enc = {enc_enum, []}}, - #attr{name = <<"value">>}], - refs = [#ref{name = privacy_message, default = false, - min = 0, max = 1, label = '$message'}, - #ref{name = privacy_iq, default = false, - min = 0, max = 1, label = '$iq'}, - #ref{name = privacy_presence_in, default = false, - min = 0, max = 1, label = '$presence_in'}, - #ref{name = privacy_presence_out, default = false, - min = 0, max = 1, label = '$presence_out'}]}). - --xml(privacy_list, - #elem{name = <<"list">>, - xmlns = <<"jabber:iq:privacy">>, - result = {privacy_list, '$name', '$items'}, - attrs = [#attr{name = <<"name">>, - required = true}], - refs = [#ref{name = privacy_item, - label = '$items'}]}). - --xml(privacy_default_list, - #elem{name = <<"default">>, - xmlns = <<"jabber:iq:privacy">>, - result = '$name', - attrs = [#attr{name = <<"name">>, - default = none}]}). - --xml(privacy_active_list, - #elem{name = <<"active">>, - xmlns = <<"jabber:iq:privacy">>, - result = '$name', - attrs = [#attr{name = <<"name">>, - default = none}]}). - --xml(privacy, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:privacy">>, - result = {privacy_query, '$lists', '$default', '$active'}, - refs = [#ref{name = privacy_list, - label = '$lists'}, - #ref{name = privacy_default_list, - min = 0, max = 1, - label = '$default'}, - #ref{name = privacy_active_list, - min = 0, max = 1, - label = '$active'}]}). - --xml(block_item, - #elem{name = <<"item">>, - xmlns = <<"urn:xmpp:blocking">>, - result = '$jid', - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(block, - #elem{name = <<"block">>, - xmlns = <<"urn:xmpp:blocking">>, - result = {block, '$items'}, - refs = [#ref{name = block_item, - label = '$items'}]}). - --xml(unblock, - #elem{name = <<"unblock">>, - xmlns = <<"urn:xmpp:blocking">>, - result = {unblock, '$items'}, - refs = [#ref{name = block_item, - label = '$items'}]}). - --xml(block_list, - #elem{name = <<"blocklist">>, - xmlns = <<"urn:xmpp:blocking">>, - result = {block_list, '$items'}, - refs = [#ref{name = block_item, - label = '$items'}]}). - --xml(disco_identity, - #elem{name = <<"identity">>, - xmlns = <<"http://jabber.org/protocol/disco#info">>, - result = {identity, '$category', '$type', '$lang', '$name'}, - attrs = [#attr{name = <<"category">>, - required = true}, - #attr{name = <<"type">>, - required = true}, - #attr{name = <<"xml:lang">>, - label = '$lang'}, - #attr{name = <<"name">>}]}). - --xml(disco_feature, - #elem{name = <<"feature">>, - xmlns = <<"http://jabber.org/protocol/disco#info">>, - result = '$var', - attrs = [#attr{name = <<"var">>, - required = true}]}). - --xml(disco_info, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/disco#info">>, - result = {disco_info, '$node', '$identities', '$features', '$xdata'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = disco_identity, - label = '$identities'}, - #ref{name = disco_feature, - label = '$features'}, - #ref{name = xdata, - label = '$xdata'}]}). - --xml(disco_item, - #elem{name = <<"item">>, - xmlns = <<"http://jabber.org/protocol/disco#items">>, - result = {disco_item, '$jid', '$name', '$node'}, - attrs = [#attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}, - required = true}, - #attr{name = <<"name">>}, - #attr{name = <<"node">>}]}). --xml(disco_items, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/disco#items">>, - result = {disco_items, '$node', '$items', '$rsm'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = disco_item, - label = '$items'}, - #ref{name = rsm_set, min = 0, max = 1, - label = '$rsm'}]}). - --xml(private, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:private">>, - result = {private, '$_xmls'}}). - --xml(conference_nick, - #elem{name = <<"nick">>, - xmlns = <<"storage:bookmarks">>, - result = '$cdata'}). - --xml(conference_password, - #elem{name = <<"password">>, - xmlns = <<"storage:bookmarks">>, - result = '$cdata'}). - --xml(bookmark_conference, - #elem{name = <<"conference">>, - xmlns = <<"storage:bookmarks">>, - result = {bookmark_conference, '$name', '$jid', - '$autojoin', '$nick', '$password'}, - attrs = [#attr{name = <<"name">>, - required = true}, - #attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"autojoin">>, - default = false, - dec = {dec_bool, []}, - enc = {enc_bool, []}}], - refs = [#ref{name = conference_nick, - label = '$nick', - min = 0, max = 1}, - #ref{name = conference_password, - label = '$password', - min = 0, max = 1}]}). - --xml(bookmark_url, - #elem{name = <<"url">>, - xmlns = <<"storage:bookmarks">>, - result = {bookmark_url, '$name', '$url'}, - attrs = [#attr{name = <<"name">>, - required = true}, - #attr{name = <<"url">>, - required = true}]}). - --xml(bookmarks_storage, - #elem{name = <<"storage">>, - xmlns = <<"storage:bookmarks">>, - result = {bookmark_storage, '$conference', '$url'}, - refs = [#ref{name = bookmark_conference, - label = '$conference'}, - #ref{name = bookmark_url, - label = '$url'}]}). - --xml(stat_error, - #elem{name = <<"error">>, - xmlns = <<"http://jabber.org/protocol/stats">>, - result = {stat_error, '$code', '$reason'}, - cdata = #cdata{default = <<"">>, label = '$reason'}, - attrs = [#attr{name = <<"code">>, - required = true, - enc = {enc_int, []}, - dec = {dec_int, []}}]}). - --xml(stat, - #elem{name = <<"stat">>, - xmlns = <<"http://jabber.org/protocol/stats">>, - result = {stat, '$name', '$units', '$value', '$error'}, - attrs = [#attr{name = <<"name">>, - required = true}, - #attr{name = <<"units">>, default = <<"">>}, - #attr{name = <<"value">>, default = <<"">>}], - refs = [#ref{name = stat_error, label = '$error', - min = 0, max = 1}]}). - --xml(stats, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/stats">>, - result = {stats, '$list', '$node'}, - attrs = [#attr{name = <<"node">>, default = <<"">>}], - refs = [#ref{name = stat, label = '$list'}]}). - --xml(iq, - #elem{name = <<"iq">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {iq, '$id', '$type', '$lang', '$from', '$to', '$_els'}, - attrs = [#attr{name = <<"id">>, - required = true}, - #attr{name = <<"type">>, - required = true, - enc = {enc_enum, []}, - dec = {dec_enum, [[get, set, result, error]]}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xml:lang">>, - label = '$lang'}]}). - --xml(message_subject, - #elem{name = <<"subject">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}). - --xml(message_body, - #elem{name = <<"body">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}). - --xml(message_thread, - #elem{name = <<"thread">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = '$cdata'}). - --xml(message, - #elem{name = <<"message">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {message, '$id', '$type', '$lang', '$from', '$to', - '$subject', '$body', '$thread', '$_els'}, - attrs = [#attr{name = <<"id">>}, - #attr{name = <<"type">>, - default = normal, - enc = {enc_enum, []}, - dec = {dec_enum, [[chat, normal, groupchat, - headline, error]]}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xml:lang">>, - label = '$lang'}], - refs = [#ref{name = message_subject, label = '$subject'}, - #ref{name = message_thread, min = 0, max = 1, label = '$thread'}, - #ref{name = message_body, label = '$body'}]}). - --xml(presence_show, - #elem{name = <<"show">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = '$cdata', - cdata = #cdata{enc = {enc_enum, []}, - dec = {dec_enum, [[away, chat, dnd, xa]]}}}). - --xml(presence_status, - #elem{name = <<"status">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - attrs = [#attr{name = <<"xml:lang">>, - label = '$lang'}]}). - --xml(presence_priority, - #elem{name = <<"priority">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = '$cdata', - cdata = #cdata{enc = {enc_int, []}, - dec = {dec_int, []}}}). - --xml(presence, - #elem{name = <<"presence">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {presence, '$id', '$type', '$lang', '$from', '$to', - '$show', '$status', '$priority', '$_els'}, - attrs = [#attr{name = <<"id">>}, - #attr{name = <<"type">>, - default = available, - enc = {enc_enum, []}, - dec = {dec_enum, [[unavailable, subscribe, subscribed, - unsubscribe, unsubscribed, - available, probe, error]]}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xml:lang">>, - label = '$lang'}], - refs = [#ref{name = presence_show, min = 0, max = 1, label = '$show'}, - #ref{name = presence_status, label = '$status'}, - #ref{name = presence_priority, min = 0, max = 1, - label = '$priority'}]}). - --xml(error_bad_request, - #elem{name = <<"bad-request">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'bad-request'}). --xml(error_conflict, - #elem{name = <<"conflict">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'conflict'}). --xml(error_feature_not_implemented, - #elem{name = <<"feature-not-implemented">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'feature-not-implemented'}). --xml(error_forbidden, - #elem{name = <<"forbidden">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'forbidden'}). --xml(error_gone, - #elem{name = <<"gone">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - cdata = #cdata{label = '$uri'}, - result = {'gone', '$uri'}}). --xml(error_internal_server_error, - #elem{name = <<"internal-server-error">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'internal-server-error'}). --xml(error_item_not_found, - #elem{name = <<"item-not-found">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'item-not-found'}). --xml(error_jid_malformed, - #elem{name = <<"jid-malformed">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'jid-malformed'}). --xml(error_not_acceptable, - #elem{name = <<"not-acceptable">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'not-acceptable'}). --xml(error_not_allowed, - #elem{name = <<"not-allowed">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'not-allowed'}). --xml(error_not_authorized, - #elem{name = <<"not-authorized">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'not-authorized'}). --xml(error_payment_required, - #elem{name = <<"payment-required">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'payment-required'}). --xml(error_policy_violation, - #elem{name = <<"policy-violation">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'policy-violation'}). --xml(error_recipient_unavailable, - #elem{name = <<"recipient-unavailable">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'recipient-unavailable'}). --xml(error_redirect, - #elem{name = <<"redirect">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - cdata = #cdata{label = '$uri'}, - result = {'redirect', '$uri'}}). --xml(error_registration_required, - #elem{name = <<"registration-required">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'registration-required'}). --xml(error_remote_server_not_found, - #elem{name = <<"remote-server-not-found">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'remote-server-not-found'}). --xml(error_remote_server_timeout, - #elem{name = <<"remote-server-timeout">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'remote-server-timeout'}). --xml(error_resource_constraint, - #elem{name = <<"resource-constraint">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'resource-constraint'}). --xml(error_service_unavailable, - #elem{name = <<"service-unavailable">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'service-unavailable'}). --xml(error_subscription_required, - #elem{name = <<"subscription-required">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'subscription-required'}). --xml(error_undefined_condition, - #elem{name = <<"undefined-condition">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'undefined-condition'}). --xml(error_unexpected_request, - #elem{name = <<"unexpected-request">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - result = 'unexpected-request'}). - --xml(error_text, - #elem{name = <<"text">>, - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, - attrs = [#attr{name = <<"xml:lang">>, - label = '$lang'}]}). - --xml(error, - #elem{name = <<"error">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {stanza_error, '$type', '$code', '$by', '$reason', '$text', '$_els'}, - attrs = [#attr{name = <<"type">>, - label = '$type', - required = true, - dec = {dec_enum, [[auth, cancel, continue, - modify, wait]]}, - enc = {enc_enum, []}}, - #attr{name = <<"code">>, - label = '$code', - enc = {enc_int, []}, - dec = {dec_int, [0, infinity]}}, - #attr{name = <<"by">>}], - refs = [#ref{name = error_text, - min = 0, max = 1, label = '$text'}, - #ref{name = error_bad_request, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_conflict, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_feature_not_implemented, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_forbidden, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_gone, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_internal_server_error, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_item_not_found, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_jid_malformed, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_acceptable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_allowed, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_authorized, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_payment_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_policy_violation, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_recipient_unavailable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_redirect, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_registration_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_remote_server_not_found, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_remote_server_timeout, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_resource_constraint, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_service_unavailable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_subscription_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_undefined_condition, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_unexpected_request, - min = 0, max = 1, label = '$reason'}]}). - --xml(bind_jid, - #elem{name = <<"jid">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, - result = '$cdata', - cdata = #cdata{dec = {dec_jid, []}, - enc = {enc_jid, []}}}). - --xml(bind_resource, - #elem{name = <<"resource">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, - result = '$cdata', - cdata = #cdata{dec = {resourceprep, []}, - enc = {resourceprep, []}}}). - --xml(bind, #elem{name = <<"bind">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-bind">>, - result = {bind, '$jid', '$resource'}, - refs = [#ref{name = bind_jid, - label = '$jid', - min = 0, max = 1}, - #ref{name = bind_resource, - min = 0, max = 1, - default = <<"">>, - label = '$resource'}]}). - --xml(legacy_auth_username, - #elem{name = <<"username">>, - xmlns = <<"jabber:iq:auth">>, - result = '$cdata'}). --xml(legacy_auth_password, - #elem{name = <<"password">>, - xmlns = <<"jabber:iq:auth">>, - result = '$cdata'}). --xml(legacy_auth_digest, - #elem{name = <<"digest">>, - xmlns = <<"jabber:iq:auth">>, - result = '$cdata'}). --xml(legacy_auth_resource, - #elem{name = <<"resource">>, - xmlns = <<"jabber:iq:auth">>, - result = '$cdata'}). - --xml(legacy_auth, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:auth">>, - result = {legacy_auth, '$username', '$password', - '$digest', '$resource'}, - refs = [#ref{name = legacy_auth_username, min = 0, max = 1, - label = '$username'}, - #ref{name = legacy_auth_password, min = 0, max = 1, - label = '$password'}, - #ref{name = legacy_auth_digest, min = 0, max = 1, - label = '$digest'}, - #ref{name = legacy_auth_resource, min = 0, max = 1, - label = '$resource'}]}). - --xml(sasl_auth, - #elem{name = <<"auth">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - cdata = #cdata{label = '$text', - dec = {base64, decode, []}, - enc = {base64, encode, []}}, - result = {sasl_auth, '$mechanism', '$text'}, - attrs = [#attr{name = <<"mechanism">>, - required = true}]}). - --xml(sasl_abort, - #elem{name = <<"abort">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - result = {sasl_abort}}). - --xml(sasl_challenge, - #elem{name = <<"challenge">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - cdata = #cdata{label = '$text', - dec = {base64, decode, []}, - enc = {base64, encode, []}}, - result = {sasl_challenge, '$text'}}). - --xml(sasl_response, - #elem{name = <<"response">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - cdata = #cdata{label = '$text', - dec = {base64, decode, []}, - enc = {base64, encode, []}}, - result = {sasl_response, '$text'}}). - --xml(sasl_success, - #elem{name = <<"success">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - cdata = #cdata{label = '$text', - dec = {base64, decode, []}, - enc = {base64, encode, []}}, - result = {sasl_success, '$text'}}). - --xml(sasl_failure_text, - #elem{name = <<"text">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - attrs = [#attr{name = <<"xml:lang">>, - label = '$lang'}]}). - --xml(sasl_failure_aborted, - #elem{name = <<"aborted">>, - result = 'aborted', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_account_disabled, - #elem{name = <<"account-disabled">>, - result = 'account-disabled', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_credentials_expired, - #elem{name = <<"credentials-expired">>, - result = 'credentials-expired', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_encryption_required, - #elem{name = <<"encryption-required">>, - result = 'encryption-required', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_incorrect_encoding, - #elem{name = <<"incorrect-encoding">>, - result = 'incorrect-encoding', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_invalid_authzid, - #elem{name = <<"invalid-authzid">>, - result = 'invalid-authzid', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_invalid_mechanism, - #elem{name = <<"invalid-mechanism">>, - result = 'invalid-mechanism', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_malformed_request, - #elem{name = <<"malformed-request">>, - result = 'malformed-request', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_mechanism_too_weak, - #elem{name = <<"mechanism-too-weak">>, - result = 'mechanism-too-weak', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_not_authorized, - #elem{name = <<"not-authorized">>, - result = 'not-authorized', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_bad_protocol, - #elem{name = <<"bad-protocol">>, - result = 'bad-protocol', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). --xml(sasl_failure_temporary_auth_failure, - #elem{name = <<"temporary-auth-failure">>, - result = 'temporary-auth-failure', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>}). - --xml(sasl_failure, - #elem{name = <<"failure">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - result = {sasl_failure, '$reason', '$text'}, - refs = [#ref{name = sasl_failure_text, - label = '$text'}, - #ref{name = sasl_failure_aborted, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_account_disabled, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_credentials_expired, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_encryption_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_incorrect_encoding, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_invalid_authzid, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_invalid_mechanism, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_malformed_request, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_mechanism_too_weak, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_not_authorized, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_bad_protocol, - min = 0, max = 1, label = '$reason'}, - #ref{name = sasl_failure_temporary_auth_failure, - min = 0, max = 1, label = '$reason'}]}). - --xml(sasl_mechanism, - #elem{name = <<"mechanism">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - result = '$cdata'}). - --xml(sasl_mechanisms, - #elem{name = <<"mechanisms">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, - result = {sasl_mechanisms, '$list'}, - refs = [#ref{name = sasl_mechanism, - label = '$list'}]}). - --xml(starttls_required, - #elem{name = <<"required">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, - result = true}). - --xml(starttls, - #elem{name = <<"starttls">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, - result = {starttls, '$required'}, - refs = [#ref{name = starttls_required, - label = '$required', - min = 0, max = 1, - default = false}]}). - --xml(starttls_proceed, - #elem{name = <<"proceed">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, - result = {starttls_proceed}}). - --xml(starttls_failure, - #elem{name = <<"failure">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-tls">>, - result = {starttls_failure}}). - --xml(compress_failure_setup_failed, - #elem{name = <<"setup-failed">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = 'setup-failed'}). --xml(compress_failure_processing_failed, - #elem{name = <<"processing-failed">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = 'processing-failed'}). --xml(compress_failure_unsupported_method, - #elem{name = <<"unsupported-method">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = 'unsupported-method'}). - --xml(compress_failure, - #elem{name = <<"failure">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = {compress_failure, '$reason'}, - refs = [#ref{name = compress_failure_setup_failed, - min = 0, max = 1, label = '$reason'}, - #ref{name = compress_failure_processing_failed, - min = 0, max = 1, label = '$reason'}, - #ref{name = compress_failure_unsupported_method, - min = 0, max = 1, label = '$reason'}]}). - --xml(compress_method, - #elem{name = <<"method">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = '$cdata'}). - --xml(compress, - #elem{name = <<"compress">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = {compress, '$methods'}, - refs = [#ref{name = compress_method, - label = '$methods'}]}). - --xml(compressed, - #elem{name = <<"compressed">>, - xmlns = <<"http://jabber.org/protocol/compress">>, - result = {compressed}}). - --xml(compression_method, - #elem{name = <<"method">>, - xmlns = <<"http://jabber.org/features/compress">>, - result = '$cdata'}). - --xml(compression, - #elem{name = <<"compression">>, - xmlns = <<"http://jabber.org/features/compress">>, - result = {compression, '$methods'}, - refs = [#ref{name = compression_method, label = '$methods'}]}). - --xml(stream_features, - #elem{name = <<"stream:features">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>], - result = {stream_features, '$_els'}}). - --xml(p1_push, - #elem{name = <<"push">>, - result = {p1_push}, - xmlns = <<"p1:push">>}). - --xml(p1_rebind, - #elem{name = <<"rebind">>, - result = {p1_rebind}, - xmlns = <<"p1:rebind">>}). - --xml(p1_ack, - #elem{name = <<"ack">>, - result = {p1_ack}, - xmlns = <<"p1:ack">>}). - --xml(caps, - #elem{name = <<"c">>, - xmlns = <<"http://jabber.org/protocol/caps">>, - result = {caps, '$node', '$version', '$hash', '$exts'}, - attrs = [#attr{name = <<"hash">>}, - #attr{name = <<"node">>}, - #attr{name = <<"ext">>, - label = '$exts', - default = [], - dec = {re, split, ["\\h+"]}, - enc = {join, [$ ]}}, - #attr{name = <<"ver">>, - label = '$version'}]}). - --xml(feature_register, - #elem{name = <<"register">>, - xmlns = <<"http://jabber.org/features/iq-register">>, - result = {feature_register}}). - --xml(register_registered, - #elem{name = <<"registered">>, - xmlns = <<"jabber:iq:register">>, - result = true}). --xml(register_remove, - #elem{name = <<"remove">>, - xmlns = <<"jabber:iq:register">>, - result = true}). --xml(register_instructions, - #elem{name = <<"instructions">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_username, - #elem{name = <<"username">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_nick, - #elem{name = <<"nick">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_password, - #elem{name = <<"password">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_name, - #elem{name = <<"name">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_first, - #elem{name = <<"first">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_last, - #elem{name = <<"last">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_email, - #elem{name = <<"email">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_address, - #elem{name = <<"address">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_city, - #elem{name = <<"city">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_state, - #elem{name = <<"state">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_zip, - #elem{name = <<"zip">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_phone, - #elem{name = <<"phone">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_url, - #elem{name = <<"url">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_date, - #elem{name = <<"date">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_misc, - #elem{name = <<"misc">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_text, - #elem{name = <<"text">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). --xml(register_key, - #elem{name = <<"key">>, - xmlns = <<"jabber:iq:register">>, - result = '$cdata'}). - --xml(register, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:register">>, - result = {register, '$registered', '$remove', '$instructions', - '$username', '$nick', '$password', '$name', - '$first', '$last', '$email', '$address', - '$city', '$state', '$zip', '$phone', '$url', - '$date', '$misc', '$text', '$key', '$xdata', '$_els'}, - refs = [#ref{name = xdata, min = 0, max = 1, - label = '$xdata'}, - #ref{name = register_registered, min = 0, max = 1, - default = false, label = '$registered'}, - #ref{name = register_remove, min = 0, max = 1, - default = false, label = '$remove'}, - #ref{name = register_instructions, min = 0, max = 1, - label = '$instructions'}, - #ref{name = register_username, min = 0, max = 1, - label = '$username'}, - #ref{name = register_nick, min = 0, max = 1, - label = '$nick'}, - #ref{name = register_password, min = 0, max = 1, - label = '$password'}, - #ref{name = register_name, min = 0, max = 1, - label = '$name'}, - #ref{name = register_first, min = 0, max = 1, - label = '$first'}, - #ref{name = register_last, min = 0, max = 1, - label = '$last'}, - #ref{name = register_email, min = 0, max = 1, - label = '$email'}, - #ref{name = register_address, min = 0, max = 1, - label = '$address'}, - #ref{name = register_city, min = 0, max = 1, - label = '$city'}, - #ref{name = register_state, min = 0, max = 1, - label = '$state'}, - #ref{name = register_zip, min = 0, max = 1, - label = '$zip'}, - #ref{name = register_phone, min = 0, max = 1, - label = '$phone'}, - #ref{name = register_url, min = 0, max = 1, - label = '$url'}, - #ref{name = register_date, min = 0, max = 1, - label = '$date'}, - #ref{name = register_misc, min = 0, max = 1, - label = '$misc'}, - #ref{name = register_text, min = 0, max = 1, - label = '$text'}, - #ref{name = register_key, min = 0, max = 1, - label = '$key'}]}). - --xml(session_optional, - #elem{name = <<"optional">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-session">>, - result = true}). - --xml(session, - #elem{name = <<"session">>, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-session">>, - result = {xmpp_session, '$optional'}, - refs = [#ref{name = session_optional, - min = 0, max = 1, default = false, - label = '$optional'}]}). - --xml(ping, - #elem{name = <<"ping">>, - xmlns = <<"urn:xmpp:ping">>, - result = {ping}}). - --xml(time_utc, - #elem{name = <<"utc">>, - xmlns = <<"urn:xmpp:time">>, - result = '$cdata', - cdata = #cdata{dec = {dec_utc, []}, - enc = {enc_utc, []}}}). - --xml(time_tzo, - #elem{name = <<"tzo">>, - xmlns = <<"urn:xmpp:time">>, - result = '$cdata', - cdata = #cdata{dec = {dec_tzo, []}, - enc = {enc_tzo, []}}}). - --xml(time, - #elem{name = <<"time">>, - xmlns = <<"urn:xmpp:time">>, - result = {time, '$tzo', '$utc'}, - refs = [#ref{name = time_tzo, - label = '$tzo', - min = 0, max = 1}, - #ref{name = time_utc, - label = '$utc', - min = 0, max = 1}]}). - --xml(stream_error_text, - #elem{name = <<"text">>, - result = {text, '$lang', '$data'}, - cdata = #cdata{label = '$data'}, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>, - attrs = [#attr{name = <<"xml:lang">>, - label = '$lang'}]}). - --xml(stream_error_bad_format, - #elem{name = <<"bad-format">>, - result = 'bad-format', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_bad_namespace_prefix, - #elem{name = <<"bad-namespace-prefix">>, - result = 'bad-namespace-prefix', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_conflict, - #elem{name = <<"conflict">>, - result = 'conflict', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_connection_timeout, - #elem{name = <<"connection-timeout">>, - result = 'connection-timeout', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_host_gone, - #elem{name = <<"host-gone">>, - result = 'host-gone', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_host_unknown, - #elem{name = <<"host-unknown">>, - result = 'host-unknown', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_improper_addressing, - #elem{name = <<"improper-addressing">>, - result = 'improper-addressing', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_internal_server_error, - #elem{name = <<"internal-server-error">>, - result = 'internal-server-error', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_invalid_from, - #elem{name = <<"invalid-from">>, - result = 'invalid-from', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_invalid_id, - #elem{name = <<"invalid-id">>, - result = 'invalid-id', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_invalid_namespace, - #elem{name = <<"invalid-namespace">>, - result = 'invalid-namespace', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_invalid_xml, - #elem{name = <<"invalid-xml">>, - result = 'invalid-xml', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_not_authorized, - #elem{name = <<"not-authorized">>, - result = 'not-authorized', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_not_well_formed, - #elem{name = <<"not-well-formed">>, - result = 'not-well-formed', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_policy_violation, - #elem{name = <<"policy-violation">>, - result = 'policy-violation', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_remote_connection_failed, - #elem{name = <<"remote-connection-failed">>, - result = 'remote-connection-failed', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_reset, - #elem{name = <<"reset">>, - result = 'reset', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_resource_constraint, - #elem{name = <<"resource-constraint">>, - result = 'resource-constraint', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_restricted_xml, - #elem{name = <<"restricted-xml">>, - result = 'restricted-xml', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_see_other_host, - #elem{name = <<"see-other-host">>, - cdata = #cdata{required = true, label = '$host', - dec = {dec_host_port, []}, - enc = {enc_host_port, []}}, - result = {'see-other-host', '$host'}, - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_system_shutdown, - #elem{name = <<"system-shutdown">>, - result = 'system-shutdown', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_undefined_condition, - #elem{name = <<"undefined-condition">>, - result = 'undefined-condition', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_unsupported_encoding, - #elem{name = <<"unsupported-encoding">>, - result = 'unsupported-encoding', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_unsupported_stanza_type, - #elem{name = <<"unsupported-stanza-type">>, - result = 'unsupported-stanza-type', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). --xml(stream_error_unsupported_version, - #elem{name = <<"unsupported-version">>, - result = 'unsupported-version', - xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>}). - --xml(stream_error, - #elem{name = <<"stream:error">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {stream_error, '$reason', '$text'}, - refs = [#ref{name = stream_error_text, - label = '$text', - min = 0, max = 1}, - #ref{name = stream_error_bad_format, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_bad_namespace_prefix, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_conflict, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_connection_timeout, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_host_gone, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_host_unknown, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_improper_addressing, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_internal_server_error, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_invalid_from, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_invalid_id, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_invalid_namespace, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_invalid_xml, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_not_authorized, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_not_well_formed, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_policy_violation, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_remote_connection_failed, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_reset, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_resource_constraint, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_restricted_xml, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_see_other_host, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_system_shutdown, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_undefined_condition, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_unsupported_encoding, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_unsupported_stanza_type, - min = 0, max = 1, label = '$reason'}, - #ref{name = stream_error_unsupported_version, - min = 0, max = 1, label = '$reason'} - ]}). - --xml(vcard_HOME, #elem{name = <<"HOME">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_WORK, #elem{name = <<"WORK">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_VOICE, #elem{name = <<"VOICE">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_FAX, #elem{name = <<"FAX">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_PAGER, #elem{name = <<"PAGER">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_MSG, #elem{name = <<"MSG">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_CELL, #elem{name = <<"CELL">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_VIDEO, #elem{name = <<"VIDEO">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_BBS, #elem{name = <<"BBS">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_MODEM, #elem{name = <<"MODEM">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_ISDN, #elem{name = <<"ISDN">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_PCS, #elem{name = <<"PCS">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_POSTAL, #elem{name = <<"POSTAL">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_PARCEL, #elem{name = <<"PARCEL">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_DOM, #elem{name = <<"DOM">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_INTL, #elem{name = <<"INTL">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_PREF, #elem{name = <<"PREF">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_INTERNET, #elem{name = <<"INTERNET">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_X400, #elem{name = <<"X400">>, xmlns = <<"vcard-temp">>, result = true}). --xml(vcard_FAMILY, #elem{name = <<"FAMILY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_GIVEN, #elem{name = <<"GIVEN">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_MIDDLE, #elem{name = <<"MIDDLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_PREFIX, #elem{name = <<"PREFIX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_SUFFIX, #elem{name = <<"SUFFIX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_POBOX, #elem{name = <<"POBOX">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_EXTADD, #elem{name = <<"EXTADD">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_STREET, #elem{name = <<"STREET">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_LOCALITY, #elem{name = <<"LOCALITY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_REGION, #elem{name = <<"REGION">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_PCODE, #elem{name = <<"PCODE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_CTRY, #elem{name = <<"CTRY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_LINE, #elem{name = <<"LINE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_NUMBER, #elem{name = <<"NUMBER">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_USERID, #elem{name = <<"USERID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_LAT, #elem{name = <<"LAT">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_LON, #elem{name = <<"LON">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_ORGNAME, #elem{name = <<"ORGNAME">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_ORGUNIT, #elem{name = <<"ORGUNIT">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_PHONETIC, #elem{name = <<"PHONETIC">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_CRED, #elem{name = <<"CRED">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_VERSION, #elem{name = <<"VERSION">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_FN, #elem{name = <<"FN">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_NICKNAME, #elem{name = <<"NICKNAME">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_BDAY, #elem{name = <<"BDAY">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_JABBERID, #elem{name = <<"JABBERID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_MAILER, #elem{name = <<"MAILER">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_TZ, #elem{name = <<"TZ">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_TITLE, #elem{name = <<"TITLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_ROLE, #elem{name = <<"ROLE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_KEYWORD, #elem{name = <<"KEYWORD">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_NOTE, #elem{name = <<"NOTE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_PRODID, #elem{name = <<"PRODID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_REV, #elem{name = <<"REV">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_SORT_STRING, #elem{name = <<"SORT-STRING">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_UID, #elem{name = <<"UID">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_URL, #elem{name = <<"URL">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_DESC, #elem{name = <<"DESC">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_TYPE, #elem{name = <<"TYPE">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_EXTVAL, #elem{name = <<"EXTVAL">>, xmlns = <<"vcard-temp">>, result = '$cdata'}). --xml(vcard_PUBLIC, #elem{name = <<"PUBLIC">>, xmlns = <<"vcard-temp">>, result = public}). --xml(vcard_PRIVATE, #elem{name = <<"PRIVATE">>, xmlns = <<"vcard-temp">>, result = private}). --xml(vcard_CONFIDENTIAL, #elem{name = <<"CONFIDENTIAL">>, xmlns = <<"vcard-temp">>, result = confidential}). - --xml(vcard_N, - #elem{name = <<"N">>, - xmlns = <<"vcard-temp">>, - result = {vcard_name, '$family', '$given', '$middle', - '$prefix', '$suffix'}, - refs = [#ref{name = vcard_FAMILY, min = 0, max = 1, label = '$family'}, - #ref{name = vcard_GIVEN, min = 0, max = 1, label = '$given'}, - #ref{name = vcard_MIDDLE, min = 0, max = 1, label = '$middle'}, - #ref{name = vcard_PREFIX, min = 0, max = 1, label = '$prefix'}, - #ref{name = vcard_SUFFIX, min = 0, max = 1, label = '$suffix'}]}). - --xml(vcard_ADR, - #elem{name = <<"ADR">>, - xmlns = <<"vcard-temp">>, - result = {vcard_adr, '$home', '$work', '$postal', '$parcel', - '$dom', '$intl', '$pref', '$pobox', '$extadd', '$street', - '$locality', '$region', '$pcode', '$ctry'}, - refs = [#ref{name = vcard_HOME, default = false, - min = 0, max = 1, label = '$home'}, - #ref{name = vcard_WORK, default = false, - min = 0, max = 1, label = '$work'}, - #ref{name = vcard_POSTAL, default = false, - min = 0, max = 1, label = '$postal'}, - #ref{name = vcard_PARCEL, default = false, - min = 0, max = 1, label = '$parcel'}, - #ref{name = vcard_DOM, default = false, - min = 0, max = 1, label = '$dom'}, - #ref{name = vcard_INTL, default = false, - min = 0, max = 1, label = '$intl'}, - #ref{name = vcard_PREF, default = false, - min = 0, max = 1, label = '$pref'}, - #ref{name = vcard_POBOX, min = 0, max = 1, label = '$pobox'}, - #ref{name = vcard_EXTADD, min = 0, max = 1, label = '$extadd'}, - #ref{name = vcard_STREET, min = 0, max = 1, label = '$street'}, - #ref{name = vcard_LOCALITY, min = 0, max = 1, label = '$locality'}, - #ref{name = vcard_REGION, min = 0, max = 1, label = '$region'}, - #ref{name = vcard_PCODE, min = 0, max = 1, label = '$pcode'}, - #ref{name = vcard_CTRY, min = 0, max = 1, label = '$ctry'}]}). - --xml(vcard_LABEL, - #elem{name = <<"LABEL">>, - xmlns = <<"vcard-temp">>, - result = {vcard_label, '$home', '$work', '$postal', '$parcel', - '$dom', '$intl', '$pref', '$line'}, - refs = [#ref{name = vcard_HOME, default = false, - min = 0, max = 1, label = '$home'}, - #ref{name = vcard_WORK, default = false, - min = 0, max = 1, label = '$work'}, - #ref{name = vcard_POSTAL, default = false, - min = 0, max = 1, label = '$postal'}, - #ref{name = vcard_PARCEL, default = false, - min = 0, max = 1, label = '$parcel'}, - #ref{name = vcard_DOM, default = false, - min = 0, max = 1, label = '$dom'}, - #ref{name = vcard_INTL, default = false, - min = 0, max = 1, label = '$intl'}, - #ref{name = vcard_PREF, default = false, - min = 0, max = 1, label = '$pref'}, - #ref{name = vcard_LINE, label = '$line'}]}). - --xml(vcard_TEL, - #elem{name = <<"TEL">>, - xmlns = <<"vcard-temp">>, - result = {vcard_tel, '$home', '$work', '$voice', '$fax', - '$pager', '$msg', '$cell', '$video', '$bbs', - '$modem', '$isdn', '$pcs', '$pref', '$number'}, - refs = [#ref{name = vcard_HOME, default = false, - min = 0, max = 1, label = '$home'}, - #ref{name = vcard_WORK, default = false, - min = 0, max = 1, label = '$work'}, - #ref{name = vcard_VOICE, default = false, - min = 0, max = 1, label = '$voice'}, - #ref{name = vcard_FAX, default = false, - min = 0, max = 1, label = '$fax'}, - #ref{name = vcard_PAGER, default = false, - min = 0, max = 1, label = '$pager'}, - #ref{name = vcard_MSG, default = false, - min = 0, max = 1, label = '$msg'}, - #ref{name = vcard_CELL, default = false, - min = 0, max = 1, label = '$cell'}, - #ref{name = vcard_VIDEO, default = false, - min = 0, max = 1, label = '$video'}, - #ref{name = vcard_BBS, default = false, - min = 0, max = 1, label = '$bbs'}, - #ref{name = vcard_MODEM, default = false, - min = 0, max = 1, label = '$modem'}, - #ref{name = vcard_ISDN, default = false, - min = 0, max = 1, label = '$isdn'}, - #ref{name = vcard_PCS, default = false, - min = 0, max = 1, label = '$pcs'}, - #ref{name = vcard_PREF, default = false, - min = 0, max = 1, label = '$pref'}, - #ref{name = vcard_NUMBER, - min = 0, max = 1, label = '$number'}]}). - --xml(vcard_EMAIL, - #elem{name = <<"EMAIL">>, - xmlns = <<"vcard-temp">>, - result = {vcard_email, '$home', '$work', - '$internet', '$pref', '$x400', '$userid'}, - refs = [#ref{name = vcard_HOME, default = false, - min = 0, max = 1, label = '$home'}, - #ref{name = vcard_WORK, default = false, - min = 0, max = 1, label = '$work'}, - #ref{name = vcard_INTERNET, default = false, - min = 0, max = 1, label = '$internet'}, - #ref{name = vcard_PREF, default = false, - min = 0, max = 1, label = '$pref'}, - #ref{name = vcard_X400, default = false, - min = 0, max = 1, label = '$x400'}, - #ref{name = vcard_USERID, - min = 0, max = 1, label = '$userid'}]}). - --xml(vcard_GEO, - #elem{name = <<"GEO">>, - xmlns = <<"vcard-temp">>, - result = {vcard_geo, '$lat', '$lon'}, - refs = [#ref{name = vcard_LAT, min = 0, max = 1, label = '$lat'}, - #ref{name = vcard_LON, min = 0, max = 1, label = '$lon'}]}). - --xml(vcard_BINVAL, - #elem{name = <<"BINVAL">>, - xmlns = <<"vcard-temp">>, - cdata = #cdata{dec = {base64, decode, []}, - enc = {base64, encode, []}}, - result = '$cdata'}). - --xml(vcard_LOGO, - #elem{name = <<"LOGO">>, - xmlns = <<"vcard-temp">>, - result = {vcard_logo, '$type', '$binval', '$extval'}, - refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, - #ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, - #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). - --xml(vcard_PHOTO, - #elem{name = <<"PHOTO">>, - xmlns = <<"vcard-temp">>, - result = {vcard_photo, '$type', '$binval', '$extval'}, - refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, - #ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, - #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). - --xml(vcard_ORG, - #elem{name = <<"ORG">>, - xmlns = <<"vcard-temp">>, - result = {vcard_org, '$name', '$units'}, - refs = [#ref{name = vcard_ORGNAME, - label = '$name', - min = 0, max = 1}, - #ref{name = vcard_ORGUNIT, - label = '$units'}]}). - --xml(vcard_SOUND, - #elem{name = <<"SOUND">>, - xmlns = <<"vcard-temp">>, - result = {vcard_sound, '$phonetic', '$binval', '$extval'}, - refs = [#ref{name = vcard_BINVAL, min = 0, max = 1, label = '$binval'}, - #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}, - #ref{name = vcard_PHONETIC, min = 0, max = 1, label = '$phonetic'}]}). - --xml(vcard_KEY, - #elem{name = <<"KEY">>, - xmlns = <<"vcard-temp">>, - result = {vcard_key, '$type', '$cred'}, - refs = [#ref{name = vcard_TYPE, min = 0, max = 1, label = '$type'}, - #ref{name = vcard_CRED, min = 0, max = 1, label = '$cred'}]}). - --xml(vcard_CATEGORIES, - #elem{name = <<"CATEGORIES">>, - xmlns = <<"vcard-temp">>, - result = '$keywords', - refs = [#ref{name = vcard_KEYWORD, label = '$keywords'}]}). - --xml(vcard_CLASS, - #elem{name = <<"CLASS">>, - xmlns = <<"vcard-temp">>, - result = '$class', - refs = [#ref{name = vcard_PUBLIC, min = 0, max = 1, label = '$class'}, - #ref{name = vcard_PRIVATE, min = 0, max = 1, label = '$class'}, - #ref{name = vcard_CONFIDENTIAL, min = 0, max = 1, label = '$class'}]}). - -%% {vcard_AGENT, -%% #elem{name = <<"AGENT">>, -%% xmlns = <<"vcard-temp">>, -%% result = {vcard_agent, '$vcard', '$extval'}, -%% refs = [#ref{name = vcard, min = 0, max = 1, label = '$vcard'}, -%% #ref{name = vcard_EXTVAL, min = 0, max = 1, label = '$extval'}]}). - --xml(vcard_temp, - #elem{name = <<"vCard">>, - xmlns = <<"vcard-temp">>, - result = {vcard_temp, '$version', '$fn', '$n', '$nickname', '$photo', - '$bday', '$adr', '$label', '$tel', '$email', '$jabberid', - '$mailer', '$tz', '$geo', '$title', '$role', '$logo', - '$org', '$categories', '$note', '$prodid', %% '$agent', - '$rev', '$sort_string', '$sound', '$uid', '$url', '$class', - '$key', '$desc'}, - refs = [#ref{name = vcard_N, min = 0, max = 1, label = '$n'}, - #ref{name = vcard_ADR, label = '$adr'}, - #ref{name = vcard_LABEL, label = '$label'}, - #ref{name = vcard_TEL, label = '$tel'}, - #ref{name = vcard_EMAIL, label = '$email'}, - #ref{name = vcard_GEO, min = 0, max = 1, label = '$geo'}, - #ref{name = vcard_LOGO, min = 0, max = 1, label = '$logo'}, - #ref{name = vcard_PHOTO, min = 0, max = 1, label = '$photo'}, - #ref{name = vcard_ORG, min = 0, max = 1, label = '$org'}, - #ref{name = vcard_SOUND, min = 0, max = 1, label = '$sound'}, - #ref{name = vcard_KEY, min = 0, max = 1, label = '$key'}, - #ref{name = vcard_VERSION, min = 0, max = 1, label = '$version'}, - #ref{name = vcard_FN, min = 0, max = 1, label = '$fn'}, - #ref{name = vcard_NICKNAME, min = 0, max = 1, label = '$nickname'}, - #ref{name = vcard_BDAY, min = 0, max = 1, label = '$bday'}, - #ref{name = vcard_JABBERID, min = 0, max = 1, label = '$jabberid'}, - #ref{name = vcard_MAILER, min = 0, max = 1, label = '$mailer'}, - #ref{name = vcard_TZ, min = 0, max = 1, label = '$tz'}, - #ref{name = vcard_TITLE, min = 0, max = 1, label = '$title'}, - #ref{name = vcard_ROLE, min = 0, max = 1, label = '$role'}, - #ref{name = vcard_NOTE, min = 0, max = 1, label = '$note'}, - #ref{name = vcard_PRODID, min = 0, max = 1, label = '$prodid'}, - #ref{name = vcard_REV, min = 0, max = 1, label = '$rev'}, - %%#ref{name = vcard_AGENT, min = 0, max = 1, label = '$agent'}, - #ref{name = vcard_SORT_STRING, min = 0, max = 1, - label = '$sort_string'}, - #ref{name = vcard_UID, min = 0, max = 1, label = '$uid'}, - #ref{name = vcard_URL, min = 0, max = 1, label = '$url'}, - #ref{name = vcard_DESC, min = 0, max = 1, label = '$desc'}, - #ref{name = vcard_CATEGORIES, default = [], min = 0, max = 1, - label = '$categories'}, - #ref{name = vcard_CLASS, min = 0, max = 1, label = '$class'}]}). - --xml(vcard_xupdate_photo, - #elem{name = <<"photo">>, - xmlns = <<"vcard-temp:x:update">>, - result = '$cdata'}). - --record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()}, - hash :: binary()}). --type vcard_xupdate() :: #vcard_xupdate{}. - --xml(vcard_xupdate, - #elem{name = <<"x">>, - xmlns = <<"vcard-temp:x:update">>, - result = {vcard_xupdate, '$_', '$hash'}, - refs = [#ref{name = vcard_xupdate_photo, min = 0, max = 1, - label = '$hash'}]}). - --xml(xdata_field_required, - #elem{name = <<"required">>, - xmlns = <<"jabber:x:data">>, - result = true}). - --xml(xdata_field_desc, - #elem{name = <<"desc">>, xmlns = <<"jabber:x:data">>, result = '$cdata'}). - --xml(xdata_field_value, - #elem{name = <<"value">>, xmlns = <<"jabber:x:data">>, result = '$cdata'}). - --xml(xdata_field_option, - #elem{name = <<"option">>, - xmlns = <<"jabber:x:data">>, - result = {xdata_option, '$label', '$value'}, - attrs = [#attr{name = <<"label">>}], - refs = [#ref{name = xdata_field_value, - label = '$value', - min = 1, max = 1}]}). - --xml(xdata_field, - #elem{name = <<"field">>, - xmlns = <<"jabber:x:data">>, - result = {xdata_field, '$label', '$type', '$var', - '$required', '$desc', '$values', '$options', '$_els'}, - attrs = [#attr{name = <<"label">>}, - #attr{name = <<"type">>, - enc = {enc_enum, []}, - dec = {dec_enum, [['boolean', - 'fixed', - 'hidden', - 'jid-multi', - 'jid-single', - 'list-multi', - 'list-single', - 'text-multi', - 'text-private', - 'text-single']]}}, - #attr{name = <<"var">>}], - refs = [#ref{name = xdata_field_required, - label = '$required', - default = false, - min = 0, max = 1}, - #ref{name = xdata_field_desc, - label = '$desc', - min = 0, max = 1}, - #ref{name = xdata_field_value, - label = '$values'}, - #ref{name = xdata_field_option, - label = '$options'}]}). - --xml(xdata_instructions, #elem{name = <<"instructions">>, - xmlns = <<"jabber:x:data">>, - result = '$cdata'}). --xml(xdata_title, #elem{name = <<"title">>, - xmlns = <<"jabber:x:data">>, - result = '$cdata'}). --xml(xdata_reported, #elem{name = <<"reported">>, - xmlns = <<"jabber:x:data">>, - result = '$fields', - refs = [#ref{name = xdata_field, - label = '$fields'}]}). --xml(xdata_item, #elem{name = <<"item">>, - xmlns = <<"jabber:x:data">>, - result = '$fields', - refs = [#ref{name = xdata_field, - label = '$fields'}]}). - --xml(xdata, - #elem{name = <<"x">>, - xmlns = <<"jabber:x:data">>, - result = {xdata, '$type', '$instructions', '$title', - '$reported', '$items', '$fields'}, - attrs = [#attr{name = <<"type">>, - required = true, - dec = {dec_enum, [[cancel, form, result, submit]]}, - enc = {enc_enum, []}}], - refs = [#ref{name = xdata_instructions, - label = '$instructions'}, - #ref{name = xdata_title, - label = '$title', - min = 0, max = 1}, - #ref{name = xdata_reported, - label = '$reported', - min = 0, max = 1}, - #ref{name = xdata_item, - label = '$items'}, - #ref{name = xdata_field, - label = '$fields'}]}). - --xml(pubsub_subscription, - #elem{name = <<"subscription">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = {ps_subscription, '$xmlns', '$jid', '$type', - '$node', '$subid', '$expiry'}, - attrs = [#attr{name = <<"xmlns">>}, - #attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"node">>}, - #attr{name = <<"subid">>}, - #attr{name = <<"subscription">>, - label = '$type', - dec = {dec_enum, [[none, pending, subscribed, - unconfigured]]}, - enc = {enc_enum, []}}, - #attr{name = <<"expiry">>, - dec = {dec_utc, []}, - enc = {enc_utc, []}}]}). - --record(ps_affiliation, {xmlns = <<>> :: binary(), - node = <<>> :: binary(), - type :: member | none | outcast | - owner | publisher | 'publish-only', - jid :: jid:jid()}). --type ps_affiliation() :: #ps_affiliation{}. - --xml(pubsub_affiliation, - #elem{name = <<"affiliation">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_affiliation, '$xmlns', '$node', '$type', '$_'}, - attrs = [#attr{name = <<"node">>, required = true}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"affiliation">>, - label = '$type', - required = true, - dec = {dec_enum, [[member, none, outcast, owner, - publisher, 'publish-only']]}, - enc = {enc_enum, []}}]}). - --xml(pubsub_owner_affiliation, - #elem{name = <<"affiliation">>, - xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, - result = {ps_affiliation, '$xmlns', '$_', '$type', '$jid'}, - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"affiliation">>, - label = '$type', - required = true, - dec = {dec_enum, [[member, none, outcast, owner, - publisher, 'publish-only']]}, - enc = {enc_enum, []}}]}). - --xml(pubsub_event_configuration, - #elem{name = <<"configuration">>, - xmlns = <<"http://jabber.org/protocol/pubsub#event">>, - result = {'$node', '$xdata'}, - attrs = [#attr{name = <<"node">>, required = true}], - refs = [#ref{name = xdata, min = 0, max = 1}]}). - --xml(pubsub_event_retract, - #elem{name = <<"retract">>, - xmlns = <<"http://jabber.org/protocol/pubsub#event">>, - result = '$id', - attrs = [#attr{name = <<"id">>, required = true}]}). - --xml(pubsub_item, - #elem{name = <<"item">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = {ps_item, '$xmlns', '$id', '$_xmls', '$node', '$publisher'}, - attrs = [#attr{name = <<"id">>}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"node">>}, - #attr{name = <<"publisher">>}]}). - --xml(pubsub_items, - #elem{name = <<"items">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = {ps_items, '$xmlns', '$node', '$items', '$max_items', - '$subid', '$retract'}, - attrs = [#attr{name = <<"xmlns">>}, - #attr{name = <<"max_items">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"node">>, - required = true}, - #attr{name = <<"subid">>}], - refs = [#ref{name = pubsub_event_retract, label = '$retract', - min = 0, max = 1}, - #ref{name = pubsub_item, label = '$items'}]}). - --xml(pubsub_event, - #elem{name = <<"event">>, - xmlns = <<"http://jabber.org/protocol/pubsub#event">>, - result = {ps_event, '$items', '$purge', '$subscription', '$delete', - '$create', '$configuration'}, - refs = [#ref{name = pubsub_items, label = '$items', - min = 0, max = 1}, - #ref{name = pubsub_subscription, min = 0, max = 1, - label = '$subscription'}, - #ref{name = pubsub_purge, min = 0, max = 1, - label = '$purge'}, - #ref{name = pubsub_delete, min = 0, max = 1, - label = '$delete'}, - #ref{name = pubsub_create, min = 0, max = 1, - label = '$create'}, - #ref{name = pubsub_event_configuration, min = 0, max = 1, - label = '$configuration'}]}). - --xml(pubsub_subscriptions, - #elem{name = <<"subscriptions">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>], - result = {'$node', '$subscriptions'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = pubsub_subscription, label = '$subscriptions'}]}). - --xml(pubsub_affiliations, - #elem{name = <<"affiliations">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {'$node', '$affiliations'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = pubsub_affiliation, label = '$affiliations'}]}). - --xml(pubsub_owner_affiliations, - #elem{name = <<"affiliations">>, - xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, - result = {'$node', '$affiliations'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = pubsub_owner_affiliation, label = '$affiliations'}]}). - --xml(pubsub_subscribe, - #elem{name = <<"subscribe">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_subscribe, '$node', '$jid'}, - attrs = [#attr{name = <<"node">>}, - #attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(pubsub_unsubscribe, - #elem{name = <<"unsubscribe">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_unsubscribe, '$node', '$jid', '$subid'}, - attrs = [#attr{name = <<"node">>}, - #attr{name = <<"subid">>}, - #attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(pubsub_publish, - #elem{name = <<"publish">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_publish, '$node', '$items'}, - attrs = [#attr{name = <<"node">>, - required = true}], - refs = [#ref{name = pubsub_item, label = '$items'}]}). - --xml(pubsub_options, - #elem{name = <<"options">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_options, '$node', '$jid', '$subid', '$xdata'}, - attrs = [#attr{name = <<"node">>}, - #attr{name = <<"subid">>}, - #attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}], - refs = [#ref{name = xdata, min = 0, max = 1, - label = '$xdata'}]}). - --xml(pubsub_retract, - #elem{name = <<"retract">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {ps_retract, '$node', '$notify', '$items'}, - attrs = [#attr{name = <<"node">>, - required = true}, - #attr{name = <<"notify">>, - default = false, - dec = {dec_bool, []}, - enc = {enc_bool, []}}], - refs = [#ref{name = pubsub_item, label = '$items'}]}). - --xml(pubsub_create, - #elem{name = <<"create">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = '$node', - attrs = [#attr{name = <<"node">>}]}). - --xml(pubsub_configure, - #elem{name = <<"configure">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>], - result = {'$node', '$xdata'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = xdata, min = 0, max = 1}]}). - --xml(pubsub_publish_options, - #elem{name = <<"publish-options">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = '$xdata', - refs = [#ref{name = xdata, min = 0, max = 1}]}). - --xml(pubsub_default, - #elem{name = <<"default">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>], - result = {'$node', '$xdata'}, - attrs = [#attr{name = <<"node">>}], - refs = [#ref{name = xdata, min = 0, max = 1}]}). - --xml(pubsub_redirect, - #elem{name = <<"redirect">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = '$uri', - attrs = [#attr{name = <<"uri">>, required = true}]}). - --xml(pubsub_delete, - #elem{name = <<"delete">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = {'$node', '$uri'}, - attrs = [#attr{name = <<"node">>, required = true}], - refs = [#ref{name = pubsub_redirect, min = 0, max = 1, - label = '$uri', default = <<>>}]}). - --xml(pubsub_purge, - #elem{name = <<"purge">>, - xmlns = [<<"http://jabber.org/protocol/pubsub">>, - <<"http://jabber.org/protocol/pubsub#owner">>, - <<"http://jabber.org/protocol/pubsub#event">>], - result = '$node', - attrs = [#attr{name = <<"node">>, required = true}]}). - --xml(pubsub, - #elem{name = <<"pubsub">>, - xmlns = <<"http://jabber.org/protocol/pubsub">>, - result = {pubsub, '$subscriptions', '$subscription', - '$affiliations', '$publish', '$publish_options', - '$subscribe', '$unsubscribe', '$options', '$items', - '$retract', '$create', '$configure', '$default', '$delete', - '$purge', '$rsm'}, - refs = [#ref{name = pubsub_subscriptions, label = '$subscriptions', - min = 0, max = 1}, - #ref{name = pubsub_affiliations, label = '$affiliations', - min = 0, max = 1}, - #ref{name = pubsub_subscribe, label = '$subscribe', - min = 0, max = 1}, - #ref{name = pubsub_unsubscribe, label = '$unsubscribe', - min = 0, max = 1}, - #ref{name = pubsub_options, label = '$options', - min = 0, max = 1}, - #ref{name = pubsub_items, label = '$items', - min = 0, max = 1}, - #ref{name = pubsub_retract, label = '$retract', - min = 0, max = 1}, - #ref{name = pubsub_create, label = '$create', - min = 0, max = 1}, - #ref{name = pubsub_configure, label = '$configure', - min = 0, max = 1}, - #ref{name = pubsub_publish_options, min = 0, max = 1, - label = '$publish_options'}, - #ref{name = pubsub_default, label = '$default', - min = 0, max = 1}, - #ref{name = pubsub_delete, label = '$delete', - min = 0, max = 1}, - #ref{name = pubsub_purge, label = '$purge', - min = 0, max = 1}, - #ref{name = pubsub_subscription, label = '$subscription', - min = 0, max = 1}, - #ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}, - #ref{name = pubsub_publish, label = '$publish', - min = 0, max = 1}]}). - --xml(pubsub_owner, - #elem{name = <<"pubsub">>, - xmlns = <<"http://jabber.org/protocol/pubsub#owner">>, - result = {pubsub_owner, '$affiliations', '$configure', '$default', - '$delete', '$purge', '$subscriptions'}, - refs = [#ref{name = pubsub_owner_affiliations, - label = '$affiliations', min = 0, max = 1}, - #ref{name = pubsub_configure, label = '$configure', - min = 0, max = 1}, - #ref{name = pubsub_default, label = '$default', - min = 0, max = 1}, - #ref{name = pubsub_delete, label = '$delete', - min = 0, max = 1}, - #ref{name = pubsub_purge, label = '$purge', - min = 0, max = 1}, - #ref{name = pubsub_subscriptions, - label = '$subscriptions', min = 0, max = 1}]}). - --type ps_error_type() :: 'closed-node' | 'configuration-required' | - 'invalid-jid' | 'invalid-options' | - 'invalid-payload' | 'invalid-subid' | - 'item-forbidden' | 'item-required' | 'jid-required' | - 'max-items-exceeded' | 'max-nodes-exceeded' | - 'nodeid-required' | 'not-in-roster-group' | - 'not-subscribed' | 'payload-too-big' | - 'payload-required' | 'pending-subscription' | - 'presence-subscription-required' | 'subid-required' | - 'too-many-subscriptions' | 'unsupported' | - 'unsupported-access-model'. --type ps_feature() :: 'access-authorize' | 'access-open' | - 'access-presence' | 'access-roster' | - 'access-whitelist' | 'auto-create' | - 'auto-subscribe' | 'collections' | 'config-node' | - 'create-and-configure' | 'create-nodes' | - 'delete-items' | 'delete-nodes' | - 'filtered-notifications' | 'get-pending' | - 'instant-nodes' | 'item-ids' | 'last-published' | - 'leased-subscription' | 'manage-subscriptions' | - 'member-affiliation' | 'meta-data' | - 'modify-affiliations' | 'multi-collection' | - 'multi-subscribe' | 'outcast-affiliation' | - 'persistent-items' | 'presence-notifications' | - 'presence-subscribe' | 'publish' | - 'publish-options' | 'publish-only-affiliation' | - 'publisher-affiliation' | 'purge-nodes' | - 'retract-items' | 'retrieve-affiliations' | - 'retrieve-default' | 'retrieve-items' | - 'retrieve-subscriptions' | 'subscribe' | - 'subscription-options' | 'subscription-notifications'. --record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}). --type ps_error() :: #ps_error{}. - --xml(pubsub_error_closed_node, - #elem{name = <<"closed-node">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'closed-node', '$_'}}). --xml(pubsub_error_configuration_required, - #elem{name = <<"configuration-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'configuration-required', '$_'}}). --xml(pubsub_error_invalid_jid, - #elem{name = <<"invalid-jid">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'invalid-jid', '$_'}}). --xml(pubsub_error_invalid_options, - #elem{name = <<"invalid-options">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'invalid-options', '$_'}}). --xml(pubsub_error_invalid_payload, - #elem{name = <<"invalid-payload">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'invalid-payload', '$_'}}). --xml(pubsub_error_invalid_subid, - #elem{name = <<"invalid-subid">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'invalid-subid', '$_'}}). --xml(pubsub_error_item_forbidden, - #elem{name = <<"item-forbidden">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'item-forbidden', '$_'}}). --xml(pubsub_error_item_required, - #elem{name = <<"item-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'item-required', '$_'}}). --xml(pubsub_error_jid_required, - #elem{name = <<"jid-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'jid-required', '$_'}}). --xml(pubsub_error_max_items_exceeded, - #elem{name = <<"max-items-exceeded">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'max-items-exceeded', '$_'}}). --xml(pubsub_error_max_nodes_exceeded, - #elem{name = <<"max-nodes-exceeded">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'max-nodes-exceeded', '$_'}}). --xml(pubsub_error_nodeid_required, - #elem{name = <<"nodeid-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'nodeid-required', '$_'}}). --xml(pubsub_error_not_in_roster_group, - #elem{name = <<"not-in-roster-group">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'not-in-roster-group', '$_'}}). --xml(pubsub_error_not_subscribed, - #elem{name = <<"not-subscribed">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'not-subscribed', '$_'}}). --xml(pubsub_error_payload_too_big, - #elem{name = <<"payload-too-big">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'payload-too-big', '$_'}}). --xml(pubsub_error_payload_required, - #elem{name = <<"payload-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'payload-required', '$_'}}). --xml(pubsub_error_pending_subscription, - #elem{name = <<"pending-subscription">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'pending-subscription', '$_'}}). --xml(pubsub_error_presence_subscription_required, - #elem{name = <<"presence-subscription-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'presence-subscription-required', '$_'}}). --xml(pubsub_error_subid_required, - #elem{name = <<"subid-required">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'subid-required', '$_'}}). --xml(pubsub_error_too_many_subscriptions, - #elem{name = <<"too-many-subscriptions">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'too-many-subscriptions', '$_'}}). --xml(pubsub_error_unsupported, - #elem{name = <<"unsupported">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'unsupported', '$feature'}, - attrs = [#attr{name = <<"feature">>, required = true, - dec = {dec_enum, [['access-authorize', - 'access-open', - 'access-presence', - 'access-roster', - 'access-whitelist', - 'auto-create', - 'auto-subscribe', - 'collections', - 'config-node', - 'create-and-configure', - 'create-nodes', - 'delete-items', - 'delete-nodes', - 'filtered-notifications', - 'get-pending', - 'instant-nodes', - 'item-ids', - 'last-published', - 'leased-subscription', - 'manage-subscriptions', - 'member-affiliation', - 'meta-data', - 'modify-affiliations', - 'multi-collection', - 'multi-subscribe', - 'outcast-affiliation', - 'persistent-items', - 'presence-notifications', - 'presence-subscribe', - 'publish', - 'publish-options', - 'publish-only-affiliation', - 'publisher-affiliation', - 'purge-nodes', - 'retract-items', - 'retrieve-affiliations', - 'retrieve-default', - 'retrieve-items', - 'retrieve-subscriptions', - 'subscribe', - 'subscription-options', - 'subscription-notifications']]}, - enc = {enc_enum, []}}]}). --xml(pubsub_error_unsupported_access_model, - #elem{name = <<"unsupported-access-model">>, - xmlns = <<"http://jabber.org/protocol/pubsub#errors">>, - result = {ps_error, 'unsupported-access-model', '$_'}}). - --xml(shim_header, - #elem{name = <<"header">>, - xmlns = <<"http://jabber.org/protocol/shim">>, - result = {'$name', '$cdata'}, - attrs = [#attr{name = <<"name">>, - required = true}]}). - --xml(shim_headers, - #elem{name = <<"headers">>, - xmlns = <<"http://jabber.org/protocol/shim">>, - result = {shim, '$headers'}, - refs = [#ref{name = shim_header, label = '$headers'}]}). - --record(chatstate, {type :: active | composing | gone | inactive | paused}). --type chatstate() :: #chatstate{}. - --xml(chatstate_active, - #elem{name = <<"active">>, - xmlns = <<"http://jabber.org/protocol/chatstates">>, - result = {chatstate, active}}). - --xml(chatstate_composing, - #elem{name = <<"composing">>, - xmlns = <<"http://jabber.org/protocol/chatstates">>, - result = {chatstate, composing}}). - --xml(chatstate_gone, - #elem{name = <<"gone">>, - xmlns = <<"http://jabber.org/protocol/chatstates">>, - result = {chatstate, gone}}). - --xml(chatstate_inactive, - #elem{name = <<"inactive">>, - xmlns = <<"http://jabber.org/protocol/chatstates">>, - result = {chatstate, inactive}}). - --xml(chatstate_paused, - #elem{name = <<"paused">>, - xmlns = <<"http://jabber.org/protocol/chatstates">>, - result = {chatstate, paused}}). - --xml(delay, - #elem{name = <<"delay">>, - xmlns = <<"urn:xmpp:delay">>, - result = {delay, '$stamp', '$from', '$desc'}, - cdata = #cdata{label = '$desc', default = <<"">>}, - attrs = [#attr{name = <<"stamp">>, - required = true, - dec = {dec_utc, []}, - enc = {enc_utc, []}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(bytestreams_streamhost, - #elem{name = <<"streamhost">>, - xmlns = <<"http://jabber.org/protocol/bytestreams">>, - result = {streamhost, '$jid', '$host', '$port'}, - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"host">>, - required = true}, - #attr{name = <<"port">>, - default = 1080, - dec = {dec_int, [0, 65535]}, - enc = {enc_int, []}}]}). - --xml(bytestreams_streamhost_used, - #elem{name = <<"streamhost-used">>, - xmlns = <<"http://jabber.org/protocol/bytestreams">>, - result = '$jid', - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(bytestreams_activate, - #elem{name = <<"activate">>, - xmlns = <<"http://jabber.org/protocol/bytestreams">>, - cdata = #cdata{enc = {enc_jid, []}, dec = {dec_jid, []}}, - result = '$cdata'}). - --xml(bytestreams, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/bytestreams">>, - result = {bytestreams, '$hosts', '$used', '$activate', - '$dstaddr', '$mode', '$sid'}, - attrs = [#attr{name = <<"dstaddr">>}, - #attr{name = <<"sid">>}, - #attr{name = <<"mode">>, - default = tcp, - dec = {dec_enum, [[tcp, udp]]}, - enc = {enc_enum, []}}], - refs = [#ref{name = bytestreams_streamhost, label = '$hosts'}, - #ref{name = bytestreams_streamhost_used, - min = 0, max = 1, label = '$used'}, - #ref{name = bytestreams_activate, - min = 0, max = 1, label = '$activate'}]}). - --xml(muc_history, - #elem{name = <<"history">>, - xmlns = <<"http://jabber.org/protocol/muc">>, - result = {muc_history, '$maxchars', '$maxstanzas', - '$seconds', '$since'}, - attrs = [#attr{name = <<"maxchars">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"maxstanzas">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"seconds">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"since">>, - dec = {dec_utc, []}, - enc = {enc_utc, []}}]}). - --xml(muc_reason, - #elem{name = <<"reason">>, - xmlns = [<<"http://jabber.org/protocol/muc#user">>, - <<"http://jabber.org/protocol/muc#admin">>, - <<"http://jabber.org/protocol/muc#owner">>], - result = '$cdata'}). - --xml(muc_user_decline, - #elem{name = <<"decline">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = {muc_decline, '$reason', '$from', '$to'}, - attrs = [#attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}], - refs = [#ref{name = muc_reason, min = 0, - default = <<"">>, - max = 1, label = '$reason'}]}). - --xml(muc_destroy, - #elem{name = <<"destroy">>, - xmlns = [<<"http://jabber.org/protocol/muc#user">>, - <<"http://jabber.org/protocol/muc#owner">>], - result = {muc_destroy, '$xmlns', '$jid', '$reason', '$password'}, - attrs = [#attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xmlns">>}], - refs = [#ref{name = muc_reason, min = 0, - default = <<"">>, - max = 1, label = '$reason'}, - #ref{name = muc_password, min = 0, max = 1, - label = '$password'}]}). - --xml(muc_user_invite, - #elem{name = <<"invite">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = {muc_invite, '$reason', '$from', '$to', '$continue'}, - attrs = [#attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}], - refs = [#ref{name = muc_reason, min = 0, default = <<"">>, - max = 1, label = '$reason'}, - #ref{name = muc_user_continue, min = 0, max = 1, - label = '$continue'}]}). - --xml(muc_user_actor, - #elem{name = <<"actor">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = {muc_actor, '$jid', '$nick'}, - attrs = [#attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>}]}). - --xml(muc_user_continue, - #elem{name = <<"continue">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = '$thread', - attrs = [#attr{name = <<"thread">>}]}). - --xml(muc_user_status, - #elem{name = <<"status">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = '$code', - attrs = [#attr{name = <<"code">>, - dec = {dec_int, [100, 999]}, - enc = {enc_int, []}}]}). - --xml(muc_user_item, - #elem{name = <<"item">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = {muc_item, '$actor', '$continue', '$reason', - '$affiliation', '$role', '$jid', '$nick'}, - refs = [#ref{name = muc_user_actor, - min = 0, max = 1, label = '$actor'}, - #ref{name = muc_user_continue, - min = 0, max = 1, label = '$continue'}, - #ref{name = muc_reason, default = <<"">>, - min = 0, max = 1, label = '$reason'}], - attrs = [#attr{name = <<"affiliation">>, - dec = {dec_enum, [[admin, member, none, - outcast, owner]]}, - enc = {enc_enum, []}}, - #attr{name = <<"role">>, - dec = {dec_enum, [[moderator, none, - participant, visitor]]}, - enc = {enc_enum, []}}, - #attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>}]}). - --xml(muc_user, - #elem{name = <<"x">>, - xmlns = <<"http://jabber.org/protocol/muc#user">>, - result = {muc_user, '$decline', '$destroy', '$invites', - '$items', '$status_codes', '$password'}, - refs = [#ref{name = muc_user_decline, min = 0, - max = 1, label = '$decline'}, - #ref{name = muc_destroy, min = 0, max = 1, - label = '$destroy'}, - #ref{name = muc_password, min = 0, max = 1, - label = '$password'}, - #ref{name = muc_user_invite, label = '$invites'}, - #ref{name = muc_user_item, label = '$items'}, - #ref{name = muc_user_status, label = '$status_codes'}]}). - --xml(muc_password, - #elem{name = <<"password">>, - xmlns = [<<"http://jabber.org/protocol/muc#owner">>, - <<"http://jabber.org/protocol/muc#user">>, - <<"http://jabber.org/protocol/muc">>], - result = '$cdata'}). - --xml(muc_owner, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/muc#owner">>, - result = {muc_owner, '$destroy', '$config', '$items'}, - refs = [#ref{name = muc_destroy, min = 0, max = 1, - label = '$destroy'}, - #ref{name = xdata, min = 0, max = 1, - label = '$config'}, - #ref{name = muc_owner_item, label = '$items'}]}). - --xml(muc_owner_item, - #elem{name = <<"item">>, - xmlns = <<"http://jabber.org/protocol/muc#owner">>, - result = {muc_item, '$actor', '$continue', '$reason', - '$affiliation', '$role', '$jid', '$nick'}, - refs = [#ref{name = muc_admin_actor, - min = 0, max = 1, label = '$actor'}, - #ref{name = muc_admin_continue, - min = 0, max = 1, label = '$continue'}, - #ref{name = muc_reason, default = <<"">>, - min = 0, max = 1, label = '$reason'}], - attrs = [#attr{name = <<"affiliation">>, - dec = {dec_enum, [[admin, member, none, - outcast, owner]]}, - enc = {enc_enum, []}}, - #attr{name = <<"role">>, - dec = {dec_enum, [[moderator, none, - participant, visitor]]}, - enc = {enc_enum, []}}, - #attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>}]}). - --xml(muc_admin_item, - #elem{name = <<"item">>, - xmlns = <<"http://jabber.org/protocol/muc#admin">>, - result = {muc_item, '$actor', '$continue', '$reason', - '$affiliation', '$role', '$jid', '$nick'}, - refs = [#ref{name = muc_admin_actor, - min = 0, max = 1, label = '$actor'}, - #ref{name = muc_admin_continue, - min = 0, max = 1, label = '$continue'}, - #ref{name = muc_reason, default = <<"">>, - min = 0, max = 1, label = '$reason'}], - attrs = [#attr{name = <<"affiliation">>, - dec = {dec_enum, [[admin, member, none, - outcast, owner]]}, - enc = {enc_enum, []}}, - #attr{name = <<"role">>, - dec = {dec_enum, [[moderator, none, - participant, visitor]]}, - enc = {enc_enum, []}}, - #attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>}]}). - --xml(muc_admin_actor, - #elem{name = <<"actor">>, - xmlns = <<"http://jabber.org/protocol/muc#admin">>, - result = {muc_actor, '$jid', '$nick'}, - attrs = [#attr{name = <<"jid">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>}]}). - --xml(muc_admin_continue, - #elem{name = <<"continue">>, - xmlns = <<"http://jabber.org/protocol/muc#admin">>, - result = '$thread', - attrs = [#attr{name = <<"thread">>}]}). - --xml(muc_admin, - #elem{name = <<"query">>, - xmlns = <<"http://jabber.org/protocol/muc#admin">>, - result = {muc_admin, '$items'}, - refs = [#ref{name = muc_admin_item, label = '$items'}]}). - --xml(muc, - #elem{name = <<"x">>, - xmlns = <<"http://jabber.org/protocol/muc">>, - result = {muc, '$history', '$password'}, - refs = [#ref{name = muc_history, min = 0, max = 1, - label = '$history'}, - #ref{name = muc_password, min = 0, max = 1, - label = '$password'}]}). - --xml(muc_unique, - #elem{name = <<"unique">>, - xmlns = <<"http://jabber.org/protocol/muc#unique">>, - result = {muc_unique, '$name'}, - cdata = #cdata{default = <<"">>, - label = '$name'}}). - --xml(x_conference, - #elem{name = <<"x">>, - xmlns = <<"jabber:x:conference">>, - result = {x_conference, '$jid', '$password', '$reason', - '$continue', '$thread'}, - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"password">>, default = <<"">>}, - #attr{name = <<"reason">>, default = <<"">>}, - #attr{name = <<"thread">>, default = <<"">>}, - #attr{name = <<"continue">>, - dec = {dec_bool, []}, - enc = {enc_bool, []}}]}). - --xml(muc_subscription, - #elem{name = <<"subscription">>, - xmlns = <<"urn:xmpp:mucsub:0">>, - result = '$jid', - attrs = [#attr{name = <<"jid">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(muc_subscriptions, - #elem{name = <<"subscriptions">>, - xmlns = <<"urn:xmpp:mucsub:0">>, - result = {muc_subscriptions, '$list'}, - refs = [#ref{name = muc_subscription, label = '$list'}]}). - --xml(muc_subscribe_event, - #elem{name = <<"event">>, - xmlns = <<"urn:xmpp:mucsub:0">>, - result = '$node', - attrs = [#attr{name = <<"node">>, required = true}]}). - --xml(muc_subscribe, - #elem{name = <<"subscribe">>, - xmlns = <<"urn:xmpp:mucsub:0">>, - result = {muc_subscribe, '$nick', '$events'}, - attrs = [#attr{name = <<"nick">>, required = true}], - refs = [#ref{name = muc_subscribe_event, label = '$events'}]}). - --xml(muc_unsubscribe, - #elem{name = <<"unsubscribe">>, - xmlns = <<"urn:xmpp:mucsub:0">>, - result = {muc_unsubscribe}}). - --xml(rsm_after, - #elem{name = <<"after">>, - xmlns = <<"http://jabber.org/protocol/rsm">>, - result = '$cdata'}). - --xml(rsm_before, - #elem{name = <<"before">>, - xmlns = <<"http://jabber.org/protocol/rsm">>, - cdata = #cdata{default = <<"">>}, - result = '$cdata'}). - --xml(rsm_last, - #elem{name = <<"last">>, - xmlns = <<"http://jabber.org/protocol/rsm">>, - result = '$cdata'}). - --xml(rsm_count, - #elem{name = <<"count">>, result = '$cdata', - xmlns = <<"http://jabber.org/protocol/rsm">>, - cdata = #cdata{dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}}). - --xml(rsm_index, - #elem{name = <<"index">>, result = '$cdata', - xmlns = <<"http://jabber.org/protocol/rsm">>, - cdata = #cdata{dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}}). - --xml(rsm_max, - #elem{name = <<"max">>, result = '$cdata', - xmlns = <<"http://jabber.org/protocol/rsm">>, - cdata = #cdata{dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}}). - --xml(rsm_first, - #elem{name = <<"first">>, - xmlns = <<"http://jabber.org/protocol/rsm">>, - result = {rsm_first, '$index', '$data'}, - cdata = #cdata{label = '$data'}, - attrs = [#attr{name = <<"index">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}]}). - --xml(rsm_set, - #elem{name = <<"set">>, - xmlns = <<"http://jabber.org/protocol/rsm">>, - result = {rsm_set, '$after', '$before', '$count', - '$first', '$index', '$last', '$max'}, - refs = [#ref{name = rsm_after, label = '$after', min = 0, max = 1}, - #ref{name = rsm_before, label = '$before', min = 0, max = 1}, - #ref{name = rsm_count, label = '$count', min = 0, max = 1}, - #ref{name = rsm_first, label = '$first', min = 0, max = 1}, - #ref{name = rsm_index, label = '$index', min = 0, max = 1}, - #ref{name = rsm_last, label = '$last', min = 0, max = 1}, - #ref{name = rsm_max, label = '$max', min = 0, max = 1}]}). - --xml(mam_start, - #elem{name = <<"start">>, - xmlns = <<"urn:xmpp:mam:tmp">>, - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_utc, []}, - enc = {enc_utc, []}}}). - --xml(mam_end, - #elem{name = <<"end">>, - xmlns = <<"urn:xmpp:mam:tmp">>, - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_utc, []}, - enc = {enc_utc, []}}}). - --xml(mam_with, - #elem{name = <<"with">>, - xmlns = <<"urn:xmpp:mam:tmp">>, - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}}). - --xml(mam_withtext, - #elem{name = <<"withtext">>, - xmlns = <<"urn:xmpp:mam:tmp">>, - result = '$cdata', - cdata = #cdata{required = true}}). - --xml(mam_query, - #elem{name = <<"query">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = {mam_query, '$xmlns', '$id', '$start', '$end', '$with', - '$withtext', '$rsm', '$xdata'}, - attrs = [#attr{name = <<"queryid">>, label = '$id'}, - #attr{name = <<"xmlns">>}], - refs = [#ref{name = mam_start, min = 0, max = 1, label = '$start'}, - #ref{name = mam_end, min = 0, max = 1, label = '$end'}, - #ref{name = mam_with, min = 0, max = 1, label = '$with'}, - #ref{name = mam_withtext, min = 0, max = 1, label = '$withtext'}, - #ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}, - #ref{name = xdata, min = 0, max = 1, label = '$xdata'}]}). - --xml(mam_archived, - #elem{name = <<"archived">>, - xmlns = <<"urn:xmpp:mam:tmp">>, - result = {mam_archived, '$by', '$id'}, - attrs = [#attr{name = <<"id">>}, - #attr{name = <<"by">>, - required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}]}). - --xml(mam_result, - #elem{name = <<"result">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = {mam_result, '$xmlns', '$queryid', '$id', '$_els'}, - attrs = [#attr{name = <<"queryid">>}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"id">>}]}). - --xml(mam_jid, - #elem{name = <<"jid">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_jid, []}, - enc = {enc_jid, []}}}). - --xml(mam_never, - #elem{name = <<"never">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = '$jids', - refs = [#ref{name = mam_jid, label = '$jids'}]}). - --xml(mam_always, - #elem{name = <<"always">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = '$jids', - refs = [#ref{name = mam_jid, label = '$jids'}]}). - --xml(mam_prefs, - #elem{name = <<"prefs">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>], - result = {mam_prefs, '$xmlns', '$default', '$always', '$never'}, - attrs = [#attr{name = <<"default">>, - dec = {dec_enum, [[always, never, roster]]}, - enc = {enc_enum, []}}, - #attr{name = <<"xmlns">>}], - refs = [#ref{name = mam_always, label = '$always', - min = 0, max = 1}, - #ref{name = mam_never, label = '$never', - min = 0, max = 1}]}). - --xml(mam_fin, - #elem{name = <<"fin">>, - xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>], - result = {mam_fin, '$xmlns', '$id', '$rsm', '$stable', '$complete'}, - attrs = [#attr{name = <<"queryid">>, label = '$id'}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"stable">>, label = '$stable', - dec = {dec_bool, []}, - enc = {enc_bool, []}}, - #attr{name = <<"complete">>, label = '$complete', - dec = {dec_bool, []}, - enc = {enc_bool, []}}], - refs = [#ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}]}). - --xml(forwarded, - #elem{name = <<"forwarded">>, - xmlns = <<"urn:xmpp:forward:0">>, - result = {forwarded, '$delay', '$_els'}, - refs = [#ref{name = delay, min = 0, - max = 1, label = '$delay'}]}). - --xml(carbons_disable, - #elem{name = <<"disable">>, - xmlns = <<"urn:xmpp:carbons:2">>, - result = {carbons_disable}}). - --xml(carbons_enable, - #elem{name = <<"enable">>, - xmlns = <<"urn:xmpp:carbons:2">>, - result = {carbons_enable}}). - --xml(carbons_private, - #elem{name = <<"private">>, - xmlns = <<"urn:xmpp:carbons:2">>, - result = {carbons_private}}). - --xml(carbons_received, - #elem{name = <<"received">>, - xmlns = <<"urn:xmpp:carbons:2">>, - result = {carbons_received, '$forwarded'}, - refs = [#ref{name = forwarded, min = 1, - max = 1, label = '$forwarded'}]}). - --xml(carbons_sent, - #elem{name = <<"sent">>, - xmlns = <<"urn:xmpp:carbons:2">>, - result = {carbons_sent, '$forwarded'}, - refs = [#ref{name = forwarded, min = 1, - max = 1, label = '$forwarded'}]}). - --xml(feature_csi, - #elem{name = <<"csi">>, - xmlns = <<"urn:xmpp:csi:0">>, - result = {feature_csi, '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}]}). - --record(csi, {type :: active | inactive}). --type csi() :: #csi{}. - --xml(csi_active, - #elem{name = <<"active">>, - xmlns = <<"urn:xmpp:csi:0">>, - result = {csi, active}}). - --xml(csi_inactive, - #elem{name = <<"inactive">>, - xmlns = <<"urn:xmpp:csi:0">>, - result = {csi, inactive}}). - --xml(feature_sm, - #elem{name = <<"sm">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {feature_sm, '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}]}). - --xml(sm_enable, - #elem{name = <<"enable">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_enable, '$max', '$resume', '$xmlns'}, - attrs = [#attr{name = <<"max">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"resume">>, - default = false, - dec = {dec_bool, []}, - enc = {enc_bool, []}}]}). - --xml(sm_enabled, - #elem{name = <<"enabled">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_enabled, '$id', '$location', '$max', '$resume', '$xmlns'}, - attrs = [#attr{name = <<"id">>}, - #attr{name = <<"location">>}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"max">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"resume">>, - default = false, - dec = {dec_bool, []}, - enc = {enc_bool, []}}]}). - --xml(sm_resume, - #elem{name = <<"resume">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_resume, '$h', '$previd', '$xmlns'}, - attrs = [#attr{name = <<"h">>, - required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"previd">>, - required = true}]}). - --xml(sm_resumed, - #elem{name = <<"resumed">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_resumed, '$h', '$previd', '$xmlns'}, - attrs = [#attr{name = <<"h">>, - required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"previd">>, - required = true}]}). - --xml(sm_r, - #elem{name = <<"r">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_r, '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}]}). - --xml(sm_a, - #elem{name = <<"a">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_a, '$h', '$xmlns'}, - attrs = [#attr{name = <<"h">>, - required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"xmlns">>}]}). - --xml(sm_failed, - #elem{name = <<"failed">>, - xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>], - result = {sm_failed, '$reason', '$h', '$xmlns'}, - attrs = [#attr{name = <<"h">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"xmlns">>}], - refs = [#ref{name = error_bad_request, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_conflict, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_feature_not_implemented, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_forbidden, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_gone, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_internal_server_error, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_item_not_found, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_jid_malformed, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_acceptable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_allowed, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_not_authorized, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_policy_violation, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_recipient_unavailable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_redirect, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_registration_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_remote_server_not_found, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_remote_server_timeout, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_resource_constraint, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_service_unavailable, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_subscription_required, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_undefined_condition, - min = 0, max = 1, label = '$reason'}, - #ref{name = error_unexpected_request, - min = 0, max = 1, label = '$reason'}]}). - --xml(offline_purge, - #elem{name = <<"purge">>, - xmlns = <<"http://jabber.org/protocol/offline">>, - result = true}). - --xml(offline_fetch, - #elem{name = <<"fetch">>, - xmlns = <<"http://jabber.org/protocol/offline">>, - result = true}). - --xml(offline_item, - #elem{name = <<"item">>, - xmlns = <<"http://jabber.org/protocol/offline">>, - result = {offline_item, '$node', '$action'}, - attrs = [#attr{name = <<"node">>}, - #attr{name = <<"action">>, - dec = {dec_enum, [[view, remove]]}, - enc = {enc_enum, []}}]}). - --xml(offline, - #elem{name = <<"offline">>, - xmlns = <<"http://jabber.org/protocol/offline">>, - result = {offline, '$items', '$purge', '$fetch'}, - refs = [#ref{name = offline_purge, min = 0, max = 1, - label = '$purge', default = false}, - #ref{name = offline_fetch, min = 0, max = 1, - label = '$fetch', default = false}, - #ref{name = offline_item, min = 0, label = '$items'}]}). - --xml(mix_subscribe, - #elem{name = <<"subscribe">>, - xmlns = <<"urn:xmpp:mix:0">>, - result = '$node', - attrs = [#attr{name = <<"node">>, - required = true, - label = '$node'}]}). - --xml(mix_join, - #elem{name = <<"join">>, - xmlns = <<"urn:xmpp:mix:0">>, - result = {mix_join, '$jid', '$subscribe'}, - attrs = [#attr{name = <<"jid">>, - label = '$jid', - dec = {dec_jid, []}, - enc = {enc_jid, []}}], - refs = [#ref{name = mix_subscribe, min = 0, label = '$subscribe'}]}). - --xml(mix_leave, - #elem{name = <<"leave">>, - xmlns = <<"urn:xmpp:mix:0">>, - result = {mix_leave}}). - --xml(mix_participant, - #elem{name = <<"participant">>, - xmlns = <<"urn:xmpp:mix:0">>, - result = {mix_participant, '$jid', '$nick'}, - attrs = [#attr{name = <<"jid">>, - required = true, - label = '$jid', - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"nick">>, - label = '$nick'}]}). - --record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' | - 'no-permanent-store' | 'no-permanent-storage'}). --type hint() :: #hint{}. - --xml(hint_no_copy, - #elem{name = <<"no-copy">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'no-copy'}}). - --xml(hint_no_store, - #elem{name = <<"no-store">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'no-store'}}). - --xml(hint_no_storage, - #elem{name = <<"no-storage">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'no-storage'}}). - --xml(hint_store, - #elem{name = <<"store">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'store'}}). - --xml(hint_no_permanent_store, - #elem{name = <<"no-permanent-store">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'no-permanent-store'}}). - --xml(hint_no_permanent_storage, - #elem{name = <<"no-permanent-storage">>, - xmlns = <<"urn:xmpp:hints">>, - result = {hint, 'no-permanent-storage'}}). - --xml(search_instructions, - #elem{name = <<"instructions">>, - xmlns = <<"jabber:iq:search">>, - result = '$cdata'}). - --xml(search_first, - #elem{name = <<"first">>, - xmlns = <<"jabber:iq:search">>, - cdata = #cdata{default = <<"">>}, - result = '$cdata'}). --xml(search_last, - #elem{name = <<"last">>, - xmlns = <<"jabber:iq:search">>, - cdata = #cdata{default = <<"">>}, - result = '$cdata'}). --xml(search_nick, - #elem{name = <<"nick">>, - xmlns = <<"jabber:iq:search">>, - cdata = #cdata{default = <<"">>}, - result = '$cdata'}). --xml(search_email, - #elem{name = <<"email">>, - xmlns = <<"jabber:iq:search">>, - cdata = #cdata{default = <<"">>}, - result = '$cdata'}). - --xml(search_item, - #elem{name = <<"item">>, - xmlns = <<"jabber:iq:search">>, - result = {search_item, '$jid', '$first', '$last', '$nick', '$email'}, - attrs = [#attr{name = <<"jid">>, - required = true, - enc = {enc_jid, []}, - dec = {dec_jid, []}}], - refs = [#ref{name = search_first, min = 0, max = 1, - label = '$first'}, - #ref{name = search_last, min = 0, max = 1, - label = '$last'}, - #ref{name = search_nick, min = 0, max = 1, - label = '$nick'}, - #ref{name = search_email, min = 0, max = 1, - label = '$email'}]}). - --xml(search, - #elem{name = <<"query">>, - xmlns = <<"jabber:iq:search">>, - result = {search, '$instructions', '$first', '$last', - '$nick', '$email', '$items', '$xdata'}, - refs = [#ref{name = search_instructions, min = 0, max = 1, - label = '$instructions'}, - #ref{name = search_first, min = 0, max = 1, - label = '$first'}, - #ref{name = search_last, min = 0, max = 1, - label = '$last'}, - #ref{name = search_nick, min = 0, max = 1, - label = '$nick'}, - #ref{name = search_email, min = 0, max = 1, - label = '$email'}, - #ref{name = search_item, label = '$items'}, - #ref{name = xdata, min = 0, max = 1, - label = '$xdata'}]}). - --xml(xevent_offline, - #elem{name = <<"offline">>, - xmlns = <<"jabber:x:event">>, - result = true}). --xml(xevent_delivered, - #elem{name = <<"delivered">>, - xmlns = <<"jabber:x:event">>, - result = true}). --xml(xevent_displayed, - #elem{name = <<"displayed">>, - xmlns = <<"jabber:x:event">>, - result = true}). --xml(xevent_composing, - #elem{name = <<"composing">>, - xmlns = <<"jabber:x:event">>, - result = true}). --xml(xevent_id, - #elem{name = <<"id">>, - xmlns = <<"jabber:x:event">>, - cdata = #cdata{}, - result = '$cdata'}). - --xml(xevent, - #elem{name = <<"x">>, - xmlns = <<"jabber:x:event">>, - result = {xevent, '$offline', '$delivered', '$displayed', - '$composing', '$id'}, - refs = [#ref{name = xevent_offline, min = 0, max = 1, - label = '$offline', default = false}, - #ref{name = xevent_delivered, min = 0, max = 1, - label = '$delivered', default = false}, - #ref{name = xevent_displayed, min = 0, max = 1, - label = '$displayed', default = false}, - #ref{name = xevent_composing, min = 0, max = 1, - label = '$composing', default = false}, - #ref{name = xevent_id, min = 0, max = 1, - label = '$id'}]}). - --xml(expire, - #elem{name = <<"x">>, - xmlns = <<"jabber:x:expire">>, - result = {expire, '$seconds', '$stored'}, - attrs = [#attr{name = <<"seconds">>, - required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"stored">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}]}). - --xml(nick, - #elem{name = <<"nick">>, - xmlns = <<"http://jabber.org/protocol/nick">>, - result = {nick, '$name'}, - cdata = #cdata{label = '$name', - required = true}}). - --xml(address, - #elem{name = <<"address">>, - xmlns = <<"http://jabber.org/protocol/address">>, - result = {address, '$type', '$jid', '$desc', '$node', '$delivered'}, - attrs = [#attr{name = <<"type">>, - required = true, - dec = {dec_enum, [[bcc, cc, noreply, ofrom, - replyroom, replyto, to]]}, - enc = {enc_enum, []}}, - #attr{name = <<"jid">>, - enc = {enc_jid, []}, - dec = {dec_jid, []}}, - #attr{name = <<"desc">>}, - #attr{name = <<"node">>}, - #attr{name = <<"delivered">>, - enc = {enc_bool, []}, - dec = {dec_bool, []}}]}). - --xml(addresses, - #elem{name = <<"addresses">>, - xmlns = <<"http://jabber.org/protocol/address">>, - result = {addresses, '$list'}, - %% TODO: 'min' should be '1', but this is not implemented - refs = [#ref{name = address, label = '$list'}]}). - --xml(stanza_id, - #elem{name = <<"stanza-id">>, - xmlns = <<"urn:xmpp:sid:0">>, - result = {stanza_id, '$by', '$id'}, - attrs = [#attr{name = <<"id">>, required = true}, - #attr{name = <<"by">>, required = true, - enc = {enc_jid, []}, - dec = {dec_jid, []}}]}). - --xml(client_id, - #elem{name = <<"client-id">>, - xmlns = <<"urn:xmpp:sid:0">>, - result = {client_id, '$id'}, - attrs = [#attr{name = <<"id">>, required = true}]}). - --xml(adhoc_command_prev, - #elem{name = <<"prev">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = true}). --xml(adhoc_command_next, - #elem{name = <<"next">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = true}). --xml(adhoc_command_complete, - #elem{name = <<"complete">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = true}). - --xml(adhoc_command_actions, - #elem{name = <<"actions">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = {adhoc_actions, '$execute', '$prev', '$next', '$complete'}, - attrs = [#attr{name = <<"execute">>, - dec = {dec_enum, [[complete, next, prev]]}, - enc = {enc_enum, []}}], - refs = [#ref{name = adhoc_command_prev, min = 0, max = 1, - default = false, label = '$prev'}, - #ref{name = adhoc_command_next, min = 0, max = 1, - default = false, label = '$next'}, - #ref{name = adhoc_command_complete, min = 0, max = 1, - default = false, label = '$complete'}]}). - --xml(adhoc_command_notes, - #elem{name = <<"note">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = {adhoc_note, '$type', '$data'}, - attrs = [#attr{name = <<"type">>, default = info, - dec = {dec_enum, [[info, warn, error]]}, - enc = {enc_enum, []}}], - cdata = #cdata{default = <<"">>, label = '$data'}}). - --xml(adhoc_command, - #elem{name = <<"command">>, - xmlns = <<"http://jabber.org/protocol/commands">>, - result = {adhoc_command, '$node', '$action', '$sid', - '$status', '$lang', '$actions', '$notes', '$xdata'}, - attrs = [#attr{name = <<"node">>, required = true}, - #attr{name = <<"xml:lang">>, label = '$lang'}, - #attr{name = <<"sessionid">>, label = '$sid'}, - #attr{name = <<"status">>, - dec = {dec_enum, [[canceled, completed, executing]]}, - enc = {enc_enum, []}}, - #attr{name = <<"action">>, default = execute, - dec = {dec_enum, [[cancel, complete, - execute, next, prev]]}, - enc = {enc_enum, []}}], - refs = [#ref{name = adhoc_command_actions, min = 0, max = 1, - label = '$actions'}, - #ref{name = xdata, min = 0, max = 1}, - #ref{name = adhoc_command_notes, label = '$notes'}]}). - --xml(db_result, - #elem{name = <<"db:result">>, - xmlns = <<"jabber:server">>, - result = {db_result, '$from', '$to', '$type', '$key', '$_els'}, - cdata = #cdata{default = <<"">>, label = '$key'}, - attrs = [#attr{name = <<"from">>, required = true, - dec = {nameprep, []}, enc = {nameprep, []}}, - #attr{name = <<"to">>, required = true, - dec = {nameprep, []}, enc = {nameprep, []}}, - #attr{name = <<"type">>, - dec = {dec_enum, [[valid, invalid, error]]}, - enc = {enc_enum, []}}]}). - --xml(db_verify, - #elem{name = <<"db:verify">>, - xmlns = <<"jabber:server">>, - result = {db_verify, '$from', '$to', '$id', '$type', '$key', '$_els'}, - cdata = #cdata{default = <<"">>, label = '$key'}, - attrs = [#attr{name = <<"from">>, required = true, - dec = {nameprep, []}, enc = {nameprep, []}}, - #attr{name = <<"to">>, required = true, - dec = {nameprep, []}, enc = {nameprep, []}}, - #attr{name = <<"id">>, required = true}, - #attr{name = <<"type">>, - dec = {dec_enum, [[valid, invalid, error]]}, - enc = {enc_enum, []}}]}). - --xml(handshake, - #elem{name = <<"handshake">>, - xmlns = <<"jabber:component:accept">>, - result = {handshake, '$data'}, - cdata = #cdata{default = <<"">>, label = '$data'}}). - --xml(stream_start, - #elem{name = <<"stream:stream">>, - xmlns = [<<"jabber:client">>, <<"jabber:server">>, - <<"jabber:component:accept">>], - result = {stream_start, '$from', '$to', '$id', - '$version', '$xmlns', '$stream_xmlns', - '$db_xmlns', '$lang'}, - attrs = [#attr{name = <<"from">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"to">>, - dec = {dec_jid, []}, - enc = {enc_jid, []}}, - #attr{name = <<"xmlns">>}, - #attr{name = <<"xmlns:stream">>, - label = '$stream_xmlns', - default = <<"">>}, - #attr{name = <<"xmlns:db">>, - label = '$db_xmlns', - default = <<"">>}, - #attr{name = <<"xml:lang">>, label = '$lang', - default = <<"">>}, - #attr{name = <<"version">>, - dec = {dec_version, []}, - enc = {enc_version, []}}, - #attr{name = <<"id">>, default = <<"">>}]}). - --xml(bob_data, - #elem{name = <<"data">>, - xmlns = <<"urn:xmpp:bob">>, - result = {bob_data, '$cid', '$max-age', '$type', '$data'}, - attrs = [#attr{name = <<"cid">>, required = true}, - #attr{name = <<"max-age">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"type">>}], - cdata = #cdata{label = '$data', default = <<"">>, - dec = {base64, decode, []}, - enc = {base64, encode, []}}}). - --xml(captcha, - #elem{name = <<"captcha">>, - xmlns = <<"urn:xmpp:captcha">>, - result = {xcaptcha, '$xdata'}, - refs = [#ref{name = xdata, min = 1, max = 1}]}). - --xml(media_uri, - #elem{name = <<"uri">>, - xmlns = <<"urn:xmpp:media-element">>, - result = {media_uri, '$type', '$uri'}, - attrs = [#attr{name = <<"type">>, required = true}], - cdata = #cdata{label = '$uri', default = <<"">>}}). - --xml(media, - #elem{name = <<"media">>, - xmlns = <<"urn:xmpp:media-element">>, - result = {media, '$height', '$width', '$uri'}, - attrs = [#attr{name = <<"height">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"width">>, - dec = {dec_int, [0, inifinity]}, - enc = {enc_int, []}}], - refs = [#ref{name = media_uri, label = '$uri'}]}). - --xml(oob_url, - #elem{name = <<"url">>, - xmlns = <<"jabber:x:oob">>, - result = '$cdata', - cdata = #cdata{required = true}}). - --xml(oob_desc, - #elem{name = <<"desc">>, - xmlns = <<"jabber:x:oob">>, - result = '$cdata', - cdata = #cdata{default = <<"">>}}). - --xml(oob_x, - #elem{name = <<"x">>, - xmlns = <<"jabber:x:oob">>, - result = {oob_x, '$url', '$desc', '$sid'}, - attrs = [#attr{name = <<"sid">>, default = <<"">>}], - refs = [#ref{name = oob_url, min = 1, max = 1, - label = '$url'}, - #ref{name = oob_desc, default = <<"">>, - min = 0, max = 1, label = '$desc'}]}). - --xml(sic_ip, - #elem{name = <<"ip">>, - xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_ip, []}, - enc = {enc_ip, []}}}). - --xml(sip_port, - #elem{name = <<"port">>, - xmlns = <<"urn:xmpp:sic:1">>, - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_int, [0, 65535]}, - enc = {enc_int, []}}}). - --xml(sic, - #elem{name = <<"address">>, - xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], - result = {sic, '$ip', '$port', '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}], - refs = [#ref{name = sic_ip, min = 0, max = 1, label = '$ip'}, - #ref{name = sip_port, min = 0, max = 1, label = '$port'}]}). - --xml(upload_filename, - #elem{name = <<"filename">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = '$cdata', - cdata = #cdata{required = true}}). - --xml(upload_size, - #elem{name = <<"size">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = '$cdata', - cdata = #cdata{required = true, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}}). - --xml(upload_content_type, - #elem{name = <<"content-type">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = '$cdata', - cdata = #cdata{default = <<"">>}}). - --xml(upload_request, - #elem{name = <<"request">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = {upload_request, '$filename', '$size', - '$content-type', '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}], - refs = [#ref{name = upload_filename, label = '$filename', - min = 1, max = 1}, - #ref{name = upload_size, label = '$size', min = 1, max = 1}, - #ref{name = upload_content_type, label = '$content-type', - min = 0, max = 1, default = <<"">>}]}). - --xml(upload_get, - #elem{name = <<"get">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = '$cdata', - cdata = #cdata{required = true}}). - --xml(upload_put, - #elem{name = <<"put">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = '$cdata', - cdata = #cdata{required = true}}). - --xml(upload_slot, - #elem{name = <<"slot">>, - xmlns = [<<"urn:xmpp:http:upload">>, - <<"eu:siacs:conversations:http:upload">>], - result = {upload_slot, '$get', '$put', '$xmlns'}, - attrs = [#attr{name = <<"xmlns">>}], - refs = [#ref{name = upload_get, min = 0, max = 1, label = '$get'}, - #ref{name = upload_put, min = 0, max = 1, label = '$put'}]}). - --xml(thumbnail, - #elem{name = <<"thumbnail">>, - xmlns = <<"urn:xmpp:thumbs:1">>, - result = {thumbnail, '$uri', '$media-type', '$width', '$height'}, - attrs = [#attr{name = <<"uri">>, required = true}, - #attr{name = <<"media-type">>, default = <<"">>}, - #attr{name = <<"width">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}, - #attr{name = <<"height">>, - dec = {dec_int, [0, infinity]}, - enc = {enc_int, []}}]}). - --spec dec_tzo(_) -> {integer(), integer()}. -dec_tzo(Val) -> - [H1, M1] = str:tokens(Val, <<":">>), - H = binary_to_integer(H1), - M = binary_to_integer(M1), - if H >= -12, H =< 12, M >= 0, M < 60 -> - {H, M} - end. - -enc_tzo({H, M}) -> - Sign = if H >= 0 -> - <<>>; - true -> - <<"-">> - end, - list_to_binary([Sign, io_lib:format("~2..0w:~2..0w", [H, M])]). - --spec dec_utc(_) -> erlang:timestamp(). -dec_utc(Val) -> - {_, _, _} = jlib:datetime_string_to_timestamp(Val). - -enc_utc(Val) -> - jlib:now_to_utc_string(Val). - --spec dec_jid(_) -> jid:jid(). -dec_jid(Val) -> - case jid:from_string(Val) of - error -> - erlang:error(badarg); - J -> - J - end. - -enc_jid(J) -> - jid:to_string(J). - --spec resourceprep(_) -> binary(). -resourceprep(R) -> - case jid:resourceprep(R) of - error -> - erlang:error(badarg); - R1 -> - R1 - end. - --spec nameprep(_) -> binary(). -nameprep(S) -> - case jid:nameprep(S) of - error -> - erlang:error(badarg); - S1 -> - S1 - end. - --spec dec_bool(_) -> boolean(). -dec_bool(<<"false">>) -> false; -dec_bool(<<"0">>) -> false; -dec_bool(<<"true">>) -> true; -dec_bool(<<"1">>) -> true. - -enc_bool(false) -> <<"false">>; -enc_bool(true) -> <<"true">>. - -join([], _Sep) -> <<>>; -join([H | T], Sep) -> - <> || X <- T >>)/binary>>. - --spec dec_ip(_) -> inet:ip_address(). -dec_ip(S) -> - {ok, Addr} = inet_parse:address(binary_to_list(S)), - Addr. - -enc_ip({0,0,0,0,0,16#ffff,A,B}) -> - enc_ip({(A bsr 8) band 16#ff, A band 16#ff, - (B bsr 8) band 16#ff, B band 16#ff}); -enc_ip(Addr) -> - list_to_binary(inet_parse:ntoa(Addr)). - --spec re:split(_, _) -> [binary()]. --spec base64:decode(_) -> binary(). - --spec dec_host_port(_) -> binary() | inet:ip_address() | - {binary() | inet:ip_address(), non_neg_integer()}. -dec_host_port(<<$[, T/binary>>) -> - [IP, <<$:, Port/binary>>] = binary:split(T, <<$]>>), - {dec_ip(IP), dec_int(Port, 0, 65535)}; -dec_host_port(S) -> - case binary:split(S, <<$:>>) of - [S] -> - try dec_ip(S) catch _:_ -> S end; - [S, P] -> - {try dec_ip(S) catch _:_ -> S end, dec_int(P, 0, 65535)} - end. - -enc_host_port(Host) when is_binary(Host) -> - Host; -enc_host_port({{_,_,_,_,_,_,_,_} = IPv6, Port}) -> - enc_host_port({<<$[, (enc_ip(IPv6))/binary, $]>>, Port}); -enc_host_port({{_,_,_,_} = IPv4, Port}) -> - enc_host_port({enc_ip(IPv4), Port}); -enc_host_port({Host, Port}) -> - <>; -enc_host_port(Addr) -> - enc_ip(Addr). - --spec dec_version(_) -> {non_neg_integer(), non_neg_integer()}. -dec_version(S) -> - [Major, Minor] = binary:split(S, <<$.>>), - {binary_to_integer(Major), binary_to_integer(Minor)}. - -enc_version({Maj, Min}) -> - <<(integer_to_binary(Maj))/binary, $., (integer_to_binary(Min))/binary>>. - -%% Local Variables: -%% mode: erlang -%% End: -%% vim: set filetype=erlang tabstop=8: -- cgit v1.2.3