mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-26 16:26:24 +01:00
Rewrite mod_mam and mod_muc to use XML generator
This commit is contained in:
parent
5d90292849
commit
179fcd9521
@ -71,6 +71,7 @@
|
|||||||
-type config() :: #config{}.
|
-type config() :: #config{}.
|
||||||
|
|
||||||
-type role() :: moderator | participant | visitor | none.
|
-type role() :: moderator | participant | visitor | none.
|
||||||
|
-type affiliation() :: admin | member | outcast | owner | none.
|
||||||
|
|
||||||
-record(user,
|
-record(user,
|
||||||
{
|
{
|
||||||
@ -120,5 +121,3 @@
|
|||||||
host = <<>> :: binary() | '_' | '$2'}).
|
host = <<>> :: binary() | '_' | '$2'}).
|
||||||
|
|
||||||
-type muc_online_users() :: #muc_online_users{}.
|
-type muc_online_users() :: #muc_online_users{}.
|
||||||
|
|
||||||
-type muc_room_state() :: #state{}.
|
|
||||||
|
@ -11,13 +11,20 @@
|
|||||||
-record(csi, {type :: active | inactive}).
|
-record(csi, {type :: active | inactive}).
|
||||||
-type csi() :: #csi{}.
|
-type csi() :: #csi{}.
|
||||||
|
|
||||||
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
|
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' |
|
||||||
'store' | 'no-permanent-store'}).
|
'no-permanent-store' | 'no-permanent-storage'}).
|
||||||
-type hint() :: #hint{}.
|
-type hint() :: #hint{}.
|
||||||
|
|
||||||
-record(feature_register, {}).
|
-record(feature_register, {}).
|
||||||
-type feature_register() :: #feature_register{}.
|
-type feature_register() :: #feature_register{}.
|
||||||
|
|
||||||
|
-record(address, {type :: 'bcc' | 'cc' | 'noreply' | 'ofrom' | 'replyroom' | 'replyto' | 'to',
|
||||||
|
jid :: any(),
|
||||||
|
desc :: binary(),
|
||||||
|
node :: binary(),
|
||||||
|
delivered :: any()}).
|
||||||
|
-type address() :: #address{}.
|
||||||
|
|
||||||
-record(sasl_success, {text :: any()}).
|
-record(sasl_success, {text :: any()}).
|
||||||
-type sasl_success() :: #sasl_success{}.
|
-type sasl_success() :: #sasl_success{}.
|
||||||
|
|
||||||
@ -55,6 +62,9 @@
|
|||||||
stored :: non_neg_integer()}).
|
stored :: non_neg_integer()}).
|
||||||
-type expire() :: #expire{}.
|
-type expire() :: #expire{}.
|
||||||
|
|
||||||
|
-record(muc_unsubscribe, {}).
|
||||||
|
-type muc_unsubscribe() :: #muc_unsubscribe{}.
|
||||||
|
|
||||||
-record(pubsub_unsubscribe, {node :: binary(),
|
-record(pubsub_unsubscribe, {node :: binary(),
|
||||||
jid :: any(),
|
jid :: any(),
|
||||||
subid :: binary()}).
|
subid :: binary()}).
|
||||||
@ -81,7 +91,7 @@
|
|||||||
type :: 'member' | 'none' | 'outcast' | 'owner' | 'publish-only' | 'publisher'}).
|
type :: 'member' | 'none' | 'outcast' | 'owner' | 'publish-only' | 'publisher'}).
|
||||||
-type pubsub_affiliation() :: #pubsub_affiliation{}.
|
-type pubsub_affiliation() :: #pubsub_affiliation{}.
|
||||||
|
|
||||||
-record(muc_decline, {reason :: binary(),
|
-record(muc_decline, {reason = <<>> :: 'undefined' | binary(),
|
||||||
from :: any(),
|
from :: any(),
|
||||||
to :: any()}).
|
to :: any()}).
|
||||||
-type muc_decline() :: #muc_decline{}.
|
-type muc_decline() :: #muc_decline{}.
|
||||||
@ -90,9 +100,20 @@
|
|||||||
xmlns :: binary()}).
|
xmlns :: binary()}).
|
||||||
-type sm_a() :: #sm_a{}.
|
-type sm_a() :: #sm_a{}.
|
||||||
|
|
||||||
|
-record(muc_subscribe, {nick :: binary(),
|
||||||
|
events = [] :: [binary()]}).
|
||||||
|
-type muc_subscribe() :: #muc_subscribe{}.
|
||||||
|
|
||||||
|
-record(stanza_id, {by :: any(),
|
||||||
|
id :: binary()}).
|
||||||
|
-type stanza_id() :: #stanza_id{}.
|
||||||
|
|
||||||
-record(starttls_proceed, {}).
|
-record(starttls_proceed, {}).
|
||||||
-type starttls_proceed() :: #starttls_proceed{}.
|
-type starttls_proceed() :: #starttls_proceed{}.
|
||||||
|
|
||||||
|
-record(client_id, {id :: binary()}).
|
||||||
|
-type client_id() :: #client_id{}.
|
||||||
|
|
||||||
-record(sm_resumed, {h :: non_neg_integer(),
|
-record(sm_resumed, {h :: non_neg_integer(),
|
||||||
previd :: binary(),
|
previd :: binary(),
|
||||||
xmlns :: binary()}).
|
xmlns :: binary()}).
|
||||||
@ -116,9 +137,19 @@
|
|||||||
-record(gone, {uri :: binary()}).
|
-record(gone, {uri :: binary()}).
|
||||||
-type gone() :: #gone{}.
|
-type gone() :: #gone{}.
|
||||||
|
|
||||||
|
-record(x_conference, {jid :: any(),
|
||||||
|
password = <<>> :: binary(),
|
||||||
|
reason = <<>> :: binary(),
|
||||||
|
continue :: any(),
|
||||||
|
thread = <<>> :: binary()}).
|
||||||
|
-type x_conference() :: #x_conference{}.
|
||||||
|
|
||||||
-record(private, {xml_els = [] :: [any()]}).
|
-record(private, {xml_els = [] :: [any()]}).
|
||||||
-type private() :: #private{}.
|
-type private() :: #private{}.
|
||||||
|
|
||||||
|
-record(nick, {name :: binary()}).
|
||||||
|
-type nick() :: #nick{}.
|
||||||
|
|
||||||
-record(p1_ack, {}).
|
-record(p1_ack, {}).
|
||||||
-type p1_ack() :: #p1_ack{}.
|
-type p1_ack() :: #p1_ack{}.
|
||||||
|
|
||||||
@ -163,6 +194,9 @@
|
|||||||
error = [] :: [{integer(),'undefined' | binary()}]}).
|
error = [] :: [{integer(),'undefined' | binary()}]}).
|
||||||
-type stat() :: #stat{}.
|
-type stat() :: #stat{}.
|
||||||
|
|
||||||
|
-record(addresses, {list = [] :: [#address{}]}).
|
||||||
|
-type addresses() :: #addresses{}.
|
||||||
|
|
||||||
-record('see-other-host', {host :: binary()}).
|
-record('see-other-host', {host :: binary()}).
|
||||||
-type 'see-other-host'() :: #'see-other-host'{}.
|
-type 'see-other-host'() :: #'see-other-host'{}.
|
||||||
|
|
||||||
@ -194,6 +228,9 @@
|
|||||||
-record(pubsub_event, {items = [] :: [#pubsub_event_items{}]}).
|
-record(pubsub_event, {items = [] :: [#pubsub_event_items{}]}).
|
||||||
-type pubsub_event() :: #pubsub_event{}.
|
-type pubsub_event() :: #pubsub_event{}.
|
||||||
|
|
||||||
|
-record(muc_unique, {name = <<>> :: binary()}).
|
||||||
|
-type muc_unique() :: #muc_unique{}.
|
||||||
|
|
||||||
-record(sasl_response, {text :: any()}).
|
-record(sasl_response, {text :: any()}).
|
||||||
-type sasl_response() :: #sasl_response{}.
|
-type sasl_response() :: #sasl_response{}.
|
||||||
|
|
||||||
@ -217,19 +254,11 @@
|
|||||||
-record(feature_csi, {xmlns :: binary()}).
|
-record(feature_csi, {xmlns :: binary()}).
|
||||||
-type feature_csi() :: #feature_csi{}.
|
-type feature_csi() :: #feature_csi{}.
|
||||||
|
|
||||||
-record(muc_user_destroy, {reason :: binary(),
|
|
||||||
jid :: any()}).
|
|
||||||
-type muc_user_destroy() :: #muc_user_destroy{}.
|
|
||||||
|
|
||||||
-record(disco_item, {jid :: any(),
|
-record(disco_item, {jid :: any(),
|
||||||
name :: binary(),
|
name :: binary(),
|
||||||
node :: binary()}).
|
node :: binary()}).
|
||||||
-type disco_item() :: #disco_item{}.
|
-type disco_item() :: #disco_item{}.
|
||||||
|
|
||||||
-record(disco_items, {node :: binary(),
|
|
||||||
items = [] :: [#disco_item{}]}).
|
|
||||||
-type disco_items() :: #disco_items{}.
|
|
||||||
|
|
||||||
-record(unblock, {items = [] :: [any()]}).
|
-record(unblock, {items = [] :: [any()]}).
|
||||||
-type unblock() :: #unblock{}.
|
-type unblock() :: #unblock{}.
|
||||||
|
|
||||||
@ -239,10 +268,8 @@
|
|||||||
-record(compression, {methods = [] :: [binary()]}).
|
-record(compression, {methods = [] :: [binary()]}).
|
||||||
-type compression() :: #compression{}.
|
-type compression() :: #compression{}.
|
||||||
|
|
||||||
-record(muc_owner_destroy, {jid :: any(),
|
-record(muc_subscriptions, {list = [] :: [any()]}).
|
||||||
reason :: binary(),
|
-type muc_subscriptions() :: #muc_subscriptions{}.
|
||||||
password :: binary()}).
|
|
||||||
-type muc_owner_destroy() :: #muc_owner_destroy{}.
|
|
||||||
|
|
||||||
-record(pubsub_subscription, {jid :: any(),
|
-record(pubsub_subscription, {jid :: any(),
|
||||||
node :: binary(),
|
node :: binary(),
|
||||||
@ -252,7 +279,7 @@
|
|||||||
|
|
||||||
-record(muc_item, {actor :: #muc_actor{},
|
-record(muc_item, {actor :: #muc_actor{},
|
||||||
continue :: binary(),
|
continue :: binary(),
|
||||||
reason :: binary(),
|
reason = <<>> :: 'undefined' | binary(),
|
||||||
affiliation :: 'admin' | 'member' | 'none' | 'outcast' | 'owner',
|
affiliation :: 'admin' | 'member' | 'none' | 'outcast' | 'owner',
|
||||||
role :: 'moderator' | 'none' | 'participant' | 'visitor',
|
role :: 'moderator' | 'none' | 'participant' | 'visitor',
|
||||||
jid :: any(),
|
jid :: any(),
|
||||||
@ -267,8 +294,8 @@
|
|||||||
|
|
||||||
-record(mam_prefs, {xmlns :: binary(),
|
-record(mam_prefs, {xmlns :: binary(),
|
||||||
default :: 'always' | 'never' | 'roster',
|
default :: 'always' | 'never' | 'roster',
|
||||||
always = [] :: [any()],
|
always :: [any()],
|
||||||
never = [] :: [any()]}).
|
never :: [any()]}).
|
||||||
-type mam_prefs() :: #mam_prefs{}.
|
-type mam_prefs() :: #mam_prefs{}.
|
||||||
|
|
||||||
-record(caps, {node :: binary(),
|
-record(caps, {node :: binary(),
|
||||||
@ -350,13 +377,17 @@
|
|||||||
-record(block_list, {items = [] :: [any()]}).
|
-record(block_list, {items = [] :: [any()]}).
|
||||||
-type block_list() :: #block_list{}.
|
-type block_list() :: #block_list{}.
|
||||||
|
|
||||||
|
-record(xdata_option, {label :: binary(),
|
||||||
|
value :: binary()}).
|
||||||
|
-type xdata_option() :: #xdata_option{}.
|
||||||
|
|
||||||
-record(xdata_field, {label :: binary(),
|
-record(xdata_field, {label :: binary(),
|
||||||
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
||||||
var :: binary(),
|
var :: binary(),
|
||||||
required = false :: boolean(),
|
required = false :: boolean(),
|
||||||
desc :: binary(),
|
desc :: binary(),
|
||||||
values = [] :: [binary()],
|
values = [] :: [binary()],
|
||||||
options = [] :: [binary()]}).
|
options = [] :: [#xdata_option{}]}).
|
||||||
-type xdata_field() :: #xdata_field{}.
|
-type xdata_field() :: #xdata_field{}.
|
||||||
|
|
||||||
-record(version, {name :: binary(),
|
-record(version, {name :: binary(),
|
||||||
@ -364,11 +395,6 @@
|
|||||||
os :: binary()}).
|
os :: binary()}).
|
||||||
-type version() :: #version{}.
|
-type version() :: #version{}.
|
||||||
|
|
||||||
-record(muc_invite, {reason :: binary(),
|
|
||||||
from :: any(),
|
|
||||||
to :: any()}).
|
|
||||||
-type muc_invite() :: #muc_invite{}.
|
|
||||||
|
|
||||||
-record(bind, {jid :: any(),
|
-record(bind, {jid :: any(),
|
||||||
resource :: any()}).
|
resource :: any()}).
|
||||||
-type bind() :: #bind{}.
|
-type bind() :: #bind{}.
|
||||||
@ -376,13 +402,11 @@
|
|||||||
-record(rosterver_feature, {}).
|
-record(rosterver_feature, {}).
|
||||||
-type rosterver_feature() :: #rosterver_feature{}.
|
-type rosterver_feature() :: #rosterver_feature{}.
|
||||||
|
|
||||||
-record(muc_user, {decline :: #muc_decline{},
|
-record(muc_invite, {reason = <<>> :: 'undefined' | binary(),
|
||||||
destroy :: #muc_user_destroy{},
|
from :: any(),
|
||||||
invites = [] :: [#muc_invite{}],
|
to :: any(),
|
||||||
items = [] :: [#muc_item{}],
|
continue :: binary()}).
|
||||||
status_codes = [] :: [pos_integer()],
|
-type muc_invite() :: #muc_invite{}.
|
||||||
password :: binary()}).
|
|
||||||
-type muc_user() :: #muc_user{}.
|
|
||||||
|
|
||||||
-record(carbons_disable, {}).
|
-record(carbons_disable, {}).
|
||||||
-type carbons_disable() :: #carbons_disable{}.
|
-type carbons_disable() :: #carbons_disable{}.
|
||||||
@ -400,7 +424,7 @@
|
|||||||
-type vcard_org() :: #vcard_org{}.
|
-type vcard_org() :: #vcard_org{}.
|
||||||
|
|
||||||
-record(rsm_set, {'after' :: binary(),
|
-record(rsm_set, {'after' :: binary(),
|
||||||
before :: 'none' | binary(),
|
before :: binary(),
|
||||||
count :: non_neg_integer(),
|
count :: non_neg_integer(),
|
||||||
first :: #rsm_first{},
|
first :: #rsm_first{},
|
||||||
index :: non_neg_integer(),
|
index :: non_neg_integer(),
|
||||||
@ -414,6 +438,11 @@
|
|||||||
complete :: any()}).
|
complete :: any()}).
|
||||||
-type mam_fin() :: #mam_fin{}.
|
-type mam_fin() :: #mam_fin{}.
|
||||||
|
|
||||||
|
-record(disco_items, {node :: binary(),
|
||||||
|
items = [] :: [#disco_item{}],
|
||||||
|
rsm :: #rsm_set{}}).
|
||||||
|
-type disco_items() :: #disco_items{}.
|
||||||
|
|
||||||
-record(vcard_tel, {home = false :: boolean(),
|
-record(vcard_tel, {home = false :: boolean(),
|
||||||
work = false :: boolean(),
|
work = false :: boolean(),
|
||||||
voice = false :: boolean(),
|
voice = false :: boolean(),
|
||||||
@ -430,6 +459,20 @@
|
|||||||
number :: binary()}).
|
number :: binary()}).
|
||||||
-type vcard_tel() :: #vcard_tel{}.
|
-type vcard_tel() :: #vcard_tel{}.
|
||||||
|
|
||||||
|
-record(muc_destroy, {xmlns :: binary(),
|
||||||
|
jid :: any(),
|
||||||
|
reason = <<>> :: 'undefined' | binary(),
|
||||||
|
password :: binary()}).
|
||||||
|
-type muc_destroy() :: #muc_destroy{}.
|
||||||
|
|
||||||
|
-record(muc_user, {decline :: #muc_decline{},
|
||||||
|
destroy :: #muc_destroy{},
|
||||||
|
invites = [] :: [#muc_invite{}],
|
||||||
|
items = [] :: [#muc_item{}],
|
||||||
|
status_codes = [] :: [pos_integer()],
|
||||||
|
password :: binary()}).
|
||||||
|
-type muc_user() :: #muc_user{}.
|
||||||
|
|
||||||
-record(vcard_key, {type :: binary(),
|
-record(vcard_key, {type :: binary(),
|
||||||
cred :: binary()}).
|
cred :: binary()}).
|
||||||
-type vcard_key() :: #vcard_key{}.
|
-type vcard_key() :: #vcard_key{}.
|
||||||
@ -530,14 +573,11 @@
|
|||||||
start :: any(),
|
start :: any(),
|
||||||
'end' :: any(),
|
'end' :: any(),
|
||||||
with :: any(),
|
with :: any(),
|
||||||
|
withtext :: binary(),
|
||||||
rsm :: #rsm_set{},
|
rsm :: #rsm_set{},
|
||||||
xdata :: #xdata{}}).
|
xdata :: #xdata{}}).
|
||||||
-type mam_query() :: #mam_query{}.
|
-type mam_query() :: #mam_query{}.
|
||||||
|
|
||||||
-record(muc_owner, {destroy :: #muc_owner_destroy{},
|
|
||||||
config :: #xdata{}}).
|
|
||||||
-type muc_owner() :: #muc_owner{}.
|
|
||||||
|
|
||||||
-record(pubsub_options, {node :: binary(),
|
-record(pubsub_options, {node :: binary(),
|
||||||
jid :: any(),
|
jid :: any(),
|
||||||
subid :: binary(),
|
subid :: binary(),
|
||||||
@ -592,6 +632,11 @@
|
|||||||
fetch = false :: boolean()}).
|
fetch = false :: boolean()}).
|
||||||
-type offline() :: #offline{}.
|
-type offline() :: #offline{}.
|
||||||
|
|
||||||
|
-record(muc_owner, {destroy :: #muc_destroy{},
|
||||||
|
config :: #xdata{},
|
||||||
|
items = [] :: [#muc_item{}]}).
|
||||||
|
-type muc_owner() :: #muc_owner{}.
|
||||||
|
|
||||||
-record(sasl_mechanisms, {list = [] :: [binary()]}).
|
-record(sasl_mechanisms, {list = [] :: [binary()]}).
|
||||||
-type sasl_mechanisms() :: #sasl_mechanisms{}.
|
-type sasl_mechanisms() :: #sasl_mechanisms{}.
|
||||||
|
|
||||||
@ -709,6 +754,7 @@
|
|||||||
|
|
||||||
-type xmpp_element() :: compression() |
|
-type xmpp_element() :: compression() |
|
||||||
pubsub_subscription() |
|
pubsub_subscription() |
|
||||||
|
xdata_option() |
|
||||||
version() |
|
version() |
|
||||||
pubsub_affiliation() |
|
pubsub_affiliation() |
|
||||||
muc_admin() |
|
muc_admin() |
|
||||||
@ -726,7 +772,9 @@
|
|||||||
rsm_set() |
|
rsm_set() |
|
||||||
'see-other-host'() |
|
'see-other-host'() |
|
||||||
hint() |
|
hint() |
|
||||||
|
stanza_id() |
|
||||||
starttls_proceed() |
|
starttls_proceed() |
|
||||||
|
client_id() |
|
||||||
sm_resumed() |
|
sm_resumed() |
|
||||||
forwarded() |
|
forwarded() |
|
||||||
xevent() |
|
xevent() |
|
||||||
@ -742,8 +790,11 @@
|
|||||||
pubsub_event_item() |
|
pubsub_event_item() |
|
||||||
muc_item() |
|
muc_item() |
|
||||||
vcard_temp() |
|
vcard_temp() |
|
||||||
|
address() |
|
||||||
sasl_success() |
|
sasl_success() |
|
||||||
|
addresses() |
|
||||||
pubsub_event_items() |
|
pubsub_event_items() |
|
||||||
|
muc_subscriptions() |
|
||||||
disco_items() |
|
disco_items() |
|
||||||
pubsub_options() |
|
pubsub_options() |
|
||||||
compress() |
|
compress() |
|
||||||
@ -751,33 +802,25 @@
|
|||||||
muc_history() |
|
muc_history() |
|
||||||
identity() |
|
identity() |
|
||||||
feature_csi() |
|
feature_csi() |
|
||||||
muc_user_destroy() |
|
|
||||||
privacy_query() |
|
privacy_query() |
|
||||||
delay() |
|
delay() |
|
||||||
vcard_tel() |
|
vcard_tel() |
|
||||||
vcard_logo() |
|
|
||||||
disco_info() |
|
|
||||||
vcard_geo() |
|
vcard_geo() |
|
||||||
vcard_photo() |
|
vcard_photo() |
|
||||||
feature_register() |
|
|
||||||
register() |
|
|
||||||
muc_owner() |
|
|
||||||
pubsub() |
|
pubsub() |
|
||||||
sm_r() |
|
muc_owner() |
|
||||||
muc_actor() |
|
muc_actor() |
|
||||||
error() |
|
|
||||||
stream_error() |
|
|
||||||
muc_user() |
|
|
||||||
vcard_adr() |
|
|
||||||
carbons_private() |
|
carbons_private() |
|
||||||
mix_leave() |
|
mix_leave() |
|
||||||
muc_invite() |
|
muc_subscribe() |
|
||||||
rosterver_feature() |
|
rosterver_feature() |
|
||||||
|
muc_invite() |
|
||||||
vcard_xupdate() |
|
vcard_xupdate() |
|
||||||
carbons_disable() |
|
carbons_disable() |
|
||||||
bookmark_conference() |
|
bookmark_conference() |
|
||||||
offline() |
|
offline() |
|
||||||
time() |
|
time() |
|
||||||
|
muc_unique() |
|
||||||
sasl_response() |
|
sasl_response() |
|
||||||
pubsub_subscribe() |
|
pubsub_subscribe() |
|
||||||
presence() |
|
presence() |
|
||||||
@ -786,6 +829,7 @@
|
|||||||
starttls_failure() |
|
starttls_failure() |
|
||||||
sasl_challenge() |
|
sasl_challenge() |
|
||||||
gone() |
|
gone() |
|
||||||
|
x_conference() |
|
||||||
private() |
|
private() |
|
||||||
compress_failure() |
|
compress_failure() |
|
||||||
sasl_failure() |
|
sasl_failure() |
|
||||||
@ -794,6 +838,7 @@
|
|||||||
sm_resume() |
|
sm_resume() |
|
||||||
carbons_enable() |
|
carbons_enable() |
|
||||||
expire() |
|
expire() |
|
||||||
|
muc_unsubscribe() |
|
||||||
pubsub_unsubscribe() |
|
pubsub_unsubscribe() |
|
||||||
muc_decline() |
|
muc_decline() |
|
||||||
chatstate() |
|
chatstate() |
|
||||||
@ -803,6 +848,7 @@
|
|||||||
search() |
|
search() |
|
||||||
pubsub_publish() |
|
pubsub_publish() |
|
||||||
unblock() |
|
unblock() |
|
||||||
|
nick() |
|
||||||
p1_ack() |
|
p1_ack() |
|
||||||
block() |
|
block() |
|
||||||
mix_join() |
|
mix_join() |
|
||||||
@ -832,11 +878,20 @@
|
|||||||
starttls() |
|
starttls() |
|
||||||
mam_prefs() |
|
mam_prefs() |
|
||||||
sasl_mechanisms() |
|
sasl_mechanisms() |
|
||||||
|
muc_destroy() |
|
||||||
vcard_key() |
|
vcard_key() |
|
||||||
csi() |
|
csi() |
|
||||||
roster_query() |
|
roster_query() |
|
||||||
muc_owner_destroy() |
|
|
||||||
mam_query() |
|
mam_query() |
|
||||||
bookmark_url() |
|
bookmark_url() |
|
||||||
vcard_email() |
|
vcard_email() |
|
||||||
vcard_label().
|
vcard_label() |
|
||||||
|
vcard_logo() |
|
||||||
|
disco_info() |
|
||||||
|
feature_register() |
|
||||||
|
register() |
|
||||||
|
sm_r() |
|
||||||
|
error() |
|
||||||
|
stream_error() |
|
||||||
|
muc_user() |
|
||||||
|
vcard_adr().
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
-spec route(jid(), jid(), xmlel() | xmpp_element()) -> ok.
|
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
|
||||||
|
|
||||||
route(From, To, Packet) ->
|
route(From, To, Packet) ->
|
||||||
case catch do_route(From, To, Packet) of
|
case catch do_route(From, To, Packet) of
|
||||||
@ -85,13 +85,21 @@ route(From, To, Packet) ->
|
|||||||
|
|
||||||
%% Route the error packet only if the originating packet is not an error itself.
|
%% Route the error packet only if the originating packet is not an error itself.
|
||||||
%% RFC3920 9.3.1
|
%% RFC3920 9.3.1
|
||||||
-spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok.
|
-spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok;
|
||||||
|
(jid(), jid(), stanza(), error()) -> ok.
|
||||||
|
|
||||||
route_error(From, To, ErrPacket, OrigPacket) ->
|
route_error(From, To, #xmlel{} = ErrPacket, #xmlel{} = OrigPacket) ->
|
||||||
#xmlel{attrs = Attrs} = OrigPacket,
|
#xmlel{attrs = Attrs} = OrigPacket,
|
||||||
case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of
|
case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of
|
||||||
false -> route(From, To, ErrPacket);
|
false -> route(From, To, ErrPacket);
|
||||||
true -> ok
|
true -> ok
|
||||||
|
end;
|
||||||
|
route_error(From, To, Packet, #error{} = Err) ->
|
||||||
|
Type = xmpp:get_type(Packet),
|
||||||
|
if Type == error; Type == result ->
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
ejabberd_router:route(From, To, xmpp:make_error(Packet, Err))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec register_route(binary()) -> term().
|
-spec register_route(binary()) -> term().
|
||||||
@ -406,11 +414,16 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any().
|
-spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any().
|
||||||
do_route(From, To, Packet,
|
do_route(From, To, Packet, #route{local_hint = LocalHint,
|
||||||
#route{local_hint = {apply, Module, Function}, pid = Pid})
|
pid = Pid}) when is_pid(Pid) ->
|
||||||
when is_pid(Pid) andalso node(Pid) == node() ->
|
try xmpp:decode(Packet, [ignore_els]) of
|
||||||
try
|
Pkt ->
|
||||||
Module:Function(From, To, xmpp:decode(Packet, [ignore_els]))
|
case LocalHint of
|
||||||
|
{apply, Module, Function} when node(Pid) == node() ->
|
||||||
|
Module:Function(From, To, Pkt);
|
||||||
|
_ ->
|
||||||
|
Pid ! {route, From, To, Pkt}
|
||||||
|
end
|
||||||
catch error:{xmpp_codec, Why} ->
|
catch error:{xmpp_codec, Why} ->
|
||||||
?ERROR_MSG("failed to decode xml element ~p when "
|
?ERROR_MSG("failed to decode xml element ~p when "
|
||||||
"routing from ~s to ~s: ~s",
|
"routing from ~s to ~s: ~s",
|
||||||
@ -418,8 +431,6 @@ do_route(From, To, Packet,
|
|||||||
xmpp:format_error(Why)]),
|
xmpp:format_error(Why)]),
|
||||||
drop
|
drop
|
||||||
end;
|
end;
|
||||||
do_route(From, To, Packet, #route{pid = Pid}) when is_pid(Pid) ->
|
|
||||||
Pid ! {route, From, To, xmpp:encode(Packet)};
|
|
||||||
do_route(_From, _To, _Packet, _Route) ->
|
do_route(_From, _To, _Packet, _Route) ->
|
||||||
drop.
|
drop.
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ process_sm_iq_items(#iq{type = get, lang = Lang,
|
|||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
Txt = <<"Not subscribed">>,
|
Txt = <<"Not subscribed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_sm_items({error, error()} | {result, [disco_item()]} | empty,
|
-spec get_sm_items({error, error()} | {result, [disco_item()]} | empty,
|
||||||
@ -371,7 +371,7 @@ process_sm_iq_info(#iq{type = get, lang = Lang,
|
|||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
Txt = <<"Not subscribed">>,
|
Txt = <<"Not subscribed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_sm_identity([identity()], jid(), jid(),
|
-spec get_sm_identity([identity()], jid(), jid(),
|
||||||
|
@ -140,7 +140,7 @@ process_sm_iq(#iq{from = From, to = To, lang = Lang} = IQ) ->
|
|||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
Txt = <<"Not subscribed">>,
|
Txt = <<"Not subscribed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_not_subscribed(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (LUser::string(), LServer::string()) ->
|
%% @spec (LUser::string(), LServer::string()) ->
|
||||||
|
645
src/mod_mam.erl
645
src/mod_mam.erl
@ -34,12 +34,12 @@
|
|||||||
-export([start/2, stop/1, depends/2]).
|
-export([start/2, stop/1, depends/2]).
|
||||||
|
|
||||||
-export([user_send_packet/4, user_send_packet_strip_tag/4, user_receive_packet/5,
|
-export([user_send_packet/4, user_send_packet_strip_tag/4, user_receive_packet/5,
|
||||||
process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
|
process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5,
|
||||||
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/4,
|
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2,
|
||||||
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
||||||
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/4]).
|
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/4]).
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("mod_muc_room.hrl").
|
-include("mod_muc_room.hrl").
|
||||||
-include("ejabberd_commands.hrl").
|
-include("ejabberd_commands.hrl").
|
||||||
@ -54,17 +54,12 @@
|
|||||||
-callback delete_old_messages(binary() | global,
|
-callback delete_old_messages(binary() | global,
|
||||||
erlang:timestamp(),
|
erlang:timestamp(),
|
||||||
all | chat | groupchat) -> any().
|
all | chat | groupchat) -> any().
|
||||||
-callback extended_fields() -> [xmlel()].
|
-callback extended_fields() -> [xdata_field()].
|
||||||
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
|
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
|
||||||
jid(), binary(), recv | send) -> {ok, binary()} | any().
|
jid(), binary(), recv | send) -> {ok, binary()} | any().
|
||||||
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
|
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
|
||||||
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
|
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
|
||||||
-callback select(binary(), jid(), jid(),
|
-callback select(binary(), jid(), jid(), mam_query(), chat | groupchat) ->
|
||||||
none | erlang:timestamp(),
|
|
||||||
none | erlang:timestamp(),
|
|
||||||
none | ljid() | {text, binary()},
|
|
||||||
none | #rsm_in{},
|
|
||||||
chat | groupchat) ->
|
|
||||||
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -200,14 +195,10 @@ get_room_config(X, RoomState, _From, Lang) ->
|
|||||||
true -> <<"1">>;
|
true -> <<"1">>;
|
||||||
_ -> <<"0">>
|
_ -> <<"0">>
|
||||||
end,
|
end,
|
||||||
XField = #xmlel{name = <<"field">>,
|
XField = #xdata_field{type = boolean,
|
||||||
attrs =
|
label = translate:translate(Lang, Label),
|
||||||
[{<<"type">>, <<"boolean">>},
|
var = Var,
|
||||||
{<<"label">>, translate:translate(Lang, Label)},
|
values = [Val]},
|
||||||
{<<"var">>, Var}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children = [{xmlcdata, Val}]}]},
|
|
||||||
X ++ [XField].
|
X ++ [XField].
|
||||||
|
|
||||||
set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
|
set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
|
||||||
@ -222,7 +213,7 @@ set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
|
|||||||
catch _:{case_clause, _} ->
|
catch _:{case_clause, _} ->
|
||||||
Txt = <<"Value of '~s' should be boolean">>,
|
Txt = <<"Value of '~s' should be boolean">>,
|
||||||
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
|
||||||
{error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)}
|
{error, xmpp:err_bad_request(Lang, ErrTxt)}
|
||||||
end;
|
end;
|
||||||
set_room_option(Acc, _Opt, _Vals, _Lang) ->
|
set_room_option(Acc, _Opt, _Vals, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
@ -236,16 +227,7 @@ user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
|
|||||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||||
case store_msg(C2SState, NewPkt, LUser, LServer, Peer, recv) of
|
case store_msg(C2SState, NewPkt, LUser, LServer, Peer, recv) of
|
||||||
{ok, ID} ->
|
{ok, ID} ->
|
||||||
Archived = #xmlel{name = <<"archived">>,
|
set_stanza_id(NewPkt, LServer, ID);
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_MAM_TMP},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
StanzaID = #xmlel{name = <<"stanza-id">>,
|
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_SID_0},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
|
|
||||||
NewPkt#xmlel{children = NewEls};
|
|
||||||
_ ->
|
_ ->
|
||||||
NewPkt
|
NewPkt
|
||||||
end;
|
end;
|
||||||
@ -259,19 +241,10 @@ user_send_packet(Pkt, C2SState, JID, Peer) ->
|
|||||||
case should_archive(Pkt, LServer) of
|
case should_archive(Pkt, LServer) of
|
||||||
true ->
|
true ->
|
||||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||||
case store_msg(C2SState, jlib:replace_from_to(JID, Peer, NewPkt),
|
case store_msg(C2SState, xmpp:set_from_to(NewPkt, JID, Peer),
|
||||||
LUser, LServer, Peer, send) of
|
LUser, LServer, Peer, send) of
|
||||||
{ok, ID} ->
|
{ok, ID} ->
|
||||||
Archived = #xmlel{name = <<"archived">>,
|
set_stanza_id(NewPkt, LServer, ID);
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_MAM_TMP},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
StanzaID = #xmlel{name = <<"stanza-id">>,
|
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_SID_0},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
|
|
||||||
NewPkt#xmlel{children = NewEls};
|
|
||||||
_ ->
|
_ ->
|
||||||
NewPkt
|
NewPkt
|
||||||
end;
|
end;
|
||||||
@ -291,16 +264,7 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
|
|||||||
StorePkt = strip_x_jid_tags(NewPkt),
|
StorePkt = strip_x_jid_tags(NewPkt),
|
||||||
case store_muc(MUCState, StorePkt, RoomJID, From, FromNick) of
|
case store_muc(MUCState, StorePkt, RoomJID, From, FromNick) of
|
||||||
{ok, ID} ->
|
{ok, ID} ->
|
||||||
Archived = #xmlel{name = <<"archived">>,
|
set_stanza_id(NewPkt, LServer, ID);
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_MAM_TMP},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
StanzaID = #xmlel{name = <<"stanza-id">>,
|
|
||||||
attrs = [{<<"by">>, LServer},
|
|
||||||
{<<"xmlns">>, ?NS_SID_0},
|
|
||||||
{<<"id">>, ID}]},
|
|
||||||
NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
|
|
||||||
NewPkt#xmlel{children = NewEls};
|
|
||||||
_ ->
|
_ ->
|
||||||
NewPkt
|
NewPkt
|
||||||
end;
|
end;
|
||||||
@ -308,71 +272,98 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
|
|||||||
Pkt
|
Pkt
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
set_stanza_id(Pkt, LServer, ID) ->
|
||||||
|
Archived = #mam_archived{by = jid:make(LServer), id = ID},
|
||||||
|
StanzaID = #stanza_id{by = jid:make(LServer), id = ID},
|
||||||
|
NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)],
|
||||||
|
xmpp:set_els(Pkt, NewEls).
|
||||||
|
|
||||||
% Query archive v0.2
|
% Query archive v0.2
|
||||||
process_iq_v0_2(#jid{lserver = LServer} = From,
|
process_iq_v0_2(#iq{from = #jid{lserver = LServer},
|
||||||
#jid{lserver = LServer} = To,
|
to = #jid{lserver = LServer},
|
||||||
#iq{type = get, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
type = get, sub_els = [#mam_query{}]} = IQ) ->
|
||||||
Fs = parse_query_v0_2(SubEl),
|
process_iq(LServer, IQ, chat);
|
||||||
process_iq(LServer, From, To, IQ, SubEl, Fs, chat);
|
process_iq_v0_2(IQ) ->
|
||||||
process_iq_v0_2(From, To, IQ) ->
|
process_iq(IQ).
|
||||||
process_iq(From, To, IQ).
|
|
||||||
|
|
||||||
% Query archive v0.3
|
% Query archive v0.3
|
||||||
process_iq_v0_3(#jid{lserver = LServer} = From,
|
process_iq_v0_3(#iq{from = #jid{lserver = LServer},
|
||||||
#jid{lserver = LServer} = To,
|
to = #jid{lserver = LServer},
|
||||||
#iq{type = set, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
type = set, sub_els = [#mam_query{}]} = IQ) ->
|
||||||
process_iq(LServer, From, To, IQ, SubEl, get_xdata_fields(SubEl), chat);
|
process_iq(LServer, IQ, chat);
|
||||||
process_iq_v0_3(#jid{lserver = LServer},
|
process_iq_v0_3(#iq{from = #jid{lserver = LServer},
|
||||||
#jid{lserver = LServer},
|
to = #jid{lserver = LServer},
|
||||||
#iq{type = get, sub_el = #xmlel{name = <<"query">>}} = IQ) ->
|
type = get, sub_els = [#mam_query{}]} = IQ) ->
|
||||||
process_iq(LServer, IQ);
|
process_iq(LServer, IQ);
|
||||||
process_iq_v0_3(From, To, IQ) ->
|
process_iq_v0_3(IQ) ->
|
||||||
process_iq(From, To, IQ).
|
process_iq(IQ).
|
||||||
|
|
||||||
muc_process_iq(#iq{type = set,
|
muc_process_iq(#iq{type = T, lang = Lang,
|
||||||
sub_el = #xmlel{name = <<"query">>,
|
from = From,
|
||||||
attrs = Attrs} = SubEl} = IQ,
|
sub_els = [#mam_query{xmlns = NS}]} = IQ,
|
||||||
MUCState, From, To) ->
|
MUCState)
|
||||||
case fxml:get_attr_s(<<"xmlns">>, Attrs) of
|
when (T == set andalso (NS == ?NS_MAM_0 orelse NS == ?NS_MAM_1)) orelse
|
||||||
NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
|
(T == get andalso NS == ?NS_MAM_TMP) ->
|
||||||
muc_process_iq(IQ, MUCState, From, To, get_xdata_fields(SubEl));
|
case may_enter_room(From, MUCState) of
|
||||||
_ ->
|
true ->
|
||||||
IQ
|
LServer = MUCState#state.server_host,
|
||||||
|
Role = mod_muc_room:get_role(From, MUCState),
|
||||||
|
process_iq(LServer, IQ, {groupchat, Role, MUCState});
|
||||||
|
false ->
|
||||||
|
Text = <<"Only members may query archives of this room">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_forbidden(Text, Lang))
|
||||||
end;
|
end;
|
||||||
muc_process_iq(#iq{type = get,
|
muc_process_iq(#iq{type = get,
|
||||||
sub_el = #xmlel{name = <<"query">>,
|
sub_els = [#mam_query{xmlns = NS}]} = IQ,
|
||||||
attrs = Attrs} = SubEl} = IQ,
|
MUCState) when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
|
||||||
MUCState, From, To) ->
|
|
||||||
case fxml:get_attr_s(<<"xmlns">>, Attrs) of
|
|
||||||
?NS_MAM_TMP ->
|
|
||||||
muc_process_iq(IQ, MUCState, From, To, parse_query_v0_2(SubEl));
|
|
||||||
NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
|
|
||||||
LServer = MUCState#state.server_host,
|
LServer = MUCState#state.server_host,
|
||||||
process_iq(LServer, IQ);
|
process_iq(LServer, IQ);
|
||||||
_ ->
|
muc_process_iq(IQ, _MUCState) ->
|
||||||
IQ
|
|
||||||
end;
|
|
||||||
muc_process_iq(IQ, _MUCState, _From, _To) ->
|
|
||||||
IQ.
|
IQ.
|
||||||
|
|
||||||
get_xdata_fields(SubEl) ->
|
parse_query(#mam_query{xdata = #xdata{fields = Fs}} = Query, Lang) ->
|
||||||
case {fxml:get_subtag_with_xmlns(SubEl, <<"x">>, ?NS_XDATA),
|
try
|
||||||
fxml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
|
lists:foldl(
|
||||||
{#xmlel{} = XData, false} ->
|
fun(#xdata_field{var = <<"start">>, values = [Data|_]}, Q) ->
|
||||||
jlib:parse_xdata_submit(XData);
|
case jlib:datetime_string_to_timestamp(Data) of
|
||||||
{#xmlel{} = XData, #xmlel{}} ->
|
undefined -> throw({error, <<"start">>});
|
||||||
[{<<"set">>, SubEl} | jlib:parse_xdata_submit(XData)];
|
{_, _, _} = TS -> Q#mam_query{start = TS}
|
||||||
{false, #xmlel{}} ->
|
end;
|
||||||
[{<<"set">>, SubEl}];
|
(#xdata_field{var = <<"end">>, values = [Data|_]}, Q) ->
|
||||||
{false, false} ->
|
case jlib:datetime_string_to_timestamp(Data) of
|
||||||
[]
|
undefined -> throw({error, <<"end">>});
|
||||||
end.
|
{_, _, _} = TS -> Q#mam_query{'end' = TS}
|
||||||
|
end;
|
||||||
|
(#xdata_field{var = <<"with">>, values = [Data|_]}, Q) ->
|
||||||
|
case jid:from_string(Data) of
|
||||||
|
error -> throw({error, <<"with">>});
|
||||||
|
J -> Q#mam_query{with = J}
|
||||||
|
end;
|
||||||
|
(#xdata_field{var = <<"withtext">>, values = [Data|_]}, Q) ->
|
||||||
|
case Data of
|
||||||
|
<<"">> -> throw({error, <<"withtext">>});
|
||||||
|
_ -> Q#mam_query{withtext = Data}
|
||||||
|
end;
|
||||||
|
(#xdata_field{var = <<"FORM_TYPE">>, values = [NS|_]}, Q) ->
|
||||||
|
case Query#mam_query.xmlns of
|
||||||
|
NS -> Q;
|
||||||
|
_ -> throw({error, <<"FORM_TYPE">>})
|
||||||
|
end;
|
||||||
|
(#xdata_field{}, Acc) ->
|
||||||
|
Acc
|
||||||
|
end, Query, Fs)
|
||||||
|
catch throw:{error, Var} ->
|
||||||
|
Txt = io_lib:format("Incorrect value of field '~s'", [Var]),
|
||||||
|
{error, xmpp:err_bad_request(iolist_to_binary(Txt), Lang)}
|
||||||
|
end;
|
||||||
|
parse_query(Query, _Lang) ->
|
||||||
|
Query.
|
||||||
|
|
||||||
disco_sm_features(empty, From, To, Node, Lang) ->
|
disco_sm_features(empty, From, To, Node, Lang) ->
|
||||||
disco_sm_features({result, []}, From, To, Node, Lang);
|
disco_sm_features({result, []}, From, To, Node, Lang);
|
||||||
disco_sm_features({result, OtherFeatures},
|
disco_sm_features({result, OtherFeatures},
|
||||||
#jid{luser = U, lserver = S},
|
#jid{luser = U, lserver = S},
|
||||||
#jid{luser = U, lserver = S}, <<>>, _Lang) ->
|
#jid{luser = U, lserver = S}, undefined, _Lang) ->
|
||||||
{result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1 | OtherFeatures]};
|
{result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1 | OtherFeatures]};
|
||||||
disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
@ -440,152 +431,76 @@ delete_old_messages(_TypeBin, _Days) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
|
process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) ->
|
||||||
NS = case fxml:get_attr_s(<<"xmlns">>, Attrs) of
|
CommonFields = [#xdata_field{type = hidden,
|
||||||
?NS_MAM_0 ->
|
var = <<"FORM_TYPE">>,
|
||||||
?NS_MAM_0;
|
values = [NS]},
|
||||||
_ ->
|
#xdata_field{type = 'jid-single', var = <<"with">>},
|
||||||
?NS_MAM_1
|
#xdata_field{type = 'text-single', var = <<"start">>},
|
||||||
end,
|
#xdata_field{type = 'text-single', var = <<"end">>}],
|
||||||
CommonFields = [#xmlel{name = <<"field">>,
|
|
||||||
attrs = [{<<"type">>, <<"hidden">>},
|
|
||||||
{<<"var">>, <<"FORM_TYPE">>}],
|
|
||||||
children = [#xmlel{name = <<"value">>,
|
|
||||||
children = [{xmlcdata, NS}]}]},
|
|
||||||
#xmlel{name = <<"field">>,
|
|
||||||
attrs = [{<<"type">>, <<"jid-single">>},
|
|
||||||
{<<"var">>, <<"with">>}]},
|
|
||||||
#xmlel{name = <<"field">>,
|
|
||||||
attrs = [{<<"type">>, <<"text-single">>},
|
|
||||||
{<<"var">>, <<"start">>}]},
|
|
||||||
#xmlel{name = <<"field">>,
|
|
||||||
attrs = [{<<"type">>, <<"text-single">>},
|
|
||||||
{<<"var">>, <<"end">>}]}],
|
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
ExtendedFields = Mod:extended_fields(),
|
ExtendedFields = Mod:extended_fields(),
|
||||||
Fields = ExtendedFields ++ CommonFields,
|
Fields = CommonFields ++ ExtendedFields,
|
||||||
Form = #xmlel{name = <<"x">>,
|
Form = #xdata{type = form, fields = Fields},
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
|
xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = Form}).
|
||||||
children = Fields},
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [#xmlel{name = <<"query">>,
|
|
||||||
attrs = [{<<"xmlns">>, NS}],
|
|
||||||
children = [Form]}]}.
|
|
||||||
|
|
||||||
% Preference setting (both v0.2 & v0.3)
|
% Preference setting (both v0.2 & v0.3)
|
||||||
process_iq(#jid{luser = LUser, lserver = LServer},
|
process_iq(#iq{type = set, lang = Lang,
|
||||||
#jid{lserver = LServer},
|
sub_els = [#mam_prefs{default = undefined, xmlns = NS}]} = IQ) ->
|
||||||
#iq{type = set, lang = Lang, sub_el = #xmlel{name = <<"prefs">>} = SubEl} = IQ) ->
|
Why = {missing_attr, <<"default">>, <<"prefs">>, NS},
|
||||||
try {case fxml:get_tag_attr_s(<<"default">>, SubEl) of
|
ErrTxt = xmpp:format_error(Why),
|
||||||
<<"always">> -> always;
|
xmpp:make_error(IQ, xmpp:err_bad_request(ErrTxt, Lang));
|
||||||
<<"never">> -> never;
|
process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
<<"roster">> -> roster
|
to = #jid{lserver = LServer},
|
||||||
end,
|
type = set, lang = Lang,
|
||||||
lists:foldl(
|
sub_els = [#mam_prefs{xmlns = NS,
|
||||||
fun(#xmlel{name = <<"always">>, children = Els}, {A, N}) ->
|
default = Default,
|
||||||
{get_jids(Els) ++ A, N};
|
always = Always0,
|
||||||
(#xmlel{name = <<"never">>, children = Els}, {A, N}) ->
|
never = Never0}]} = IQ) ->
|
||||||
{A, get_jids(Els) ++ N};
|
Always = lists:usort(get_jids(Always0)),
|
||||||
(_, {A, N}) ->
|
Never = lists:usort(get_jids(Never0)),
|
||||||
{A, N}
|
|
||||||
end, {[], []}, SubEl#xmlel.children)} of
|
|
||||||
{Default, {Always0, Never0}} ->
|
|
||||||
Always = lists:usort(Always0),
|
|
||||||
Never = lists:usort(Never0),
|
|
||||||
case write_prefs(LUser, LServer, LServer, Default, Always, Never) of
|
case write_prefs(LUser, LServer, LServer, Default, Always, Never) of
|
||||||
ok ->
|
ok ->
|
||||||
NewPrefs = prefs_el(Default, Always, Never, IQ#iq.xmlns),
|
NewPrefs = prefs_el(Default, Always, Never, NS),
|
||||||
IQ#iq{type = result, sub_el = [NewPrefs]};
|
xmpp:make_iq_result(IQ, NewPrefs);
|
||||||
_Err ->
|
_Err ->
|
||||||
Txt = <<"Database failure">>,
|
Txt = <<"Database failure">>,
|
||||||
IQ#iq{type = error,
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]}
|
|
||||||
end
|
|
||||||
catch _:_ ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
|
||||||
end;
|
end;
|
||||||
process_iq(#jid{luser = LUser, lserver = LServer},
|
process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
#jid{lserver = LServer},
|
to = #jid{lserver = LServer},
|
||||||
#iq{type = get, sub_el = #xmlel{name = <<"prefs">>}} = IQ) ->
|
type = get, sub_els = [#mam_prefs{xmlns = NS}]} = IQ) ->
|
||||||
Prefs = get_prefs(LUser, LServer),
|
Prefs = get_prefs(LUser, LServer),
|
||||||
PrefsEl = prefs_el(Prefs#archive_prefs.default,
|
PrefsEl = prefs_el(Prefs#archive_prefs.default,
|
||||||
Prefs#archive_prefs.always,
|
Prefs#archive_prefs.always,
|
||||||
Prefs#archive_prefs.never,
|
Prefs#archive_prefs.never,
|
||||||
IQ#iq.xmlns),
|
NS),
|
||||||
IQ#iq{type = result, sub_el = [PrefsEl]};
|
xmpp:make_iq_result(IQ, PrefsEl);
|
||||||
process_iq(_, _, #iq{sub_el = SubEl} = IQ) ->
|
process_iq(IQ) ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
|
xmpp:make_error(IQ, xmpp:err_not_allowed()).
|
||||||
|
|
||||||
process_iq(LServer, #jid{luser = LUser} = From, To, IQ, SubEl, Fs, MsgType) ->
|
process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang,
|
||||||
|
sub_els = [SubEl]} = IQ, MsgType) ->
|
||||||
case MsgType of
|
case MsgType of
|
||||||
chat ->
|
chat ->
|
||||||
maybe_activate_mam(LUser, LServer);
|
maybe_activate_mam(LUser, LServer);
|
||||||
{groupchat, _Role, _MUCState} ->
|
{groupchat, _Role, _MUCState} ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
case catch lists:foldl(
|
case parse_query(SubEl, Lang) of
|
||||||
fun({<<"start">>, [Data|_]}, {_, End, With, RSM}) ->
|
#mam_query{rsm = #rsm_set{index = I}} when is_integer(I) ->
|
||||||
{{_, _, _} = jlib:datetime_string_to_timestamp(Data),
|
xmpp:make_error(IQ, xmpp:err_feature_not_implemented());
|
||||||
End, With, RSM};
|
#mam_query{rsm = RSM, xmlns = NS} = Query ->
|
||||||
({<<"end">>, [Data|_]}, {Start, _, With, RSM}) ->
|
NewRSM = limit_max(RSM, NS),
|
||||||
{Start,
|
NewQuery = Query#mam_query{rsm = NewRSM},
|
||||||
{_, _, _} = jlib:datetime_string_to_timestamp(Data),
|
select_and_send(LServer, NewQuery, IQ, MsgType);
|
||||||
With, RSM};
|
{error, Err} ->
|
||||||
({<<"with">>, [Data|_]}, {Start, End, _, RSM}) ->
|
xmpp:make_error(IQ, Err)
|
||||||
{Start, End, jid:tolower(jid:from_string(Data)), RSM};
|
|
||||||
({<<"withtext">>, [Data|_]}, {Start, End, _, RSM}) ->
|
|
||||||
{Start, End, {text, Data}, RSM};
|
|
||||||
({<<"set">>, El}, {Start, End, With, _}) ->
|
|
||||||
{Start, End, With, jlib:rsm_decode(El)};
|
|
||||||
(_, Acc) ->
|
|
||||||
Acc
|
|
||||||
end, {none, [], none, none}, Fs) of
|
|
||||||
{'EXIT', _} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]};
|
|
||||||
{_Start, _End, _With, #rsm_in{index = Index}} when is_integer(Index) ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
|
|
||||||
{Start, End, With, RSM} ->
|
|
||||||
NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
|
|
||||||
select_and_send(LServer, From, To, Start, End,
|
|
||||||
With, limit_max(RSM, NS), IQ, MsgType)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
muc_process_iq(#iq{lang = Lang, sub_el = SubEl} = IQ, MUCState, From, To, Fs) ->
|
should_archive(#message{type = T}, _LServer) when T == error; T == result ->
|
||||||
case may_enter_room(From, MUCState) of
|
|
||||||
true ->
|
|
||||||
LServer = MUCState#state.server_host,
|
|
||||||
Role = mod_muc_room:get_role(From, MUCState),
|
|
||||||
process_iq(LServer, From, To, IQ, SubEl, Fs,
|
|
||||||
{groupchat, Role, MUCState});
|
|
||||||
false ->
|
|
||||||
Text = <<"Only members may query archives of this room">>,
|
|
||||||
Error = ?ERRT_FORBIDDEN(Lang, Text),
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
|
||||||
end.
|
|
||||||
|
|
||||||
parse_query_v0_2(Query) ->
|
|
||||||
lists:flatmap(
|
|
||||||
fun (#xmlel{name = <<"start">>} = El) ->
|
|
||||||
[{<<"start">>, [fxml:get_tag_cdata(El)]}];
|
|
||||||
(#xmlel{name = <<"end">>} = El) ->
|
|
||||||
[{<<"end">>, [fxml:get_tag_cdata(El)]}];
|
|
||||||
(#xmlel{name = <<"with">>} = El) ->
|
|
||||||
[{<<"with">>, [fxml:get_tag_cdata(El)]}];
|
|
||||||
(#xmlel{name = <<"withtext">>} = El) ->
|
|
||||||
[{<<"withtext">>, [fxml:get_tag_cdata(El)]}];
|
|
||||||
(#xmlel{name = <<"set">>}) ->
|
|
||||||
[{<<"set">>, Query}];
|
|
||||||
(_) ->
|
|
||||||
[]
|
|
||||||
end, Query#xmlel.children).
|
|
||||||
|
|
||||||
should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
|
|
||||||
case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
|
|
||||||
<<"error">> ->
|
|
||||||
false;
|
false;
|
||||||
<<"groupchat">> ->
|
should_archive(#message{body = Body} = Pkt, LServer) ->
|
||||||
false;
|
|
||||||
_ ->
|
|
||||||
case is_resent(Pkt, LServer) of
|
case is_resent(Pkt, LServer) of
|
||||||
true ->
|
true ->
|
||||||
false;
|
false;
|
||||||
@ -596,7 +511,7 @@ should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
|
|||||||
no_store ->
|
no_store ->
|
||||||
false;
|
false;
|
||||||
none ->
|
none ->
|
||||||
case fxml:get_subtag_cdata(Pkt, <<"body">>) of
|
case xmpp:get_text(Body) of
|
||||||
<<>> ->
|
<<>> ->
|
||||||
%% Empty body
|
%% Empty body
|
||||||
false;
|
false;
|
||||||
@ -604,46 +519,67 @@ should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
should_archive(#xmlel{}, _LServer) ->
|
should_archive(_, _LServer) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
strip_my_archived_tag(Pkt, LServer) ->
|
strip_my_archived_tag(Pkt, LServer) ->
|
||||||
NewEls = lists:filter(
|
NewPkt = xmpp:decode_els(
|
||||||
fun(#xmlel{name = Tag, attrs = Attrs})
|
Pkt, fun(El) ->
|
||||||
when Tag == <<"archived">>; Tag == <<"stanza-id">> ->
|
case xmpp:get_name(El) of
|
||||||
case catch jid:nameprep(
|
<<"archived">> ->
|
||||||
fxml:get_attr_s(
|
xmpp:get_ns(El) == ?NS_MAM_TMP;
|
||||||
<<"by">>, Attrs)) of
|
<<"stanza-id">> ->
|
||||||
LServer ->
|
xmpp:get_ns(El) == ?NS_SID_0;
|
||||||
false;
|
|
||||||
_ ->
|
_ ->
|
||||||
true
|
false
|
||||||
end;
|
end
|
||||||
|
end),
|
||||||
|
NewEls = lists:filter(
|
||||||
|
fun(#mam_archived{by = #jid{luser = <<>>} = By}) ->
|
||||||
|
By#jid.lserver /= LServer;
|
||||||
|
(#stanza_id{by = #jid{luser = <<>>} = By}) ->
|
||||||
|
By#jid.lserver /= LServer;
|
||||||
(_) ->
|
(_) ->
|
||||||
true
|
true
|
||||||
end, Pkt#xmlel.children),
|
end, xmpp:get_els(NewPkt)),
|
||||||
Pkt#xmlel{children = NewEls}.
|
xmpp:set_els(NewPkt, NewEls).
|
||||||
|
|
||||||
strip_x_jid_tags(Pkt) ->
|
strip_x_jid_tags(Pkt) ->
|
||||||
|
NewPkt = xmpp:decode_els(
|
||||||
|
Pkt, fun(El) ->
|
||||||
|
case xmpp:get_name(El) of
|
||||||
|
<<"x">> ->
|
||||||
|
case xmpp:get_ns(El) of
|
||||||
|
?NS_MUC_USER -> true;
|
||||||
|
?NS_MUC_ADMIN -> true;
|
||||||
|
?NS_MUC_OWNER -> true;
|
||||||
|
_ -> false
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end),
|
||||||
NewEls = lists:filter(
|
NewEls = lists:filter(
|
||||||
fun(#xmlel{name = <<"x">>} = XEl) ->
|
fun(El) ->
|
||||||
not lists:any(fun(ItemEl) ->
|
Items = case El of
|
||||||
fxml:get_tag_attr(<<"jid">>, ItemEl)
|
#muc_user{items = Is} -> Is;
|
||||||
/= false
|
#muc_admin{items = Is} -> Is;
|
||||||
end, fxml:get_subtags(XEl, <<"item">>));
|
#muc_owner{items = Is} -> Is;
|
||||||
(_) ->
|
_ -> []
|
||||||
true
|
end,
|
||||||
end, Pkt#xmlel.children),
|
not lists:any(fun(#muc_item{jid = JID}) ->
|
||||||
Pkt#xmlel{children = NewEls}.
|
JID /= undefined
|
||||||
|
end, Items)
|
||||||
|
end, xmpp:get_els(NewPkt)),
|
||||||
|
xmpp:set_els(NewPkt, NewEls).
|
||||||
|
|
||||||
should_archive_peer(C2SState,
|
should_archive_peer(C2SState,
|
||||||
#archive_prefs{default = Default,
|
#archive_prefs{default = Default,
|
||||||
always = Always,
|
always = Always,
|
||||||
never = Never},
|
never = Never},
|
||||||
Peer) ->
|
Peer) ->
|
||||||
LPeer = jid:tolower(Peer),
|
LPeer = jid:remove_resource(jid:tolower(Peer)),
|
||||||
case lists:member(LPeer, Always) of
|
case lists:member(LPeer, Always) of
|
||||||
true ->
|
true ->
|
||||||
true;
|
true;
|
||||||
@ -667,30 +603,30 @@ should_archive_peer(C2SState,
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
should_archive_muc(Pkt) ->
|
should_archive_muc(#message{type = groupchat,
|
||||||
case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
|
body = Body, subject = Subj} = Pkt) ->
|
||||||
<<"groupchat">> ->
|
|
||||||
case check_store_hint(Pkt) of
|
case check_store_hint(Pkt) of
|
||||||
store ->
|
store ->
|
||||||
true;
|
true;
|
||||||
no_store ->
|
no_store ->
|
||||||
false;
|
false;
|
||||||
none ->
|
none ->
|
||||||
case fxml:get_subtag_cdata(Pkt, <<"body">>) of
|
case xmpp:get_text(Body) of
|
||||||
<<>> ->
|
<<"">> ->
|
||||||
case fxml:get_subtag_cdata(Pkt, <<"subject">>) of
|
case xmpp:get_text(Subj) of
|
||||||
<<>> ->
|
<<"">> ->
|
||||||
false;
|
false;
|
||||||
_ ->
|
_ ->
|
||||||
true
|
true
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
true
|
true
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end.
|
end;
|
||||||
|
should_archive_muc(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
check_store_hint(Pkt) ->
|
check_store_hint(Pkt) ->
|
||||||
case has_store_hint(Pkt) of
|
case has_store_hint(Pkt) of
|
||||||
@ -705,31 +641,25 @@ check_store_hint(Pkt) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec has_store_hint(message()) -> boolean().
|
||||||
has_store_hint(Message) ->
|
has_store_hint(Message) ->
|
||||||
fxml:get_subtag_with_xmlns(Message, <<"store">>, ?NS_HINTS)
|
xmpp:has_subtag(Message, #hint{type = 'store'}).
|
||||||
/= false.
|
|
||||||
|
|
||||||
|
-spec has_no_store_hint(message()) -> boolean().
|
||||||
has_no_store_hint(Message) ->
|
has_no_store_hint(Message) ->
|
||||||
fxml:get_subtag_with_xmlns(Message, <<"no-store">>, ?NS_HINTS)
|
xmpp:has_subtag(Message, #hint{type = 'no-store'}) orelse
|
||||||
/= false orelse
|
xmpp:has_subtag(Message, #hint{type = 'no-storage'}) orelse
|
||||||
fxml:get_subtag_with_xmlns(Message, <<"no-storage">>, ?NS_HINTS)
|
xmpp:has_subtag(Message, #hint{type = 'no-permanent-store'}) orelse
|
||||||
/= false orelse
|
xmpp:has_subtag(Message, #hint{type = 'no-permanent-storage'}).
|
||||||
fxml:get_subtag_with_xmlns(Message, <<"no-permanent-store">>, ?NS_HINTS)
|
|
||||||
/= false orelse
|
|
||||||
fxml:get_subtag_with_xmlns(Message, <<"no-permanent-storage">>, ?NS_HINTS)
|
|
||||||
/= false.
|
|
||||||
|
|
||||||
|
-spec is_resent(message(), binary()) -> boolean().
|
||||||
is_resent(Pkt, LServer) ->
|
is_resent(Pkt, LServer) ->
|
||||||
case fxml:get_subtag_with_xmlns(Pkt, <<"stanza-id">>, ?NS_SID_0) of
|
case xmpp:get_subtag(Pkt, #stanza_id{}) of
|
||||||
#xmlel{attrs = Attrs} ->
|
#stanza_id{by = #jid{luser = <<>>, lserver = LServer}} ->
|
||||||
case fxml:get_attr(<<"by">>, Attrs) of
|
|
||||||
{value, LServer} ->
|
|
||||||
true;
|
true;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
false
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
may_enter_room(From,
|
may_enter_room(From,
|
||||||
@ -744,7 +674,8 @@ store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
|
|||||||
true ->
|
true ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:store(Pkt, LServer, US, chat, Peer, <<"">>, Dir);
|
El = xmpp:encode(Pkt),
|
||||||
|
Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir);
|
||||||
false ->
|
false ->
|
||||||
pass
|
pass
|
||||||
end.
|
end.
|
||||||
@ -755,7 +686,8 @@ store_muc(MUCState, Pkt, RoomJID, Peer, Nick) ->
|
|||||||
LServer = MUCState#state.server_host,
|
LServer = MUCState#state.server_host,
|
||||||
{U, S, _} = jid:tolower(RoomJID),
|
{U, S, _} = jid:tolower(RoomJID),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv);
|
El = xmpp:encode(Pkt),
|
||||||
|
Mod:store(El, LServer, {U, S}, groupchat, Peer, Nick, recv);
|
||||||
false ->
|
false ->
|
||||||
pass
|
pass
|
||||||
end.
|
end.
|
||||||
@ -796,20 +728,10 @@ get_prefs(LUser, LServer) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
prefs_el(Default, Always, Never, NS) ->
|
prefs_el(Default, Always, Never, NS) ->
|
||||||
Default1 = jlib:atom_to_binary(Default),
|
#mam_prefs{default = Default,
|
||||||
JFun = fun(L) ->
|
always = [jid:make(LJ) || LJ <- Always],
|
||||||
[#xmlel{name = <<"jid">>,
|
never = [jid:make(LJ) || LJ <- Never],
|
||||||
children = [{xmlcdata, jid:to_string(J)}]}
|
xmlns = NS}.
|
||||||
|| J <- L]
|
|
||||||
end,
|
|
||||||
Always1 = #xmlel{name = <<"always">>,
|
|
||||||
children = JFun(Always)},
|
|
||||||
Never1 = #xmlel{name = <<"never">>,
|
|
||||||
children = JFun(Never)},
|
|
||||||
#xmlel{name = <<"prefs">>,
|
|
||||||
attrs = [{<<"xmlns">>, NS},
|
|
||||||
{<<"default">>, Default1}],
|
|
||||||
children = [Always1, Never1]}.
|
|
||||||
|
|
||||||
maybe_activate_mam(LUser, LServer) ->
|
maybe_activate_mam(LUser, LServer) ->
|
||||||
ActivateOpt = gen_mod:get_module_opt(LServer, ?MODULE,
|
ActivateOpt = gen_mod:get_module_opt(LServer, ?MODULE,
|
||||||
@ -838,21 +760,19 @@ maybe_activate_mam(LUser, LServer) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType) ->
|
select_and_send(LServer, Query, #iq{from = From, to = To} = IQ, MsgType) ->
|
||||||
{Msgs, IsComplete, Count} = select_and_start(LServer, From, To, Start, End,
|
{Msgs, IsComplete, Count} =
|
||||||
With, RSM, MsgType),
|
|
||||||
SortedMsgs = lists:keysort(2, Msgs),
|
|
||||||
send(From, To, SortedMsgs, RSM, Count, IsComplete, IQ).
|
|
||||||
|
|
||||||
select_and_start(LServer, From, To, Start, End, With, RSM, MsgType) ->
|
|
||||||
case MsgType of
|
case MsgType of
|
||||||
chat ->
|
chat ->
|
||||||
select(LServer, From, From, Start, End, With, RSM, MsgType);
|
select(LServer, From, From, Query, MsgType);
|
||||||
{groupchat, _Role, _MUCState} ->
|
{groupchat, _Role, _MUCState} ->
|
||||||
select(LServer, From, To, Start, End, With, RSM, MsgType)
|
select(LServer, From, To, Query, MsgType)
|
||||||
end.
|
end,
|
||||||
|
SortedMsgs = lists:keysort(2, Msgs),
|
||||||
|
send(SortedMsgs, Count, IsComplete, IQ).
|
||||||
|
|
||||||
select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
select(_LServer, JidRequestor, JidArchive,
|
||||||
|
#mam_query{start = Start, 'end' = End, rsm = RSM},
|
||||||
{groupchat, _Role, #state{config = #config{mam = false},
|
{groupchat, _Role, #state{config = #config{mam = false},
|
||||||
history = History}} = MsgType) ->
|
history = History}} = MsgType) ->
|
||||||
#lqueue{len = L, queue = Q} = History,
|
#lqueue{len = L, queue = Q} = History,
|
||||||
@ -864,7 +784,7 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
|||||||
case match_interval(Now, Start, End) and
|
case match_interval(Now, Start, End) and
|
||||||
match_rsm(Now, RSM) of
|
match_rsm(Now, RSM) of
|
||||||
true ->
|
true ->
|
||||||
{[{jlib:integer_to_binary(TS), TS,
|
{[{integer_to_binary(TS), TS,
|
||||||
msg_to_el(#archive_msg{
|
msg_to_el(#archive_msg{
|
||||||
type = groupchat,
|
type = groupchat,
|
||||||
timestamp = Now,
|
timestamp = Now,
|
||||||
@ -879,31 +799,27 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
|||||||
end, 0, queue:to_list(Q)),
|
end, 0, queue:to_list(Q)),
|
||||||
Msgs = lists:flatten(Msgs0),
|
Msgs = lists:flatten(Msgs0),
|
||||||
case RSM of
|
case RSM of
|
||||||
#rsm_in{max = Max, direction = before} ->
|
#rsm_set{max = Max, before = Before} when is_binary(Before) ->
|
||||||
{NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max),
|
{NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max),
|
||||||
{NewMsgs, IsComplete, L};
|
{NewMsgs, IsComplete, L};
|
||||||
#rsm_in{max = Max} ->
|
#rsm_set{max = Max} ->
|
||||||
{NewMsgs, IsComplete} = filter_by_max(Msgs, Max),
|
{NewMsgs, IsComplete} = filter_by_max(Msgs, Max),
|
||||||
{NewMsgs, IsComplete, L};
|
{NewMsgs, IsComplete, L};
|
||||||
_ ->
|
_ ->
|
||||||
{Msgs, true, L}
|
{Msgs, true, L}
|
||||||
end;
|
end;
|
||||||
select(LServer, JidRequestor, JidArchive, Start, End, With, RSM, MsgType) ->
|
select(LServer, JidRequestor, JidArchive, Query, MsgType) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:select(LServer, JidRequestor, JidArchive, Start, End, With, RSM,
|
Mod:select(LServer, JidRequestor, JidArchive, Query, MsgType).
|
||||||
MsgType).
|
|
||||||
|
|
||||||
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
||||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
||||||
Pkt2 = maybe_update_from_to(Pkt1, JidRequestor, JidArchive, Peer, MsgType,
|
Pkt2 = maybe_update_from_to(Pkt1, JidRequestor, JidArchive, Peer, MsgType,
|
||||||
Nick),
|
Nick),
|
||||||
Pkt3 = #xmlel{name = <<"forwarded">>,
|
#forwarded{sub_els = [Pkt2],
|
||||||
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
|
delay = #delay{stamp = TS, from = jid:make(LServer)}}.
|
||||||
children = [fxml:replace_tag_attr(
|
|
||||||
<<"xmlns">>, <<"jabber:client">>, Pkt2)]},
|
|
||||||
jlib:add_delay_info(Pkt3, LServer, TS).
|
|
||||||
|
|
||||||
maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor, JidArchive,
|
maybe_update_from_to(#message{sub_els = Els} = Pkt, JidRequestor, JidArchive,
|
||||||
Peer, {groupchat, Role,
|
Peer, {groupchat, Role,
|
||||||
#state{config = #config{anonymous = Anon}}},
|
#state{config = #config{anonymous = Anon}}},
|
||||||
Nick) ->
|
Nick) ->
|
||||||
@ -919,18 +835,13 @@ maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor, JidArchive,
|
|||||||
end,
|
end,
|
||||||
Items = case ExposeJID of
|
Items = case ExposeJID of
|
||||||
true ->
|
true ->
|
||||||
[#xmlel{name = <<"x">>,
|
[#muc_user{items = [#muc_item{jid = Peer}]}];
|
||||||
attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"item">>,
|
|
||||||
attrs = [{<<"jid">>,
|
|
||||||
jid:to_string(Peer)}]}]}];
|
|
||||||
false ->
|
false ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
Pkt1 = Pkt#xmlel{children = Items ++ Els},
|
Pkt#message{from = jid:replace_resource(JidArchive, Nick),
|
||||||
Pkt2 = jlib:replace_from(jid:replace_resource(JidArchive, Nick), Pkt1),
|
to = undefined,
|
||||||
jlib:remove_attr(<<"to">>, Pkt2);
|
sub_els = Items ++ Els};
|
||||||
maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) ->
|
maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) ->
|
||||||
Pkt.
|
Pkt.
|
||||||
|
|
||||||
@ -966,62 +877,46 @@ is_bare_copy(#jid{luser = U, lserver = S, lresource = R}, To) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
|
-spec send([{binary(), integer(), xmlel()}],
|
||||||
QID = fxml:get_tag_attr_s(<<"queryid">>, SubEl),
|
non_neg_integer(), boolean(), iq()) -> iq() | ignore.
|
||||||
NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
|
send(Msgs, Count, IsComplete,
|
||||||
QIDAttr = if QID /= <<>> ->
|
#iq{from = From, to = To,
|
||||||
[{<<"queryid">>, QID}];
|
sub_els = [#mam_query{id = QID, xmlns = NS}]} = IQ) ->
|
||||||
true ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
CompleteAttr = if NS == ?NS_MAM_TMP ->
|
|
||||||
[];
|
|
||||||
NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
|
|
||||||
[{<<"complete">>, jlib:atom_to_binary(IsComplete)}]
|
|
||||||
end,
|
|
||||||
Els = lists:map(
|
Els = lists:map(
|
||||||
fun({ID, _IDInt, El}) ->
|
fun({ID, _IDInt, El}) ->
|
||||||
#xmlel{name = <<"message">>,
|
#message{sub_els = [#mam_result{xmlns = NS,
|
||||||
children = [#xmlel{name = <<"result">>,
|
id = ID,
|
||||||
attrs = [{<<"xmlns">>, NS},
|
queryid = QID,
|
||||||
{<<"id">>, ID}|QIDAttr],
|
sub_els = [El]}]}
|
||||||
children = [El]}]}
|
|
||||||
end, Msgs),
|
end, Msgs),
|
||||||
RSMOut = make_rsm_out(Msgs, RSM, Count, QIDAttr ++ CompleteAttr, NS),
|
RSMOut = make_rsm_out(Msgs, Count),
|
||||||
|
Result = if NS == ?NS_MAM_TMP ->
|
||||||
|
#mam_query{xmlns = NS, id = QID, rsm = RSMOut};
|
||||||
|
true ->
|
||||||
|
#mam_fin{id = QID, rsm = RSMOut, complete = IsComplete}
|
||||||
|
end,
|
||||||
if NS == ?NS_MAM_TMP; NS == ?NS_MAM_1 ->
|
if NS == ?NS_MAM_TMP; NS == ?NS_MAM_1 ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(El) ->
|
fun(El) ->
|
||||||
ejabberd_router:route(To, From, El)
|
ejabberd_router:route(To, From, El)
|
||||||
end, Els),
|
end, Els),
|
||||||
IQ#iq{type = result, sub_el = RSMOut};
|
xmpp:make_iq_result(IQ, Result);
|
||||||
NS == ?NS_MAM_0 ->
|
NS == ?NS_MAM_0 ->
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(To, From, xmpp:make_iq_result(IQ)),
|
||||||
To, From, jlib:iq_to_xml(IQ#iq{type = result, sub_el = []})),
|
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(El) ->
|
fun(El) ->
|
||||||
ejabberd_router:route(To, From, El)
|
ejabberd_router:route(To, From, El)
|
||||||
end, Els),
|
end, Els),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(To, From, #message{sub_els = [Result]}),
|
||||||
To, From, #xmlel{name = <<"message">>,
|
|
||||||
children = RSMOut}),
|
|
||||||
ignore
|
ignore
|
||||||
end.
|
end.
|
||||||
|
|
||||||
make_rsm_out([], _, Count, Attrs, NS) ->
|
-spec make_rsm_out([{binary(), integer(), xmlel()}], non_neg_integer()) -> rsm_set().
|
||||||
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
|
make_rsm_out([], Count) ->
|
||||||
true -> <<"fin">>
|
#rsm_set{count = Count};
|
||||||
end,
|
make_rsm_out([{FirstID, _, _}|_] = Msgs, Count) ->
|
||||||
[#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
|
|
||||||
children = jlib:rsm_encode(#rsm_out{count = Count})}];
|
|
||||||
make_rsm_out([{FirstID, _, _}|_] = Msgs, _, Count, Attrs, NS) ->
|
|
||||||
{LastID, _, _} = lists:last(Msgs),
|
{LastID, _, _} = lists:last(Msgs),
|
||||||
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
|
#rsm_set{first = #rsm_first{data = FirstID}, last = LastID, count = Count}.
|
||||||
true -> <<"fin">>
|
|
||||||
end,
|
|
||||||
[#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
|
|
||||||
children = jlib:rsm_encode(
|
|
||||||
#rsm_out{first = FirstID, count = Count,
|
|
||||||
last = LastID})}].
|
|
||||||
|
|
||||||
filter_by_max(Msgs, undefined) ->
|
filter_by_max(Msgs, undefined) ->
|
||||||
{Msgs, true};
|
{Msgs, true};
|
||||||
@ -1030,23 +925,24 @@ filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
|
|||||||
filter_by_max(_Msgs, _Junk) ->
|
filter_by_max(_Msgs, _Junk) ->
|
||||||
{[], true}.
|
{[], true}.
|
||||||
|
|
||||||
|
-spec limit_max(rsm_set(), binary()) -> rsm_set().
|
||||||
limit_max(RSM, ?NS_MAM_TMP) ->
|
limit_max(RSM, ?NS_MAM_TMP) ->
|
||||||
RSM; % XEP-0313 v0.2 doesn't require clients to support RSM.
|
RSM; % XEP-0313 v0.2 doesn't require clients to support RSM.
|
||||||
limit_max(#rsm_in{max = Max} = RSM, _NS) when not is_integer(Max) ->
|
limit_max(#rsm_set{max = Max} = RSM, _NS) when not is_integer(Max) ->
|
||||||
RSM#rsm_in{max = ?DEF_PAGE_SIZE};
|
RSM#rsm_set{max = ?DEF_PAGE_SIZE};
|
||||||
limit_max(#rsm_in{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE ->
|
limit_max(#rsm_set{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE ->
|
||||||
RSM#rsm_in{max = ?MAX_PAGE_SIZE};
|
RSM#rsm_set{max = ?MAX_PAGE_SIZE};
|
||||||
limit_max(RSM, _NS) ->
|
limit_max(RSM, _NS) ->
|
||||||
RSM.
|
RSM.
|
||||||
|
|
||||||
match_interval(Now, Start, End) ->
|
match_interval(Now, Start, End) ->
|
||||||
(Now >= Start) and (Now =< End).
|
(Now >= Start) and (Now =< End).
|
||||||
|
|
||||||
match_rsm(Now, #rsm_in{id = ID, direction = aft}) when ID /= <<"">> ->
|
match_rsm(Now, #rsm_set{'after' = ID}) when is_binary(ID), ID /= <<"">> ->
|
||||||
Now1 = (catch usec_to_now(jlib:binary_to_integer(ID))),
|
Now1 = (catch usec_to_now(binary_to_integer(ID))),
|
||||||
Now > Now1;
|
Now > Now1;
|
||||||
match_rsm(Now, #rsm_in{id = ID, direction = before}) when ID /= <<"">> ->
|
match_rsm(Now, #rsm_set{before = ID}) when is_binary(ID), ID /= <<"">> ->
|
||||||
Now1 = (catch usec_to_now(jlib:binary_to_integer(ID))),
|
Now1 = (catch usec_to_now(binary_to_integer(ID))),
|
||||||
Now < Now1;
|
Now < Now1;
|
||||||
match_rsm(_Now, _) ->
|
match_rsm(_Now, _) ->
|
||||||
true.
|
true.
|
||||||
@ -1066,15 +962,10 @@ datetime_to_now(DateTime, USecs) ->
|
|||||||
calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
|
calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
|
||||||
{Seconds div 1000000, Seconds rem 1000000, USecs}.
|
{Seconds div 1000000, Seconds rem 1000000, USecs}.
|
||||||
|
|
||||||
get_jids(Els) ->
|
get_jids(undefined) ->
|
||||||
lists:flatmap(
|
[];
|
||||||
fun(#xmlel{name = <<"jid">>} = El) ->
|
get_jids(Js) ->
|
||||||
J = jid:from_string(fxml:get_tag_cdata(El)),
|
[jid:tolower(jid:remove_resource(J)) || J <- Js].
|
||||||
[jid:tolower(jid:remove_resource(J)),
|
|
||||||
jid:tolower(J)];
|
|
||||||
(_) ->
|
|
||||||
[]
|
|
||||||
end, Els).
|
|
||||||
|
|
||||||
get_commands_spec() ->
|
get_commands_spec() ->
|
||||||
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
|
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
|
||||||
|
@ -12,10 +12,10 @@
|
|||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||||
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
|
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
|
||||||
|
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("mod_mam.hrl").
|
-include("mod_mam.hrl").
|
||||||
|
|
||||||
@ -132,7 +132,8 @@ get_prefs(LUser, LServer) ->
|
|||||||
|
|
||||||
select(_LServer, JidRequestor,
|
select(_LServer, JidRequestor,
|
||||||
#jid{luser = LUser, lserver = LServer} = JidArchive,
|
#jid{luser = LUser, lserver = LServer} = JidArchive,
|
||||||
Start, End, With, RSM, MsgType) ->
|
#mam_query{start = Start, 'end' = End,
|
||||||
|
with = With, rsm = RSM}, MsgType) ->
|
||||||
MS = make_matchspec(LUser, LServer, Start, End, With),
|
MS = make_matchspec(LUser, LServer, Start, End, With),
|
||||||
Msgs = mnesia:dirty_select(archive_msg, MS),
|
Msgs = mnesia:dirty_select(archive_msg, MS),
|
||||||
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
|
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
|
||||||
@ -174,7 +175,7 @@ make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) ->
|
|||||||
Peer == With ->
|
Peer == With ->
|
||||||
Msg
|
Msg
|
||||||
end);
|
end);
|
||||||
make_matchspec(LUser, LServer, Start, End, none) ->
|
make_matchspec(LUser, LServer, Start, End, undefined) ->
|
||||||
ets:fun2ms(
|
ets:fun2ms(
|
||||||
fun(#archive_msg{timestamp = TS,
|
fun(#archive_msg{timestamp = TS,
|
||||||
us = US,
|
us = US,
|
||||||
@ -184,28 +185,27 @@ make_matchspec(LUser, LServer, Start, End, none) ->
|
|||||||
Msg
|
Msg
|
||||||
end).
|
end).
|
||||||
|
|
||||||
filter_by_rsm(Msgs, none) ->
|
filter_by_rsm(Msgs, undefined) ->
|
||||||
{Msgs, true};
|
{Msgs, true};
|
||||||
filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 ->
|
filter_by_rsm(_Msgs, #rsm_set{max = Max}) when Max < 0 ->
|
||||||
{[], true};
|
{[], true};
|
||||||
filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
|
filter_by_rsm(Msgs, #rsm_set{max = Max, before = Before, 'after' = After}) ->
|
||||||
NewMsgs = case Direction of
|
NewMsgs = if is_binary(After), After /= <<"">> ->
|
||||||
aft when ID /= <<"">> ->
|
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun(#archive_msg{id = I}) ->
|
fun(#archive_msg{id = I}) ->
|
||||||
?BIN_GREATER_THAN(I, ID)
|
?BIN_GREATER_THAN(I, After)
|
||||||
end, Msgs);
|
end, Msgs);
|
||||||
before when ID /= <<"">> ->
|
is_binary(Before), Before /= <<"">> ->
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(#archive_msg{id = I} = Msg, Acc)
|
fun(#archive_msg{id = I} = Msg, Acc)
|
||||||
when ?BIN_LESS_THAN(I, ID) ->
|
when ?BIN_LESS_THAN(I, Before) ->
|
||||||
[Msg|Acc];
|
[Msg|Acc];
|
||||||
(_, Acc) ->
|
(_, Acc) ->
|
||||||
Acc
|
Acc
|
||||||
end, [], Msgs);
|
end, [], Msgs);
|
||||||
before when ID == <<"">> ->
|
is_binary(Before), Before == <<"">> ->
|
||||||
lists:reverse(Msgs);
|
lists:reverse(Msgs);
|
||||||
_ ->
|
true ->
|
||||||
Msgs
|
Msgs
|
||||||
end,
|
end,
|
||||||
filter_by_max(NewMsgs, Max).
|
filter_by_max(NewMsgs, Max).
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||||
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
|
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
|
||||||
|
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("mod_mam.hrl").
|
-include("mod_mam.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("ejabberd_sql_pt.hrl").
|
-include("ejabberd_sql_pt.hrl").
|
||||||
@ -51,9 +51,7 @@ delete_old_messages(ServerHost, TimeStamp, Type) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
extended_fields() ->
|
extended_fields() ->
|
||||||
[#xmlel{name = <<"field">>,
|
[#xdata_field{type = 'text-single', var = <<"withtext">>}].
|
||||||
attrs = [{<<"type">>, <<"text-single">>},
|
|
||||||
{<<"var">>, <<"withtext">>}]}].
|
|
||||||
|
|
||||||
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) ->
|
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) ->
|
||||||
TSinteger = p1_time_compat:system_time(micro_seconds),
|
TSinteger = p1_time_compat:system_time(micro_seconds),
|
||||||
@ -126,13 +124,12 @@ get_prefs(LUser, LServer) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
||||||
Start, End, With, RSM, MsgType) ->
|
MAMQuery, MsgType) ->
|
||||||
User = case MsgType of
|
User = case MsgType of
|
||||||
chat -> LUser;
|
chat -> LUser;
|
||||||
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
|
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
|
||||||
end,
|
end,
|
||||||
{Query, CountQuery} = make_sql_query(User, LServer,
|
{Query, CountQuery} = make_sql_query(User, LServer, MAMQuery),
|
||||||
Start, End, With, RSM),
|
|
||||||
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
|
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
|
||||||
% reasonable limit on how many stanzas may be pushed to a client in one
|
% reasonable limit on how many stanzas may be pushed to a client in one
|
||||||
% request. If a query returns a number of stanzas greater than this limit
|
% request. If a query returns a number of stanzas greater than this limit
|
||||||
@ -142,10 +139,7 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
|||||||
case {ejabberd_sql:sql_query(LServer, Query),
|
case {ejabberd_sql:sql_query(LServer, Query),
|
||||||
ejabberd_sql:sql_query(LServer, CountQuery)} of
|
ejabberd_sql:sql_query(LServer, CountQuery)} of
|
||||||
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
||||||
{Max, Direction} = case RSM of
|
{Max, Direction, _} = get_max_direction_id(MAMQuery#mam_query.rsm),
|
||||||
#rsm_in{max = M, direction = D} -> {M, D};
|
|
||||||
_ -> {undefined, undefined}
|
|
||||||
end,
|
|
||||||
{Res1, IsComplete} =
|
{Res1, IsComplete} =
|
||||||
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
|
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
|
||||||
if Direction == before ->
|
if Direction == before ->
|
||||||
@ -200,15 +194,10 @@ usec_to_now(Int) ->
|
|||||||
Sec = Secs rem 1000000,
|
Sec = Secs rem 1000000,
|
||||||
{MSec, Sec, USec}.
|
{MSec, Sec, USec}.
|
||||||
|
|
||||||
make_sql_query(User, LServer, Start, End, With, RSM) ->
|
make_sql_query(User, LServer,
|
||||||
{Max, Direction, ID} = case RSM of
|
#mam_query{start = Start, 'end' = End, with = With,
|
||||||
#rsm_in{} ->
|
withtext = WithText, rsm = RSM}) ->
|
||||||
{RSM#rsm_in.max,
|
{Max, Direction, ID} = get_max_direction_id(RSM),
|
||||||
RSM#rsm_in.direction,
|
|
||||||
RSM#rsm_in.id};
|
|
||||||
none ->
|
|
||||||
{none, none, <<>>}
|
|
||||||
end,
|
|
||||||
ODBCType = ejabberd_config:get_option(
|
ODBCType = ejabberd_config:get_option(
|
||||||
{sql_type, LServer},
|
{sql_type, LServer},
|
||||||
ejabberd_sql:opt_type(sql_type)),
|
ejabberd_sql:opt_type(sql_type)),
|
||||||
@ -228,12 +217,16 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|||||||
true ->
|
true ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
WithClause = case With of
|
WithTextClause = case WithText of
|
||||||
{text, <<>>} ->
|
{text, <<>>} ->
|
||||||
[];
|
[];
|
||||||
{text, Txt} ->
|
{text, Txt} ->
|
||||||
[<<" and match (txt) against ('">>,
|
[<<" and match (txt) against ('">>,
|
||||||
Escape(Txt), <<"')">>];
|
Escape(Txt), <<"')">>];
|
||||||
|
undefined ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
WithClause = case catch jid:tolower(With) of
|
||||||
{_, _, <<>>} ->
|
{_, _, <<>>} ->
|
||||||
[<<" and bare_peer='">>,
|
[<<" and bare_peer='">>,
|
||||||
Escape(jid:to_string(With)),
|
Escape(jid:to_string(With)),
|
||||||
@ -242,7 +235,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|||||||
[<<" and peer='">>,
|
[<<" and peer='">>,
|
||||||
Escape(jid:to_string(With)),
|
Escape(jid:to_string(With)),
|
||||||
<<"'">>];
|
<<"'">>];
|
||||||
none ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
PageClause = case catch jlib:binary_to_integer(ID) of
|
PageClause = case catch jlib:binary_to_integer(ID) of
|
||||||
@ -250,7 +243,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|||||||
case Direction of
|
case Direction of
|
||||||
before ->
|
before ->
|
||||||
[<<" AND timestamp < ">>, ID];
|
[<<" AND timestamp < ">>, ID];
|
||||||
aft ->
|
'after' ->
|
||||||
[<<" AND timestamp > ">>, ID];
|
[<<" AND timestamp > ">>, ID];
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
@ -276,7 +269,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|||||||
|
|
||||||
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
|
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
|
||||||
" FROM archive WHERE username='">>,
|
" FROM archive WHERE username='">>,
|
||||||
SUser, <<"'">>, WithClause, StartClause, EndClause,
|
SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause,
|
||||||
PageClause],
|
PageClause],
|
||||||
|
|
||||||
QueryPage =
|
QueryPage =
|
||||||
@ -294,4 +287,20 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|||||||
end,
|
end,
|
||||||
{QueryPage,
|
{QueryPage,
|
||||||
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
|
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
|
||||||
SUser, <<"'">>, WithClause, StartClause, EndClause, <<";">>]}.
|
SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, <<";">>]}.
|
||||||
|
|
||||||
|
-spec get_max_direction_id(rsm_set() | undefined) ->
|
||||||
|
{integer() | undefined,
|
||||||
|
before | 'after' | undefined,
|
||||||
|
binary()}.
|
||||||
|
get_max_direction_id(RSM) ->
|
||||||
|
case RSM of
|
||||||
|
#rsm_set{max = Max, before = Before} when is_binary(Before) ->
|
||||||
|
{Max, before, Before};
|
||||||
|
#rsm_set{max = Max, 'after' = After} when is_binary(After) ->
|
||||||
|
{Max, 'after', After};
|
||||||
|
#rsm_set{max = Max} ->
|
||||||
|
{Max, undefined, <<>>};
|
||||||
|
_ ->
|
||||||
|
{undefined, undefined, <<>>}
|
||||||
|
end.
|
||||||
|
623
src/mod_muc.erl
623
src/mod_muc.erl
@ -43,7 +43,12 @@
|
|||||||
forget_room/3,
|
forget_room/3,
|
||||||
create_room/5,
|
create_room/5,
|
||||||
shutdown_rooms/1,
|
shutdown_rooms/1,
|
||||||
process_iq_disco_items/4,
|
process_disco_info/1,
|
||||||
|
process_disco_items/1,
|
||||||
|
process_vcard/1,
|
||||||
|
process_register/1,
|
||||||
|
process_muc_unique/1,
|
||||||
|
process_mucsub/1,
|
||||||
broadcast_service_message/2,
|
broadcast_service_message/2,
|
||||||
export/1,
|
export/1,
|
||||||
import/1,
|
import/1,
|
||||||
@ -58,7 +63,7 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("mod_muc.hrl").
|
-include("mod_muc.hrl").
|
||||||
|
|
||||||
-record(state,
|
-record(state,
|
||||||
@ -154,17 +159,6 @@ forget_room(ServerHost, Host, Name) ->
|
|||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:forget_room(LServer, Host, Name).
|
Mod:forget_room(LServer, Host, Name).
|
||||||
|
|
||||||
process_iq_disco_items(Host, From, To,
|
|
||||||
#iq{lang = Lang} = IQ) ->
|
|
||||||
Rsm = jlib:rsm_decode(IQ),
|
|
||||||
DiscoNode = fxml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el),
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"query">>,
|
|
||||||
attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}],
|
|
||||||
children = iq_disco_items(Host, From, Lang, DiscoNode, Rsm)}]},
|
|
||||||
ejabberd_router:route(To, From, jlib:iq_to_xml(Res)).
|
|
||||||
|
|
||||||
can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false;
|
can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false;
|
||||||
can_use_nick(ServerHost, Host, JID, Nick) ->
|
can_use_nick(ServerHost, Host, JID, Nick) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
@ -176,6 +170,8 @@ can_use_nick(ServerHost, Host, JID, Nick) ->
|
|||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
|
one_queue),
|
||||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||||
<<"conference.@HOST@">>),
|
<<"conference.@HOST@">>),
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
@ -255,6 +251,18 @@ init([Host, Opts]) ->
|
|||||||
RoomShaper = gen_mod:get_opt(room_shaper, Opts,
|
RoomShaper = gen_mod:get_opt(room_shaper, Opts,
|
||||||
fun(A) when is_atom(A) -> A end,
|
fun(A) when is_atom(A) -> A end,
|
||||||
none),
|
none),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER,
|
||||||
|
?MODULE, process_register, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD,
|
||||||
|
?MODULE, process_vcard, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB,
|
||||||
|
?MODULE, process_mucsub, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE,
|
||||||
|
?MODULE, process_muc_unique, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
|
||||||
|
?MODULE, process_disco_info, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS,
|
||||||
|
?MODULE, process_disco_items, IQDisc),
|
||||||
ejabberd_router:register_route(MyHost, Host),
|
ejabberd_router:register_route(MyHost, Host),
|
||||||
load_permanent_rooms(MyHost, Host,
|
load_permanent_rooms(MyHost, Host,
|
||||||
{Access, AccessCreate, AccessAdmin, AccessPersistent},
|
{Access, AccessCreate, AccessAdmin, AccessPersistent},
|
||||||
@ -314,8 +322,14 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
|||||||
{noreply, State};
|
{noreply, State};
|
||||||
handle_info(_Info, State) -> {noreply, State}.
|
handle_info(_Info, State) -> {noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, State) ->
|
terminate(_Reason, #state{host = MyHost}) ->
|
||||||
ejabberd_router:unregister_route(State#state.host),
|
ejabberd_router:unregister_route(MyHost),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||||
@ -331,197 +345,162 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
|
|||||||
allow ->
|
allow ->
|
||||||
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||||
From, To, Packet, DefRoomOpts);
|
From, To, Packet, DefRoomOpts);
|
||||||
_ ->
|
deny ->
|
||||||
#xmlel{attrs = Attrs} = Packet,
|
Lang = xmpp:get_lang(Packet),
|
||||||
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
|
|
||||||
ErrText = <<"Access denied by service policy">>,
|
ErrText = <<"Access denied by service policy">>,
|
||||||
Err = jlib:make_error_reply(Packet,
|
Err = xmpp:err_forbidden(ErrText, Lang),
|
||||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
ejabberd_router:route_error(To, From, Packet, Err)
|
||||||
ejabberd_router:route_error(To, From, Err, Packet)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
|
||||||
|
From, #jid{luser = <<"">>, lresource = <<"">>} = To,
|
||||||
|
#iq{} = IQ, _DefRoomOpts) ->
|
||||||
|
ejabberd_local:process_iq(From, To, IQ);
|
||||||
|
do_route1(Host, ServerHost, Access, _HistorySize, _RoomShaper,
|
||||||
|
From, #jid{luser = <<"">>, lresource = <<"">>} = To,
|
||||||
|
#message{lang = Lang, body = Body, type = Type} = Packet, _) ->
|
||||||
|
{_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = Access,
|
||||||
|
if Type == error ->
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
case acl:match_rule(ServerHost, AccessAdmin, From) of
|
||||||
|
allow ->
|
||||||
|
Msg = xmpp:get_text(Body),
|
||||||
|
broadcast_service_message(Host, Msg);
|
||||||
|
deny ->
|
||||||
|
ErrText = <<"Only service administrators are allowed "
|
||||||
|
"to send service messages">>,
|
||||||
|
Err = xmpp:make_error(
|
||||||
|
Packet, xmpp:err_forbidden(ErrText, Lang)),
|
||||||
|
ejabberd_router:route(To, From, Err)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
|
||||||
|
From, #jid{luser = <<"">>} = To, Packet, _DefRoomOpts) ->
|
||||||
|
Err = xmpp:err_item_not_found(),
|
||||||
|
ejabberd_router:route_error(To, From, Packet, Err);
|
||||||
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||||
From, To, Packet, DefRoomOpts) ->
|
From, To, Packet, DefRoomOpts) ->
|
||||||
{_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access,
|
{_AccessRoute, AccessCreate, _AccessAdmin, _AccessPersistent} = Access,
|
||||||
{Room, _, Nick} = jid:tolower(To),
|
{Room, _, Nick} = jid:tolower(To),
|
||||||
#xmlel{name = Name, attrs = Attrs} = Packet,
|
|
||||||
case Room of
|
|
||||||
<<"">> ->
|
|
||||||
case Nick of
|
|
||||||
<<"">> ->
|
|
||||||
case Name of
|
|
||||||
<<"iq">> ->
|
|
||||||
case jlib:iq_query_info(Packet) of
|
|
||||||
#iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS,
|
|
||||||
sub_el = _SubEl, lang = Lang} =
|
|
||||||
IQ ->
|
|
||||||
Info = ejabberd_hooks:run_fold(disco_info,
|
|
||||||
ServerHost, [],
|
|
||||||
[ServerHost, ?MODULE,
|
|
||||||
<<"">>, <<"">>]),
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"query">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"xmlns">>, XMLNS}],
|
|
||||||
children =
|
|
||||||
iq_disco_info(
|
|
||||||
ServerHost, Lang) ++
|
|
||||||
Info}]},
|
|
||||||
ejabberd_router:route(To, From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
#iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ ->
|
|
||||||
spawn(?MODULE, process_iq_disco_items,
|
|
||||||
[Host, From, To, IQ]);
|
|
||||||
#iq{type = get, xmlns = (?NS_REGISTER) = XMLNS,
|
|
||||||
lang = Lang, sub_el = _SubEl} =
|
|
||||||
IQ ->
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"query">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"xmlns">>, XMLNS}],
|
|
||||||
children =
|
|
||||||
iq_get_register_info(ServerHost,
|
|
||||||
Host,
|
|
||||||
From,
|
|
||||||
Lang)}]},
|
|
||||||
ejabberd_router:route(To, From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
#iq{type = set, xmlns = (?NS_REGISTER) = XMLNS,
|
|
||||||
lang = Lang, sub_el = SubEl} =
|
|
||||||
IQ ->
|
|
||||||
case process_iq_register_set(ServerHost, Host, From,
|
|
||||||
SubEl, Lang)
|
|
||||||
of
|
|
||||||
{result, IQRes} ->
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"query">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"xmlns">>,
|
|
||||||
XMLNS}],
|
|
||||||
children = IQRes}]},
|
|
||||||
ejabberd_router:route(To, From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
{error, Error} ->
|
|
||||||
Err = jlib:make_error_reply(Packet, Error),
|
|
||||||
ejabberd_router:route(To, From, Err)
|
|
||||||
end;
|
|
||||||
#iq{type = get, xmlns = (?NS_VCARD) = XMLNS,
|
|
||||||
lang = Lang, sub_el = _SubEl} =
|
|
||||||
IQ ->
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"vCard">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"xmlns">>, XMLNS}],
|
|
||||||
children =
|
|
||||||
iq_get_vcard(Lang)}]},
|
|
||||||
ejabberd_router:route(To, From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
#iq{type = get, xmlns = ?NS_MUCSUB,
|
|
||||||
sub_el = #xmlel{name = <<"subscriptions">>} = SubEl} = IQ ->
|
|
||||||
RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
|
|
||||||
Subs = lists:map(
|
|
||||||
fun(J) ->
|
|
||||||
#xmlel{name = <<"subscription">>,
|
|
||||||
attrs = [{<<"jid">>,
|
|
||||||
jid:to_string(J)}]}
|
|
||||||
end, RoomJIDs),
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el = [SubEl#xmlel{children = Subs}]},
|
|
||||||
ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
|
|
||||||
#iq{type = get, xmlns = ?NS_MUC_UNIQUE} = IQ ->
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el =
|
|
||||||
[#xmlel{name = <<"unique">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"xmlns">>,
|
|
||||||
?NS_MUC_UNIQUE}],
|
|
||||||
children =
|
|
||||||
[iq_get_unique(From)]}]},
|
|
||||||
ejabberd_router:route(To, From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
#iq{} ->
|
|
||||||
Err = jlib:make_error_reply(Packet,
|
|
||||||
?ERR_FEATURE_NOT_IMPLEMENTED),
|
|
||||||
ejabberd_router:route(To, From, Err);
|
|
||||||
_ -> ok
|
|
||||||
end;
|
|
||||||
<<"message">> ->
|
|
||||||
case fxml:get_attr_s(<<"type">>, Attrs) of
|
|
||||||
<<"error">> -> ok;
|
|
||||||
_ ->
|
|
||||||
case acl:match_rule(ServerHost, AccessAdmin, From)
|
|
||||||
of
|
|
||||||
allow ->
|
|
||||||
Msg = fxml:get_path_s(Packet,
|
|
||||||
[{elem, <<"body">>},
|
|
||||||
cdata]),
|
|
||||||
broadcast_service_message(Host, Msg);
|
|
||||||
_ ->
|
|
||||||
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
|
|
||||||
ErrText =
|
|
||||||
<<"Only service administrators are allowed "
|
|
||||||
"to send service messages">>,
|
|
||||||
Err = jlib:make_error_reply(Packet,
|
|
||||||
?ERRT_FORBIDDEN(Lang,
|
|
||||||
ErrText)),
|
|
||||||
ejabberd_router:route(To, From, Err)
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
<<"presence">> -> ok
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
case fxml:get_attr_s(<<"type">>, Attrs) of
|
|
||||||
<<"error">> -> ok;
|
|
||||||
<<"result">> -> ok;
|
|
||||||
_ ->
|
|
||||||
Err = jlib:make_error_reply(Packet,
|
|
||||||
?ERR_ITEM_NOT_FOUND),
|
|
||||||
ejabberd_router:route(To, From, Err)
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
|
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
|
||||||
[] ->
|
[] ->
|
||||||
Type = fxml:get_attr_s(<<"type">>, Attrs),
|
case Packet of
|
||||||
case {Name, Type} of
|
#presence{type = available, lang = Lang} ->
|
||||||
{<<"presence">>, <<"">>} ->
|
case check_user_can_create_room(
|
||||||
case check_user_can_create_room(ServerHost,
|
ServerHost, AccessCreate, From, Room) and
|
||||||
AccessCreate, From, Room) and
|
|
||||||
check_create_roomid(ServerHost, Room) of
|
check_create_roomid(ServerHost, Room) of
|
||||||
true ->
|
true ->
|
||||||
{ok, Pid} = start_new_room(Host, ServerHost, Access,
|
{ok, Pid} = start_new_room(
|
||||||
|
Host, ServerHost, Access,
|
||||||
Room, HistorySize,
|
Room, HistorySize,
|
||||||
RoomShaper, From, Nick, DefRoomOpts),
|
RoomShaper, From, Nick, DefRoomOpts),
|
||||||
register_room(Host, Room, Pid),
|
register_room(Host, Room, Pid),
|
||||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||||
ok;
|
ok;
|
||||||
false ->
|
false ->
|
||||||
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
|
|
||||||
ErrText = <<"Room creation is denied by service policy">>,
|
ErrText = <<"Room creation is denied by service policy">>,
|
||||||
Err = jlib:make_error_reply(
|
Err = xmpp:make_error(
|
||||||
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
Packet, xmpp:err_forbidden(ErrText, Lang)),
|
||||||
ejabberd_router:route(To, From, Err)
|
ejabberd_router:route(To, From, Err)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
|
Lang = xmpp:get_lang(Packet),
|
||||||
ErrText = <<"Conference room does not exist">>,
|
ErrText = <<"Conference room does not exist">>,
|
||||||
Err = jlib:make_error_reply(Packet,
|
Err = xmpp:err_item_not_found(ErrText, Lang),
|
||||||
?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
ejabberd_router:route_error(To, From, Packet, Err)
|
||||||
ejabberd_router:route(To, From, Err)
|
|
||||||
end;
|
end;
|
||||||
[R] ->
|
[R] ->
|
||||||
Pid = R#muc_online_room.pid,
|
Pid = R#muc_online_room.pid,
|
||||||
?DEBUG("MUC: send to process ~p~n", [Pid]),
|
?DEBUG("MUC: send to process ~p~n", [Pid]),
|
||||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||||
ok
|
ok
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec process_vcard(iq()) -> iq().
|
||||||
|
process_vcard(#iq{type = get, lang = Lang} = IQ) ->
|
||||||
|
Desc = translate:translate(Lang, <<"ejabberd MUC module">>),
|
||||||
|
Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
|
||||||
|
xmpp:make_iq_result(
|
||||||
|
IQ, #vcard_temp{fn = <<"ejabberd/mod_muc">>,
|
||||||
|
url = ?EJABBERD_URI,
|
||||||
|
desc = <<Desc/binary, $\n, Copyright/binary>>});
|
||||||
|
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)).
|
||||||
|
|
||||||
|
-spec process_register(iq()) -> iq().
|
||||||
|
process_register(#iq{type = get, from = From, to = To, lang = Lang} = IQ) ->
|
||||||
|
Host = To#jid.lserver,
|
||||||
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
|
xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
||||||
|
process_register(#iq{type = set, from = From, to = To,
|
||||||
|
lang = Lang, sub_els = [El]} = IQ) ->
|
||||||
|
Host = To#jid.lserver,
|
||||||
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
|
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
||||||
|
{result, Result} ->
|
||||||
|
xmpp:make_iq_result(IQ, Result);
|
||||||
|
{error, Err} ->
|
||||||
|
xmpp:make_error(IQ, Err)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec process_disco_info(iq()) -> iq().
|
||||||
|
process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
|
process_disco_info(#iq{type = get, to = To, lang = Lang,
|
||||||
|
sub_els = [#disco_info{node = undefined}]} = IQ) ->
|
||||||
|
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
|
||||||
|
X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
|
||||||
|
[ServerHost, ?MODULE, undefined, Lang]),
|
||||||
|
MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of
|
||||||
|
true -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1];
|
||||||
|
false -> []
|
||||||
|
end,
|
||||||
|
Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
|
||||||
|
?NS_REGISTER, ?NS_MUC, ?NS_RSM,
|
||||||
|
?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE | MAMFeatures],
|
||||||
|
Identity = #identity{category = <<"conference">>,
|
||||||
|
type = <<"text">>,
|
||||||
|
name = translate:translate(Lang, <<"Chatrooms">>)},
|
||||||
|
xmpp:make_iq_result(
|
||||||
|
IQ, #disco_info{features = Features,
|
||||||
|
identities = [Identity],
|
||||||
|
xdata = X});
|
||||||
|
process_disco_info(#iq{type = get, lang = Lang} = IQ) ->
|
||||||
|
xmpp:make_error(IQ, xmpp:err_item_not_found(<<"No info available">>, Lang)).
|
||||||
|
|
||||||
|
-spec process_disco_items(iq()) -> iq().
|
||||||
|
process_disco_items(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
|
process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
|
||||||
|
sub_els = [#disco_items{node = Node, rsm = RSM}]} = IQ) ->
|
||||||
|
Host = To#jid.lserver,
|
||||||
|
xmpp:make_iq_result(
|
||||||
|
IQ, #disco_items{node = Node,
|
||||||
|
items = iq_disco_items(Host, From, Lang, Node, RSM)}).
|
||||||
|
|
||||||
|
-spec process_muc_unique(iq()) -> iq().
|
||||||
|
process_muc_unique(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
|
process_muc_unique(#iq{from = From, type = get} = IQ) ->
|
||||||
|
Name = p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
|
||||||
|
randoms:get_string()])),
|
||||||
|
xmpp:make_iq_result(IQ, #muc_unique{name = Name}).
|
||||||
|
|
||||||
|
-spec process_mucsub(iq()) -> iq().
|
||||||
|
process_mucsub(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
|
process_mucsub(#iq{type = get, from = From, to = To} = IQ) ->
|
||||||
|
Host = To#jid.lserver,
|
||||||
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
|
RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
|
||||||
|
xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs}).
|
||||||
|
|
||||||
check_user_can_create_room(ServerHost, AccessCreate,
|
check_user_can_create_room(ServerHost, AccessCreate,
|
||||||
From, _RoomID) ->
|
From, _RoomID) ->
|
||||||
case acl:match_rule(ServerHost, AccessCreate, From) of
|
case acl:match_rule(ServerHost, AccessCreate, From) of
|
||||||
@ -583,61 +562,21 @@ register_room(Host, Room, Pid) ->
|
|||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
iq_disco_items(Host, From, Lang, undefined, undefined) ->
|
||||||
iq_disco_info(ServerHost, Lang) ->
|
|
||||||
[#xmlel{name = <<"identity">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"category">>, <<"conference">>},
|
|
||||||
{<<"type">>, <<"text">>},
|
|
||||||
{<<"name">>,
|
|
||||||
translate:translate(Lang, <<"Chatrooms">>)}],
|
|
||||||
children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MUC}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MUC_UNIQUE}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_REGISTER}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_RSM}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MUCSUB}], children = []},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_VCARD}], children = []}] ++
|
|
||||||
case gen_mod:is_loaded(ServerHost, mod_mam) of
|
|
||||||
true ->
|
|
||||||
[#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MAM_TMP}]},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MAM_0}]},
|
|
||||||
#xmlel{name = <<"feature">>,
|
|
||||||
attrs = [{<<"var">>, ?NS_MAM_1}]}];
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end.
|
|
||||||
|
|
||||||
iq_disco_items(Host, From, Lang, <<>>, none) ->
|
|
||||||
Rooms = get_vh_rooms(Host),
|
Rooms = get_vh_rooms(Host),
|
||||||
case erlang:length(Rooms) < ?MAX_ROOMS_DISCOITEMS of
|
case erlang:length(Rooms) < ?MAX_ROOMS_DISCOITEMS of
|
||||||
true ->
|
true ->
|
||||||
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang});
|
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang});
|
||||||
false ->
|
false ->
|
||||||
iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, none)
|
iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, undefined)
|
||||||
end;
|
end;
|
||||||
iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, none) ->
|
iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, undefined) ->
|
||||||
XmlEmpty = #xmlel{name = <<"item">>,
|
Empty = #disco_item{jid = jid:make(<<"conference.localhost">>),
|
||||||
attrs =
|
node = <<"emptyrooms">>,
|
||||||
[{<<"jid">>, <<"conference.localhost">>},
|
name = translate:translate(Lang, <<"Empty Rooms">>)},
|
||||||
{<<"node">>, <<"emptyrooms">>},
|
|
||||||
{<<"name">>, translate:translate(Lang, <<"Empty Rooms">>)}],
|
|
||||||
children = []},
|
|
||||||
Query = {get_disco_item, only_non_empty, From, Lang},
|
Query = {get_disco_item, only_non_empty, From, Lang},
|
||||||
[XmlEmpty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
|
[Empty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
|
||||||
iq_disco_items(Host, From, Lang, <<"emptyrooms">>, none) ->
|
iq_disco_items(Host, From, Lang, <<"emptyrooms">>, undefined) ->
|
||||||
iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang});
|
iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang});
|
||||||
iq_disco_items(Host, From, Lang, _DiscoNode, Rsm) ->
|
iq_disco_items(Host, From, Lang, _DiscoNode, Rsm) ->
|
||||||
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
||||||
@ -645,62 +584,55 @@ iq_disco_items(Host, From, Lang, _DiscoNode, Rsm) ->
|
|||||||
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut.
|
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut.
|
||||||
|
|
||||||
iq_disco_items_list(Host, Rooms, Query) ->
|
iq_disco_items_list(Host, Rooms, Query) ->
|
||||||
lists:zf(fun (#muc_online_room{name_host =
|
lists:zf(
|
||||||
{Name, _Host},
|
fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||||
pid = Pid}) ->
|
case catch gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
|
||||||
case catch gen_fsm:sync_send_all_state_event(Pid,
|
|
||||||
Query,
|
|
||||||
100)
|
|
||||||
of
|
|
||||||
{item, Desc} ->
|
{item, Desc} ->
|
||||||
flush(),
|
flush(),
|
||||||
{true,
|
{true, #disco_item{jid = jid:make(Name, Host),
|
||||||
#xmlel{name = <<"item">>,
|
name = Desc}};
|
||||||
attrs =
|
_ ->
|
||||||
[{<<"jid">>,
|
false
|
||||||
jid:to_string({Name, Host,
|
|
||||||
<<"">>})},
|
|
||||||
{<<"name">>, Desc}],
|
|
||||||
children = []}};
|
|
||||||
_ -> false
|
|
||||||
end
|
end
|
||||||
end, Rooms).
|
end, Rooms).
|
||||||
|
|
||||||
get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
|
get_vh_rooms(_, _) ->
|
||||||
AllRooms = lists:sort(get_vh_rooms(Host)),
|
todo.
|
||||||
Count = erlang:length(AllRooms),
|
%% get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
|
||||||
Guard = case Direction of
|
%% AllRooms = lists:sort(get_vh_rooms(Host)),
|
||||||
_ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
|
%% Count = erlang:length(AllRooms),
|
||||||
aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
|
%% Guard = case Direction of
|
||||||
before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
|
%% _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
|
||||||
_ -> [{'==', {element, 2, '$1'}, Host}]
|
%% aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
|
||||||
end,
|
%% before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
|
||||||
L = lists:sort(
|
%% _ -> [{'==', {element, 2, '$1'}, Host}]
|
||||||
mnesia:dirty_select(muc_online_room,
|
%% end,
|
||||||
[{#muc_online_room{name_host = '$1', _ = '_'},
|
%% L = lists:sort(
|
||||||
Guard,
|
%% mnesia:dirty_select(muc_online_room,
|
||||||
['$_']}])),
|
%% [{#muc_online_room{name_host = '$1', _ = '_'},
|
||||||
L2 = if
|
%% Guard,
|
||||||
Index == undefined andalso Direction == before ->
|
%% ['$_']}])),
|
||||||
lists:reverse(lists:sublist(lists:reverse(L), 1, M));
|
%% L2 = if
|
||||||
Index == undefined ->
|
%% Index == undefined andalso Direction == before ->
|
||||||
lists:sublist(L, 1, M);
|
%% lists:reverse(lists:sublist(lists:reverse(L), 1, M));
|
||||||
Index > Count orelse Index < 0 ->
|
%% Index == undefined ->
|
||||||
[];
|
%% lists:sublist(L, 1, M);
|
||||||
true ->
|
%% Index > Count orelse Index < 0 ->
|
||||||
lists:sublist(L, Index+1, M)
|
%% [];
|
||||||
end,
|
%% true ->
|
||||||
if L2 == [] -> {L2, #rsm_out{count = Count}};
|
%% lists:sublist(L, Index+1, M)
|
||||||
true ->
|
%% end,
|
||||||
H = hd(L2),
|
%% if L2 == [] -> {L2, #rsm_out{count = Count}};
|
||||||
NewIndex = get_room_pos(H, AllRooms),
|
%% true ->
|
||||||
T = lists:last(L2),
|
%% H = hd(L2),
|
||||||
{F, _} = H#muc_online_room.name_host,
|
%% NewIndex = get_room_pos(H, AllRooms),
|
||||||
{Last, _} = T#muc_online_room.name_host,
|
%% T = lists:last(L2),
|
||||||
{L2,
|
%% {F, _} = H#muc_online_room.name_host,
|
||||||
#rsm_out{first = F, last = Last, count = Count,
|
%% {Last, _} = T#muc_online_room.name_host,
|
||||||
index = NewIndex}}
|
%% {L2,
|
||||||
end.
|
%% #rsm_out{first = F, last = Last, count = Count,
|
||||||
|
%% index = NewIndex}}
|
||||||
|
%% end.
|
||||||
|
|
||||||
get_subscribed_rooms(ServerHost, Host, From) ->
|
get_subscribed_rooms(ServerHost, Host, From) ->
|
||||||
Rooms = get_rooms(ServerHost, Host),
|
Rooms = get_rooms(ServerHost, Host),
|
||||||
@ -730,60 +662,32 @@ get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
|
|||||||
|
|
||||||
flush() -> receive _ -> flush() after 0 -> ok end.
|
flush() -> receive _ -> flush() after 0 -> ok end.
|
||||||
|
|
||||||
-define(XFIELD(Type, Label, Var, Val),
|
|
||||||
#xmlel{name = <<"field">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"type">>, Type},
|
|
||||||
{<<"label">>, translate:translate(Lang, Label)},
|
|
||||||
{<<"var">>, Var}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"value">>, attrs = [],
|
|
||||||
children = [{xmlcdata, Val}]}]}).
|
|
||||||
|
|
||||||
iq_get_unique(From) ->
|
|
||||||
{xmlcdata,
|
|
||||||
p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
|
|
||||||
randoms:get_string()]))}.
|
|
||||||
|
|
||||||
get_nick(ServerHost, Host, From) ->
|
get_nick(ServerHost, Host, From) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:get_nick(LServer, Host, From).
|
Mod:get_nick(LServer, Host, From).
|
||||||
|
|
||||||
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||||
{Nick, Registered} = case get_nick(ServerHost, Host,
|
{Nick, Registered} = case get_nick(ServerHost, Host, From) of
|
||||||
From)
|
error -> {<<"">>, false};
|
||||||
of
|
N -> {N, true}
|
||||||
error -> {<<"">>, []};
|
|
||||||
N ->
|
|
||||||
{N,
|
|
||||||
[#xmlel{name = <<"registered">>, attrs = [],
|
|
||||||
children = []}]}
|
|
||||||
end,
|
end,
|
||||||
Registered ++
|
Title = <<(translate:translate(
|
||||||
[#xmlel{name = <<"instructions">>, attrs = [],
|
Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>,
|
||||||
children =
|
Inst = translate:translate(Lang, <<"Enter nickname you want to register">>),
|
||||||
[{xmlcdata,
|
Field = #xdata_field{type = 'text-single',
|
||||||
translate:translate(Lang,
|
label = translate:translate(Lang, <<"Nickname">>),
|
||||||
<<"You need a client that supports x:data "
|
var = <<"nick">>,
|
||||||
"to register the nickname">>)}]},
|
values = [Nick]},
|
||||||
#xmlel{name = <<"x">>,
|
X = #xdata{type = form, title = Title,
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA},
|
instructions = [Inst], fields = [Field]},
|
||||||
{<<"type">>, <<"form">>}],
|
#register{nick = Nick,
|
||||||
children =
|
registered = Registered,
|
||||||
[#xmlel{name = <<"title">>, attrs = [],
|
instructions =
|
||||||
children =
|
translate:translate(
|
||||||
[{xmlcdata,
|
Lang, <<"You need a client that supports x:data "
|
||||||
<<(translate:translate(Lang,
|
"to register the nickname">>),
|
||||||
<<"Nickname Registration at ">>))/binary,
|
xdata = X}.
|
||||||
Host/binary>>}]},
|
|
||||||
#xmlel{name = <<"instructions">>, attrs = [],
|
|
||||||
children =
|
|
||||||
[{xmlcdata,
|
|
||||||
translate:translate(Lang,
|
|
||||||
<<"Enter nickname you want to register">>)}]},
|
|
||||||
?XFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>,
|
|
||||||
Nick)]}].
|
|
||||||
|
|
||||||
set_nick(ServerHost, Host, From, Nick) ->
|
set_nick(ServerHost, Host, From, Nick) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
@ -793,66 +697,43 @@ set_nick(ServerHost, Host, From, Nick) ->
|
|||||||
iq_set_register_info(ServerHost, Host, From, Nick,
|
iq_set_register_info(ServerHost, Host, From, Nick,
|
||||||
Lang) ->
|
Lang) ->
|
||||||
case set_nick(ServerHost, Host, From, Nick) of
|
case set_nick(ServerHost, Host, From, Nick) of
|
||||||
{atomic, ok} -> {result, []};
|
{atomic, ok} -> {result, undefined};
|
||||||
{atomic, false} ->
|
{atomic, false} ->
|
||||||
ErrText = <<"That nickname is registered by another "
|
ErrText = <<"That nickname is registered by another "
|
||||||
"person">>,
|
"person">>,
|
||||||
{error, ?ERRT_CONFLICT(Lang, ErrText)};
|
{error, xmpp:err_conflict(ErrText, Lang)};
|
||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Database failure">>,
|
Txt = <<"Database failure">>,
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)}
|
{error, xmpp:err_internal_server_error(Txt, Lang)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_iq_register_set(ServerHost, Host, From, SubEl,
|
process_iq_register_set(ServerHost, Host, From,
|
||||||
Lang) ->
|
#register{remove = true}, Lang) ->
|
||||||
#xmlel{children = Els} = SubEl,
|
iq_set_register_info(ServerHost, Host, From, <<"">>, Lang);
|
||||||
case fxml:get_subtag(SubEl, <<"remove">>) of
|
process_iq_register_set(_ServerHost, _Host, _From,
|
||||||
false ->
|
#register{xdata = #xdata{type = cancel}}, _Lang) ->
|
||||||
case fxml:remove_cdata(Els) of
|
{result, undefined};
|
||||||
[#xmlel{name = <<"x">>} = XEl] ->
|
process_iq_register_set(ServerHost, Host, From,
|
||||||
case {fxml:get_tag_attr_s(<<"xmlns">>, XEl),
|
#register{nick = Nick, xdata = XData}, Lang) ->
|
||||||
fxml:get_tag_attr_s(<<"type">>, XEl)}
|
|
||||||
of
|
|
||||||
{?NS_XDATA, <<"cancel">>} -> {result, []};
|
|
||||||
{?NS_XDATA, <<"submit">>} ->
|
|
||||||
XData = jlib:parse_xdata_submit(XEl),
|
|
||||||
case XData of
|
case XData of
|
||||||
invalid ->
|
#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)}
|
||||||
|
end;
|
||||||
|
#xdata{} ->
|
||||||
Txt = <<"Incorrect data form">>,
|
Txt = <<"Incorrect data form">>,
|
||||||
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
{error, xmpp:err_bad_request(Txt, Lang)};
|
||||||
|
_ when is_binary(Nick), Nick /= <<"">> ->
|
||||||
|
iq_set_register_info(ServerHost, Host, From, Nick, Lang);
|
||||||
_ ->
|
_ ->
|
||||||
case lists:keysearch(<<"nick">>, 1, XData) of
|
ErrText = <<"You must fill in field \"Nickname\" in the form">>,
|
||||||
{value, {_, [Nick]}} when Nick /= <<"">> ->
|
{error, xmpp:err_not_acceptable(ErrText, Lang)}
|
||||||
iq_set_register_info(ServerHost, Host, From,
|
|
||||||
Nick, Lang);
|
|
||||||
_ ->
|
|
||||||
ErrText =
|
|
||||||
<<"You must fill in field \"Nickname\" "
|
|
||||||
"in the form">>,
|
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ -> {error, ?ERR_BAD_REQUEST}
|
|
||||||
end;
|
|
||||||
_ -> {error, ?ERR_BAD_REQUEST}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
iq_set_register_info(ServerHost, Host, From, <<"">>,
|
|
||||||
Lang)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
iq_get_vcard(Lang) ->
|
|
||||||
[#xmlel{name = <<"FN">>, attrs = [],
|
|
||||||
children = [{xmlcdata, <<"ejabberd/mod_muc">>}]},
|
|
||||||
#xmlel{name = <<"URL">>, attrs = [],
|
|
||||||
children = [{xmlcdata, ?EJABBERD_URI}]},
|
|
||||||
#xmlel{name = <<"DESC">>, attrs = [],
|
|
||||||
children =
|
|
||||||
[{xmlcdata,
|
|
||||||
<<(translate:translate(Lang,
|
|
||||||
<<"ejabberd MUC module">>))/binary,
|
|
||||||
"\nCopyright (c) 2003-2016 ProcessOne">>}]}].
|
|
||||||
|
|
||||||
broadcast_service_message(Host, Msg) ->
|
broadcast_service_message(Host, Msg) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(#muc_online_room{pid = Pid}) ->
|
fun(#muc_online_room{pid = Pid}) ->
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("mod_muc_room.hrl").
|
-include("mod_muc_room.hrl").
|
||||||
-include("mod_muc.hrl").
|
-include("mod_muc.hrl").
|
||||||
-include("ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
@ -241,7 +241,7 @@ web_menu_host(Acc, _Host, Lang) ->
|
|||||||
|
|
||||||
-define(TDTD(L, N),
|
-define(TDTD(L, N),
|
||||||
?XE(<<"tr">>, [?XCT(<<"td">>, L),
|
?XE(<<"tr">>, [?XCT(<<"td">>, L),
|
||||||
?XC(<<"td">>, jlib:integer_to_binary(N))
|
?XC(<<"td">>, integer_to_binary(N))
|
||||||
])).
|
])).
|
||||||
|
|
||||||
web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) ->
|
web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) ->
|
||||||
@ -283,7 +283,7 @@ get_sort_query(Q) ->
|
|||||||
|
|
||||||
get_sort_query2(Q) ->
|
get_sort_query2(Q) ->
|
||||||
{value, {_, String}} = lists:keysearch(<<"sort">>, 1, Q),
|
{value, {_, String}} = lists:keysearch(<<"sort">>, 1, Q),
|
||||||
Integer = jlib:binary_to_integer(String),
|
Integer = binary_to_integer(String),
|
||||||
case Integer >= 0 of
|
case Integer >= 0 of
|
||||||
true -> {ok, {normal, Integer}};
|
true -> {ok, {normal, Integer}};
|
||||||
false -> {ok, {reverse, abs(Integer)}}
|
false -> {ok, {reverse, abs(Integer)}}
|
||||||
@ -309,7 +309,7 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
|
|||||||
{Titles_TR, _} =
|
{Titles_TR, _} =
|
||||||
lists:mapfoldl(
|
lists:mapfoldl(
|
||||||
fun(Title, Num_column) ->
|
fun(Title, Num_column) ->
|
||||||
NCS = jlib:integer_to_binary(Num_column),
|
NCS = integer_to_binary(Num_column),
|
||||||
TD = ?XE(<<"td">>, [?CT(Title),
|
TD = ?XE(<<"td">>, [?CT(Title),
|
||||||
?C(<<" ">>),
|
?C(<<" ">>),
|
||||||
?AC(<<"?sort=", NCS/binary>>, <<"<">>),
|
?AC(<<"?sort=", NCS/binary>>, <<"<">>),
|
||||||
@ -383,7 +383,7 @@ prepare_room_info(Room_info) ->
|
|||||||
Just_created,
|
Just_created,
|
||||||
Title} = Room_info,
|
Title} = Room_info,
|
||||||
[NameHost,
|
[NameHost,
|
||||||
jlib:integer_to_binary(Num_participants),
|
integer_to_binary(Num_participants),
|
||||||
Ts_last_message,
|
Ts_last_message,
|
||||||
jlib:atom_to_binary(Public),
|
jlib:atom_to_binary(Public),
|
||||||
jlib:atom_to_binary(Persistent),
|
jlib:atom_to_binary(Persistent),
|
||||||
@ -830,7 +830,7 @@ get_options(Config) ->
|
|||||||
Fields = [jlib:atom_to_binary(Field) || Field <- record_info(fields, config)],
|
Fields = [jlib:atom_to_binary(Field) || Field <- record_info(fields, config)],
|
||||||
[config | ValuesRaw] = tuple_to_list(Config),
|
[config | ValuesRaw] = tuple_to_list(Config),
|
||||||
Values = lists:map(fun(V) when is_atom(V) -> jlib:atom_to_binary(V);
|
Values = lists:map(fun(V) when is_atom(V) -> jlib:atom_to_binary(V);
|
||||||
(V) when is_integer(V) -> jlib:integer_to_binary(V);
|
(V) when is_integer(V) -> integer_to_binary(V);
|
||||||
(V) when is_tuple(V); is_list(V) -> list_to_binary(hd(io_lib:format("~w", [V])));
|
(V) when is_tuple(V); is_list(V) -> list_to_binary(hd(io_lib:format("~w", [V])));
|
||||||
(V) -> V end, ValuesRaw),
|
(V) -> V end, ValuesRaw),
|
||||||
lists:zip(Fields, Values).
|
lists:zip(Fields, Values).
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("mod_muc.hrl").
|
-include("mod_muc.hrl").
|
||||||
-include("mod_muc_room.hrl").
|
-include("mod_muc_room.hrl").
|
||||||
|
|
||||||
@ -196,15 +196,13 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
|||||||
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
|
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
|
||||||
case has_no_permanent_store_hint(Packet) of
|
case has_no_permanent_store_hint(Packet) of
|
||||||
false ->
|
false ->
|
||||||
case {fxml:get_subtag(Packet, <<"subject">>),
|
case {Packet#message.subject, Packet#message.body} of
|
||||||
fxml:get_subtag(Packet, <<"body">>)}
|
{[], []} -> ok;
|
||||||
of
|
{[], Body} ->
|
||||||
{false, false} -> ok;
|
Message = {body, xmpp:get_text(Body)},
|
||||||
{false, SubEl} ->
|
|
||||||
Message = {body, fxml:get_tag_cdata(SubEl)},
|
|
||||||
add_message_to_log(Nick, Message, Room, Opts, State);
|
add_message_to_log(Nick, Message, Room, Opts, State);
|
||||||
{SubEl, _} ->
|
{Subj, _} ->
|
||||||
Message = {subject, fxml:get_tag_cdata(SubEl)},
|
Message = {subject, xmpp:get_text(Subj)},
|
||||||
add_message_to_log(Nick, Message, Room, Opts, State)
|
add_message_to_log(Nick, Message, Room, Opts, State)
|
||||||
end;
|
end;
|
||||||
true -> ok
|
true -> ok
|
||||||
@ -1035,7 +1033,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
|
|||||||
max_users ->
|
max_users ->
|
||||||
<<"<div class=\"rcot\">",
|
<<"<div class=\"rcot\">",
|
||||||
OptText/binary, ": \"",
|
OptText/binary, ": \"",
|
||||||
(htmlize(jlib:integer_to_binary(T),
|
(htmlize(integer_to_binary(T),
|
||||||
FileFormat))/binary,
|
FileFormat))/binary,
|
||||||
"\"</div>">>;
|
"\"</div>">>;
|
||||||
title ->
|
title ->
|
||||||
@ -1053,7 +1051,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
|
|||||||
allow_private_messages_from_visitors ->
|
allow_private_messages_from_visitors ->
|
||||||
<<"<div class=\"rcot\">",
|
<<"<div class=\"rcot\">",
|
||||||
OptText/binary, ": \"",
|
OptText/binary, ": \"",
|
||||||
(htmlize(?T((jlib:atom_to_binary(T))),
|
(htmlize(?T(jlib:atom_to_binary(T)),
|
||||||
FileFormat))/binary,
|
FileFormat))/binary,
|
||||||
"\"</div>">>;
|
"\"</div>">>;
|
||||||
_ -> <<"\"", T/binary, "\"">>
|
_ -> <<"\"", T/binary, "\"">>
|
||||||
@ -1168,7 +1166,7 @@ get_room_occupants(RoomJIDString) ->
|
|||||||
[{U#user.jid, U#user.nick, U#user.role}
|
[{U#user.jid, U#user.nick, U#user.role}
|
||||||
|| {_, U} <- (?DICT):to_list(StateData#state.users)].
|
|| {_, U} <- (?DICT):to_list(StateData#state.users)].
|
||||||
|
|
||||||
-spec get_room_state(binary(), binary()) -> muc_room_state().
|
-spec get_room_state(binary(), binary()) -> mod_muc_room:state().
|
||||||
|
|
||||||
get_room_state(RoomName, MucService) ->
|
get_room_state(RoomName, MucService) ->
|
||||||
case mnesia:dirty_read(muc_online_room,
|
case mnesia:dirty_read(muc_online_room,
|
||||||
@ -1180,7 +1178,7 @@ get_room_state(RoomName, MucService) ->
|
|||||||
[] -> #state{}
|
[] -> #state{}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_room_state(pid()) -> muc_room_state().
|
-spec get_room_state(pid()) -> mod_muc_room:state().
|
||||||
|
|
||||||
get_room_state(RoomPid) ->
|
get_room_state(RoomPid) ->
|
||||||
{ok, R} = gen_fsm:sync_send_all_state_event(RoomPid,
|
{ok, R} = gen_fsm:sync_send_all_state_event(RoomPid,
|
||||||
@ -1204,14 +1202,10 @@ fjoin(FileList) ->
|
|||||||
list_to_binary(filename:join([binary_to_list(File) || File <- FileList])).
|
list_to_binary(filename:join([binary_to_list(File) || File <- FileList])).
|
||||||
|
|
||||||
has_no_permanent_store_hint(Packet) ->
|
has_no_permanent_store_hint(Packet) ->
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS)
|
xmpp:has_subtag(Packet, #hint{type = 'no-store'}) orelse
|
||||||
=/= false orelse
|
xmpp:has_subtag(Packet, #hint{type = 'no-storage'}) orelse
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS)
|
xmpp:has_subtag(Packet, #hint{type = 'no-permanent-store'}) orelse
|
||||||
=/= false orelse
|
xmpp:has_subtag(Packet, #hint{type = 'no-permanent-storage'}).
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-store">>, ?NS_HINTS)
|
|
||||||
=/= false orelse
|
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-storage">>, ?NS_HINTS)
|
|
||||||
=/= false.
|
|
||||||
|
|
||||||
mod_opt_type(access_log) ->
|
mod_opt_type(access_log) ->
|
||||||
fun (A) when is_atom(A) -> A end;
|
fun (A) when is_atom(A) -> A end;
|
||||||
|
3363
src/mod_muc_room.erl
3363
src/mod_muc_room.erl
File diff suppressed because it is too large
Load Diff
@ -72,7 +72,7 @@ process_sm_iq(#iq{type = Type, lang = Lang,
|
|||||||
case filter_xmlels(Els0) of
|
case filter_xmlels(Els0) of
|
||||||
[] ->
|
[] ->
|
||||||
Txt = <<"No private data found in this query">>,
|
Txt = <<"No private data found in this query">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_bad_format(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
|
||||||
Data when Type == set ->
|
Data when Type == set ->
|
||||||
set_data(LUser, LServer, Data),
|
set_data(LUser, LServer, Data),
|
||||||
xmpp:make_iq_result(IQ);
|
xmpp:make_iq_result(IQ);
|
||||||
|
228
src/xmpp.erl
228
src/xmpp.erl
@ -16,22 +16,33 @@
|
|||||||
set_type/2, set_to/2, set_from/2, set_id/2,
|
set_type/2, set_to/2, set_from/2, set_id/2,
|
||||||
set_lang/2, set_error/2, set_els/2, set_from_to/3,
|
set_lang/2, set_error/2, set_els/2, set_from_to/3,
|
||||||
format_error/1, is_stanza/1, set_subtag/2, get_subtag/2,
|
format_error/1, is_stanza/1, set_subtag/2, get_subtag/2,
|
||||||
remove_subtag/2, has_subtag/2, decode_els/1, pp/1,
|
remove_subtag/2, has_subtag/2, decode_els/1, decode_els/2,
|
||||||
get_name/1, get_text/1, mk_text/1, mk_text/2]).
|
pp/1, get_name/1, get_text/1, mk_text/1, mk_text/2]).
|
||||||
|
|
||||||
%% XMPP errors
|
%% XMPP errors
|
||||||
-export([err_bad_request/0, err_bad_request/2,
|
-export([err_bad_request/0, err_bad_request/2,
|
||||||
err_bad_format/0, err_bad_format/2,
|
|
||||||
err_not_allowed/0, err_not_allowed/2,
|
|
||||||
err_conflict/0, err_conflict/2,
|
err_conflict/0, err_conflict/2,
|
||||||
|
err_feature_not_implemented/0, err_feature_not_implemented/2,
|
||||||
err_forbidden/0, err_forbidden/2,
|
err_forbidden/0, err_forbidden/2,
|
||||||
err_not_acceptable/0, err_not_acceptable/2,
|
err_gone/0, err_gone/2,
|
||||||
err_internal_server_error/0, err_internal_server_error/2,
|
err_internal_server_error/0, err_internal_server_error/2,
|
||||||
err_service_unavailable/0, err_service_unavailable/2,
|
|
||||||
err_item_not_found/0, err_item_not_found/2,
|
err_item_not_found/0, err_item_not_found/2,
|
||||||
err_jid_malformed/0, err_jid_malformed/2,
|
err_jid_malformed/0, err_jid_malformed/2,
|
||||||
|
err_not_acceptable/0, err_not_acceptable/2,
|
||||||
|
err_not_allowed/0, err_not_allowed/2,
|
||||||
err_not_authorized/0, err_not_authorized/2,
|
err_not_authorized/0, err_not_authorized/2,
|
||||||
err_feature_not_implemented/0, err_feature_not_implemented/2]).
|
err_payment_required/0, err_payment_required/2,
|
||||||
|
err_policy_violation/0, err_policy_violation/2,
|
||||||
|
err_recipient_unavailable/0, err_recipient_unavailable/2,
|
||||||
|
err_redirect/0, err_redirect/2,
|
||||||
|
err_registration_required/0, err_registration_required/2,
|
||||||
|
err_remote_server_not_found/0, err_remote_server_not_found/2,
|
||||||
|
err_remote_server_timeout/0, err_remote_server_timeout/2,
|
||||||
|
err_resource_constraint/0, err_resource_constraint/2,
|
||||||
|
err_service_unavailable/0, err_service_unavailable/2,
|
||||||
|
err_subscription_required/0, err_subscription_required/2,
|
||||||
|
err_undefined_condition/0, err_undefined_condition/2,
|
||||||
|
err_unexpected_request/0, err_unexpected_request/2]).
|
||||||
|
|
||||||
%% XMPP stream errors
|
%% XMPP stream errors
|
||||||
-export([serr_bad_format/0, serr_bad_format/2,
|
-export([serr_bad_format/0, serr_bad_format/2,
|
||||||
@ -246,9 +257,12 @@ decode(Pkt, _Opts) ->
|
|||||||
(message()) -> message();
|
(message()) -> message();
|
||||||
(presence()) -> presence().
|
(presence()) -> presence().
|
||||||
decode_els(Stanza) ->
|
decode_els(Stanza) ->
|
||||||
|
decode_els(Stanza, fun xmpp_codec:is_known_tag/1).
|
||||||
|
|
||||||
|
decode_els(Stanza, MatchFun) ->
|
||||||
Els = lists:map(
|
Els = lists:map(
|
||||||
fun(#xmlel{} = El) ->
|
fun(#xmlel{} = El) ->
|
||||||
case xmpp_codec:is_known_tag(El) of
|
case MatchFun(El) of
|
||||||
true -> decode(El);
|
true -> decode(El);
|
||||||
false -> El
|
false -> El
|
||||||
end;
|
end;
|
||||||
@ -287,10 +301,10 @@ set_subtag(Stanza, Tag) ->
|
|||||||
set_els(Stanza, NewEls).
|
set_els(Stanza, NewEls).
|
||||||
|
|
||||||
set_subtag([El|Els], Tag, TagName, XMLNS) ->
|
set_subtag([El|Els], Tag, TagName, XMLNS) ->
|
||||||
case {get_name(El), get_ns(El)} of
|
case match_tag(El, TagName, XMLNS) of
|
||||||
{TagName, XMLNS} ->
|
true ->
|
||||||
[Tag|Els];
|
[Tag|Els];
|
||||||
_ ->
|
false ->
|
||||||
[El|set_subtag(Els, Tag, TagName, XMLNS)]
|
[El|set_subtag(Els, Tag, TagName, XMLNS)]
|
||||||
end;
|
end;
|
||||||
set_subtag([], Tag, _, _) ->
|
set_subtag([], Tag, _, _) ->
|
||||||
@ -304,14 +318,14 @@ get_subtag(Stanza, Tag) ->
|
|||||||
get_subtag(Els, TagName, XMLNS).
|
get_subtag(Els, TagName, XMLNS).
|
||||||
|
|
||||||
get_subtag([El|Els], TagName, XMLNS) ->
|
get_subtag([El|Els], TagName, XMLNS) ->
|
||||||
case {get_name(El), get_ns(El)} of
|
case match_tag(El, TagName, XMLNS) of
|
||||||
{TagName, XMLNS} ->
|
true ->
|
||||||
try
|
try
|
||||||
decode(El)
|
decode(El)
|
||||||
catch _:{xmpp_codec, _Why} ->
|
catch _:{xmpp_codec, _Why} ->
|
||||||
get_subtag(Els, TagName, XMLNS)
|
get_subtag(Els, TagName, XMLNS)
|
||||||
end;
|
end;
|
||||||
_ ->
|
false ->
|
||||||
get_subtag(Els, TagName, XMLNS)
|
get_subtag(Els, TagName, XMLNS)
|
||||||
end;
|
end;
|
||||||
get_subtag([], _, _) ->
|
get_subtag([], _, _) ->
|
||||||
@ -328,10 +342,10 @@ remove_subtag(Stanza, Tag) ->
|
|||||||
set_els(Stanza, NewEls).
|
set_els(Stanza, NewEls).
|
||||||
|
|
||||||
remove_subtag([El|Els], TagName, XMLNS) ->
|
remove_subtag([El|Els], TagName, XMLNS) ->
|
||||||
case {get_name(El), get_ns(El)} of
|
case match_tag(El, TagName, XMLNS) of
|
||||||
{TagName, XMLNS} ->
|
true ->
|
||||||
remove_subtag(Els, TagName, XMLNS);
|
remove_subtag(Els, TagName, XMLNS);
|
||||||
_ ->
|
false ->
|
||||||
[El|remove_subtag(Els, TagName, XMLNS)]
|
[El|remove_subtag(Els, TagName, XMLNS)]
|
||||||
end;
|
end;
|
||||||
remove_subtag([], _, _) ->
|
remove_subtag([], _, _) ->
|
||||||
@ -345,10 +359,10 @@ has_subtag(Stanza, Tag) ->
|
|||||||
has_subtag(Els, TagName, XMLNS).
|
has_subtag(Els, TagName, XMLNS).
|
||||||
|
|
||||||
has_subtag([El|Els], TagName, XMLNS) ->
|
has_subtag([El|Els], TagName, XMLNS) ->
|
||||||
case {get_name(El), get_ns(El)} of
|
case match_tag(El, TagName, XMLNS) of
|
||||||
{TagName, XMLNS} ->
|
true ->
|
||||||
true;
|
true;
|
||||||
_ ->
|
false ->
|
||||||
has_subtag(Els, TagName, XMLNS)
|
has_subtag(Els, TagName, XMLNS)
|
||||||
end;
|
end;
|
||||||
has_subtag([], _, _) ->
|
has_subtag([], _, _) ->
|
||||||
@ -385,14 +399,6 @@ err_bad_request() ->
|
|||||||
err_bad_request(Text, Lang) ->
|
err_bad_request(Text, Lang) ->
|
||||||
err(modify, 'bad-request', 400, Text, Lang).
|
err(modify, 'bad-request', 400, Text, Lang).
|
||||||
|
|
||||||
-spec err_bad_format() -> error().
|
|
||||||
err_bad_format() ->
|
|
||||||
err(modify, 'bad-format', 406).
|
|
||||||
|
|
||||||
-spec err_bad_format(binary(), binary() | undefined) -> error().
|
|
||||||
err_bad_format(Text, Lang) ->
|
|
||||||
err(modify, 'bad-format', 406, Text, Lang).
|
|
||||||
|
|
||||||
-spec err_conflict() -> error().
|
-spec err_conflict() -> error().
|
||||||
err_conflict() ->
|
err_conflict() ->
|
||||||
err(cancel, 'conflict', 409).
|
err(cancel, 'conflict', 409).
|
||||||
@ -401,14 +407,6 @@ err_conflict() ->
|
|||||||
err_conflict(Text, Lang) ->
|
err_conflict(Text, Lang) ->
|
||||||
err(cancel, 'conflict', 409, Text, Lang).
|
err(cancel, 'conflict', 409, Text, Lang).
|
||||||
|
|
||||||
-spec err_not_allowed() -> error().
|
|
||||||
err_not_allowed() ->
|
|
||||||
err(cancel, 'not-allowed', 405).
|
|
||||||
|
|
||||||
-spec err_not_allowed(binary(), binary() | undefined) -> error().
|
|
||||||
err_not_allowed(Text, Lang) ->
|
|
||||||
err(cancel, 'not-allowed', 405, Text, Lang).
|
|
||||||
|
|
||||||
-spec err_feature_not_implemented() -> error().
|
-spec err_feature_not_implemented() -> error().
|
||||||
err_feature_not_implemented() ->
|
err_feature_not_implemented() ->
|
||||||
err(cancel, 'feature-not-implemented', 501).
|
err(cancel, 'feature-not-implemented', 501).
|
||||||
@ -417,14 +415,6 @@ err_feature_not_implemented() ->
|
|||||||
err_feature_not_implemented(Text, Lang) ->
|
err_feature_not_implemented(Text, Lang) ->
|
||||||
err(cancel, 'feature-not-implemented', 501, Text, Lang).
|
err(cancel, 'feature-not-implemented', 501, Text, Lang).
|
||||||
|
|
||||||
-spec err_item_not_found() -> error().
|
|
||||||
err_item_not_found() ->
|
|
||||||
err(cancel, 'item-not-found', 404).
|
|
||||||
|
|
||||||
-spec err_item_not_found(binary(), binary() | undefined) -> error().
|
|
||||||
err_item_not_found(Text, Lang) ->
|
|
||||||
err(cancel, 'item-not-found', 404, Text, Lang).
|
|
||||||
|
|
||||||
-spec err_forbidden() -> error().
|
-spec err_forbidden() -> error().
|
||||||
err_forbidden() ->
|
err_forbidden() ->
|
||||||
err(auth, 'forbidden', 403).
|
err(auth, 'forbidden', 403).
|
||||||
@ -433,14 +423,18 @@ err_forbidden() ->
|
|||||||
err_forbidden(Text, Lang) ->
|
err_forbidden(Text, Lang) ->
|
||||||
err(auth, 'forbidden', 403, Text, Lang).
|
err(auth, 'forbidden', 403, Text, Lang).
|
||||||
|
|
||||||
-spec err_not_acceptable() -> error().
|
%% RFC 6120 says error type SHOULD be "cancel".
|
||||||
err_not_acceptable() ->
|
%% RFC 3920 and XEP-0082 says it SHOULD be "modify".
|
||||||
err(modify, 'not-acceptable', 406).
|
-spec err_gone() -> error().
|
||||||
|
err_gone() ->
|
||||||
|
err(modify, 'gone', 302).
|
||||||
|
|
||||||
-spec err_not_acceptable(binary(), binary() | undefined) -> error().
|
-spec err_gone(binary(), binary() | undefined) -> error().
|
||||||
err_not_acceptable(Text, Lang) ->
|
err_gone(Text, Lang) ->
|
||||||
err(modify, 'not-acceptable', 406, Text, Lang).
|
err(modify, 'gone', 302, Text, Lang).
|
||||||
|
|
||||||
|
%% RFC 6120 sasy error type SHOULD be "cancel".
|
||||||
|
%% RFC 3920 and XEP-0082 says it SHOULD be "wait".
|
||||||
-spec err_internal_server_error() -> error().
|
-spec err_internal_server_error() -> error().
|
||||||
err_internal_server_error() ->
|
err_internal_server_error() ->
|
||||||
err(wait, 'internal-server-error', 500).
|
err(wait, 'internal-server-error', 500).
|
||||||
@ -449,13 +443,13 @@ err_internal_server_error() ->
|
|||||||
err_internal_server_error(Text, Lang) ->
|
err_internal_server_error(Text, Lang) ->
|
||||||
err(wait, 'internal-server-error', 500, Text, Lang).
|
err(wait, 'internal-server-error', 500, Text, Lang).
|
||||||
|
|
||||||
-spec err_service_unavailable() -> error().
|
-spec err_item_not_found() -> error().
|
||||||
err_service_unavailable() ->
|
err_item_not_found() ->
|
||||||
err(cancel, 'service-unavailable', 503).
|
err(cancel, 'item-not-found', 404).
|
||||||
|
|
||||||
-spec err_service_unavailable(binary(), binary() | undefined) -> error().
|
-spec err_item_not_found(binary(), binary() | undefined) -> error().
|
||||||
err_service_unavailable(Text, Lang) ->
|
err_item_not_found(Text, Lang) ->
|
||||||
err(cancel, 'service-unavailable', 503, Text, Lang).
|
err(cancel, 'item-not-found', 404, Text, Lang).
|
||||||
|
|
||||||
-spec err_jid_malformed() -> error().
|
-spec err_jid_malformed() -> error().
|
||||||
err_jid_malformed() ->
|
err_jid_malformed() ->
|
||||||
@ -465,6 +459,22 @@ err_jid_malformed() ->
|
|||||||
err_jid_malformed(Text, Lang) ->
|
err_jid_malformed(Text, Lang) ->
|
||||||
err(modify, 'jid-malformed', 400, Text, Lang).
|
err(modify, 'jid-malformed', 400, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_not_acceptable() -> error().
|
||||||
|
err_not_acceptable() ->
|
||||||
|
err(modify, 'not-acceptable', 406).
|
||||||
|
|
||||||
|
-spec err_not_acceptable(binary(), binary() | undefined) -> error().
|
||||||
|
err_not_acceptable(Text, Lang) ->
|
||||||
|
err(modify, 'not-acceptable', 406, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_not_allowed() -> error().
|
||||||
|
err_not_allowed() ->
|
||||||
|
err(cancel, 'not-allowed', 405).
|
||||||
|
|
||||||
|
-spec err_not_allowed(binary(), binary() | undefined) -> error().
|
||||||
|
err_not_allowed(Text, Lang) ->
|
||||||
|
err(cancel, 'not-allowed', 405, Text, Lang).
|
||||||
|
|
||||||
-spec err_not_authorized() -> error().
|
-spec err_not_authorized() -> error().
|
||||||
err_not_authorized() ->
|
err_not_authorized() ->
|
||||||
err(auth, 'not-authorized', 401).
|
err(auth, 'not-authorized', 401).
|
||||||
@ -473,6 +483,108 @@ err_not_authorized() ->
|
|||||||
err_not_authorized(Text, Lang) ->
|
err_not_authorized(Text, Lang) ->
|
||||||
err(auth, 'not-authorized', 401, Text, Lang).
|
err(auth, 'not-authorized', 401, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_payment_required() -> error().
|
||||||
|
err_payment_required() ->
|
||||||
|
err(auth, 'not-authorized', 402).
|
||||||
|
|
||||||
|
-spec err_payment_required(binary(), binary() | undefined) -> error().
|
||||||
|
err_payment_required(Text, Lang) ->
|
||||||
|
err(auth, 'not-authorized', 402, Text, Lang).
|
||||||
|
|
||||||
|
%% <policy-violation/> is defined in neither RFC 3920 nor XEP-0086.
|
||||||
|
%% We choose '403' error code (as in <forbidden/>).
|
||||||
|
-spec err_policy_violation() -> error().
|
||||||
|
err_policy_violation() ->
|
||||||
|
err(modify, 'policy-violation', 403).
|
||||||
|
|
||||||
|
-spec err_policy_violation(binary(), binary() | undefined) -> error().
|
||||||
|
err_policy_violation(Text, Lang) ->
|
||||||
|
err(modify, 'policy-violation', 403, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_recipient_unavailable() -> error().
|
||||||
|
err_recipient_unavailable() ->
|
||||||
|
err(wait, 'recipient-unavailable', 404).
|
||||||
|
|
||||||
|
-spec err_recipient_unavailable(binary(), binary() | undefined) -> error().
|
||||||
|
err_recipient_unavailable(Text, Lang) ->
|
||||||
|
err(wait, 'recipient-unavailable', 404, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_redirect() -> error().
|
||||||
|
err_redirect() ->
|
||||||
|
err(modify, 'redirect', 302).
|
||||||
|
|
||||||
|
-spec err_redirect(binary(), binary() | undefined) -> error().
|
||||||
|
err_redirect(Text, Lang) ->
|
||||||
|
err(modify, 'redirect', 302, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_registration_required() -> error().
|
||||||
|
err_registration_required() ->
|
||||||
|
err(auth, 'registration-required', 407).
|
||||||
|
|
||||||
|
-spec err_registration_required(binary(), binary() | undefined) -> error().
|
||||||
|
err_registration_required(Text, Lang) ->
|
||||||
|
err(auth, 'registration-required', 407, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_remote_server_not_found() -> error().
|
||||||
|
err_remote_server_not_found() ->
|
||||||
|
err(cancel, 'remote-server-not-found', 404).
|
||||||
|
|
||||||
|
-spec err_remote_server_not_found(binary(), binary() | undefined) -> error().
|
||||||
|
err_remote_server_not_found(Text, Lang) ->
|
||||||
|
err(cancel, 'remote-server-not-found', 404, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_remote_server_timeout() -> error().
|
||||||
|
err_remote_server_timeout() ->
|
||||||
|
err(wait, 'remote-server-timeout', 504).
|
||||||
|
|
||||||
|
-spec err_remote_server_timeout(binary(), binary() | undefined) -> error().
|
||||||
|
err_remote_server_timeout(Text, Lang) ->
|
||||||
|
err(wait, 'remote-server-timeout', 504, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_resource_constraint() -> error().
|
||||||
|
err_resource_constraint() ->
|
||||||
|
err(wait, 'resource-constraint', 500).
|
||||||
|
|
||||||
|
-spec err_resource_constraint(binary(), binary() | undefined) -> error().
|
||||||
|
err_resource_constraint(Text, Lang) ->
|
||||||
|
err(wait, 'resource-constraint', 500, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_service_unavailable() -> error().
|
||||||
|
err_service_unavailable() ->
|
||||||
|
err(cancel, 'service-unavailable', 503).
|
||||||
|
|
||||||
|
-spec err_service_unavailable(binary(), binary() | undefined) -> error().
|
||||||
|
err_service_unavailable(Text, Lang) ->
|
||||||
|
err(cancel, 'service-unavailable', 503, Text, Lang).
|
||||||
|
|
||||||
|
-spec err_subscription_required() -> error().
|
||||||
|
err_subscription_required() ->
|
||||||
|
err(auth, 'subscription-required', 407).
|
||||||
|
|
||||||
|
-spec err_subscription_required(binary(), binary() | undefined) -> error().
|
||||||
|
err_subscription_required(Text, Lang) ->
|
||||||
|
err(auth, 'subscription-required', 407, Text, Lang).
|
||||||
|
|
||||||
|
%% No error type is defined for <undefined-confition/>.
|
||||||
|
%% We choose "modify" as it's used in RFC 6120 example.
|
||||||
|
-spec err_undefined_condition() -> error().
|
||||||
|
err_undefined_condition() ->
|
||||||
|
err(modify, 'undefined-condition', 500).
|
||||||
|
|
||||||
|
-spec err_undefined_condition(binary(), binary() | undefined) -> error().
|
||||||
|
err_undefined_condition(Text, Lang) ->
|
||||||
|
err(modify, 'undefined-condition', 500, Text, Lang).
|
||||||
|
|
||||||
|
%% RFC 6120 says error type SHOULD be "wait" or "modify".
|
||||||
|
%% RFC 3920 and XEP-0082 says it SHOULD be "wait".
|
||||||
|
-spec err_unexpected_request() -> error().
|
||||||
|
err_unexpected_request() ->
|
||||||
|
err(wait, 'unexpected-request', 400).
|
||||||
|
|
||||||
|
-spec err_unexpected_request(binary(), binary() | undefined) -> error().
|
||||||
|
err_unexpected_request(Text, Lang) ->
|
||||||
|
err(wait, 'unexpected-request', 400, Text, Lang).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Functions to construct stream errors
|
%%% Functions to construct stream errors
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -712,3 +824,7 @@ add_ns(#xmlel{name = Name} = El) when Name == <<"message">>;
|
|||||||
El#xmlel{attrs = Attrs};
|
El#xmlel{attrs = Attrs};
|
||||||
add_ns(El) ->
|
add_ns(El) ->
|
||||||
El.
|
El.
|
||||||
|
|
||||||
|
-spec match_tag(xmlel() | xmpp_element(), binary(), binary()) -> boolean().
|
||||||
|
match_tag(El, TagName, XMLNS) ->
|
||||||
|
get_name(El) == TagName andalso get_ns(El) == XMLNS.
|
||||||
|
2328
src/xmpp_codec.erl
2328
src/xmpp_codec.erl
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
|
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
|
||||||
is_standalone_chat_state/1]).
|
is_standalone_chat_state/1, get_xdata_values/2,
|
||||||
|
has_xdata_var/2]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
@ -76,6 +77,17 @@ is_standalone_chat_state(Stanza) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec get_xdata_values(binary(), xdata()) -> [binary()].
|
||||||
|
get_xdata_values(Var, #xdata{fields = Fields}) ->
|
||||||
|
case lists:keyfind(Var, #xdata_field.var, Fields) of
|
||||||
|
#xdata_field{values = Vals} -> Vals;
|
||||||
|
false -> []
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec has_xdata_var(binary(), xdata()) -> boolean().
|
||||||
|
has_xdata_var(Var, #xdata{fields = Fields}) ->
|
||||||
|
lists:keymember(Var, #xdata_field.var, Fields).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -224,10 +224,12 @@
|
|||||||
-xml(disco_items,
|
-xml(disco_items,
|
||||||
#elem{name = <<"query">>,
|
#elem{name = <<"query">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/disco#items">>,
|
xmlns = <<"http://jabber.org/protocol/disco#items">>,
|
||||||
result = {disco_items, '$node', '$items'},
|
result = {disco_items, '$node', '$items', '$rsm'},
|
||||||
attrs = [#attr{name = <<"node">>}],
|
attrs = [#attr{name = <<"node">>}],
|
||||||
refs = [#ref{name = disco_item,
|
refs = [#ref{name = disco_item,
|
||||||
label = '$items'}]}).
|
label = '$items'},
|
||||||
|
#ref{name = rsm_set, min = 0, max = 1,
|
||||||
|
label = '$rsm'}]}).
|
||||||
|
|
||||||
-xml(private,
|
-xml(private,
|
||||||
#elem{name = <<"query">>,
|
#elem{name = <<"query">>,
|
||||||
@ -468,6 +470,10 @@
|
|||||||
#elem{name = <<"not-authorized">>,
|
#elem{name = <<"not-authorized">>,
|
||||||
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
|
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
|
||||||
result = 'not-authorized'}).
|
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,
|
-xml(error_policy_violation,
|
||||||
#elem{name = <<"policy-violation">>,
|
#elem{name = <<"policy-violation">>,
|
||||||
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
|
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
|
||||||
@ -561,6 +567,8 @@
|
|||||||
min = 0, max = 1, label = '$reason'},
|
min = 0, max = 1, label = '$reason'},
|
||||||
#ref{name = error_not_authorized,
|
#ref{name = error_not_authorized,
|
||||||
min = 0, max = 1, label = '$reason'},
|
min = 0, max = 1, label = '$reason'},
|
||||||
|
#ref{name = error_payment_required,
|
||||||
|
min = 0, max = 1, label = '$reason'},
|
||||||
#ref{name = error_policy_violation,
|
#ref{name = error_policy_violation,
|
||||||
min = 0, max = 1, label = '$reason'},
|
min = 0, max = 1, label = '$reason'},
|
||||||
#ref{name = error_recipient_unavailable,
|
#ref{name = error_recipient_unavailable,
|
||||||
@ -1582,7 +1590,8 @@
|
|||||||
-xml(xdata_field_option,
|
-xml(xdata_field_option,
|
||||||
#elem{name = <<"option">>,
|
#elem{name = <<"option">>,
|
||||||
xmlns = <<"jabber:x:data">>,
|
xmlns = <<"jabber:x:data">>,
|
||||||
result = '$value',
|
result = {xdata_option, '$label', '$value'},
|
||||||
|
attrs = [#attr{name = <<"label">>}],
|
||||||
refs = [#ref{name = xdata_field_value,
|
refs = [#ref{name = xdata_field_value,
|
||||||
label = '$value',
|
label = '$value',
|
||||||
min = 1, max = 1}]}).
|
min = 1, max = 1}]}).
|
||||||
@ -1945,9 +1954,11 @@
|
|||||||
dec = {dec_utc, []},
|
dec = {dec_utc, []},
|
||||||
enc = {enc_utc, []}}]}).
|
enc = {enc_utc, []}}]}).
|
||||||
|
|
||||||
-xml(muc_user_reason,
|
-xml(muc_reason,
|
||||||
#elem{name = <<"reason">>,
|
#elem{name = <<"reason">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
xmlns = [<<"http://jabber.org/protocol/muc#user">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#admin">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#owner">>],
|
||||||
result = '$cdata'}).
|
result = '$cdata'}).
|
||||||
|
|
||||||
-xml(muc_user_decline,
|
-xml(muc_user_decline,
|
||||||
@ -1960,31 +1971,39 @@
|
|||||||
#attr{name = <<"from">>,
|
#attr{name = <<"from">>,
|
||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
enc = {enc_jid, []}}],
|
enc = {enc_jid, []}}],
|
||||||
refs = [#ref{name = muc_user_reason, min = 0,
|
refs = [#ref{name = muc_reason, min = 0,
|
||||||
|
default = <<"">>,
|
||||||
max = 1, label = '$reason'}]}).
|
max = 1, label = '$reason'}]}).
|
||||||
|
|
||||||
-xml(muc_user_destroy,
|
-xml(muc_destroy,
|
||||||
#elem{name = <<"destroy">>,
|
#elem{name = <<"destroy">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
xmlns = [<<"http://jabber.org/protocol/muc#user">>,
|
||||||
result = {muc_user_destroy, '$reason', '$jid'},
|
<<"http://jabber.org/protocol/muc#owner">>],
|
||||||
|
result = {muc_destroy, '$xmlns', '$jid', '$reason', '$password'},
|
||||||
attrs = [#attr{name = <<"jid">>,
|
attrs = [#attr{name = <<"jid">>,
|
||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
enc = {enc_jid, []}}],
|
enc = {enc_jid, []}},
|
||||||
refs = [#ref{name = muc_user_reason, min = 0,
|
#attr{name = <<"xmlns">>}],
|
||||||
max = 1, label = '$reason'}]}).
|
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,
|
-xml(muc_user_invite,
|
||||||
#elem{name = <<"invite">>,
|
#elem{name = <<"invite">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
||||||
result = {muc_invite, '$reason', '$from', '$to'},
|
result = {muc_invite, '$reason', '$from', '$to', '$continue'},
|
||||||
attrs = [#attr{name = <<"to">>,
|
attrs = [#attr{name = <<"to">>,
|
||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
enc = {enc_jid, []}},
|
enc = {enc_jid, []}},
|
||||||
#attr{name = <<"from">>,
|
#attr{name = <<"from">>,
|
||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
enc = {enc_jid, []}}],
|
enc = {enc_jid, []}}],
|
||||||
refs = [#ref{name = muc_user_reason, min = 0,
|
refs = [#ref{name = muc_reason, min = 0, default = <<"">>,
|
||||||
max = 1, label = '$reason'}]}).
|
max = 1, label = '$reason'},
|
||||||
|
#ref{name = muc_user_continue, min = 0, max = 1,
|
||||||
|
label = '$continue'}]}).
|
||||||
|
|
||||||
-xml(muc_user_actor,
|
-xml(muc_user_actor,
|
||||||
#elem{name = <<"actor">>,
|
#elem{name = <<"actor">>,
|
||||||
@ -2018,7 +2037,7 @@
|
|||||||
min = 0, max = 1, label = '$actor'},
|
min = 0, max = 1, label = '$actor'},
|
||||||
#ref{name = muc_user_continue,
|
#ref{name = muc_user_continue,
|
||||||
min = 0, max = 1, label = '$continue'},
|
min = 0, max = 1, label = '$continue'},
|
||||||
#ref{name = muc_user_reason,
|
#ref{name = muc_reason, default = <<"">>,
|
||||||
min = 0, max = 1, label = '$reason'}],
|
min = 0, max = 1, label = '$reason'}],
|
||||||
attrs = [#attr{name = <<"affiliation">>,
|
attrs = [#attr{name = <<"affiliation">>,
|
||||||
dec = {dec_enum, [[admin, member, none,
|
dec = {dec_enum, [[admin, member, none,
|
||||||
@ -2038,44 +2057,56 @@
|
|||||||
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
xmlns = <<"http://jabber.org/protocol/muc#user">>,
|
||||||
result = {muc_user, '$decline', '$destroy', '$invites',
|
result = {muc_user, '$decline', '$destroy', '$invites',
|
||||||
'$items', '$status_codes', '$password'},
|
'$items', '$status_codes', '$password'},
|
||||||
attrs = [#attr{name = <<"password">>}],
|
|
||||||
refs = [#ref{name = muc_user_decline, min = 0,
|
refs = [#ref{name = muc_user_decline, min = 0,
|
||||||
max = 1, label = '$decline'},
|
max = 1, label = '$decline'},
|
||||||
#ref{name = muc_user_destroy, min = 0, max = 1,
|
#ref{name = muc_destroy, min = 0, max = 1,
|
||||||
label = '$destroy'},
|
label = '$destroy'},
|
||||||
|
#ref{name = muc_password, min = 0, max = 1,
|
||||||
|
label = '$password'},
|
||||||
#ref{name = muc_user_invite, label = '$invites'},
|
#ref{name = muc_user_invite, label = '$invites'},
|
||||||
#ref{name = muc_user_item, label = '$items'},
|
#ref{name = muc_user_item, label = '$items'},
|
||||||
#ref{name = muc_user_status, label = '$status_codes'}]}).
|
#ref{name = muc_user_status, label = '$status_codes'}]}).
|
||||||
|
|
||||||
-xml(muc_owner_password,
|
-xml(muc_password,
|
||||||
#elem{name = <<"password">>,
|
#elem{name = <<"password">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
|
xmlns = [<<"http://jabber.org/protocol/muc#owner">>,
|
||||||
|
<<"http://jabber.org/protocol/muc#user">>,
|
||||||
|
<<"http://jabber.org/protocol/muc">>],
|
||||||
result = '$cdata'}).
|
result = '$cdata'}).
|
||||||
|
|
||||||
-xml(muc_owner_reason,
|
|
||||||
#elem{name = <<"reason">>,
|
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
|
|
||||||
result = '$cdata'}).
|
|
||||||
|
|
||||||
-xml(muc_owner_destroy,
|
|
||||||
#elem{name = <<"destroy">>,
|
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
|
|
||||||
result = {muc_owner_destroy, '$jid', '$reason', '$password'},
|
|
||||||
attrs = [#attr{name = <<"jid">>,
|
|
||||||
dec = {dec_jid, []},
|
|
||||||
enc = {enc_jid, []}}],
|
|
||||||
refs = [#ref{name = muc_owner_password, min = 0, max = 1,
|
|
||||||
label = '$password'},
|
|
||||||
#ref{name = muc_owner_reason, min = 0, max = 1,
|
|
||||||
label = '$reason'}]}).
|
|
||||||
|
|
||||||
-xml(muc_owner,
|
-xml(muc_owner,
|
||||||
#elem{name = <<"query">>,
|
#elem{name = <<"query">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
|
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
|
||||||
result = {muc_owner, '$destroy', '$config'},
|
result = {muc_owner, '$destroy', '$config', '$items'},
|
||||||
refs = [#ref{name = muc_owner_destroy, min = 0, max = 1,
|
refs = [#ref{name = muc_destroy, min = 0, max = 1,
|
||||||
label = '$destroy'},
|
label = '$destroy'},
|
||||||
#ref{name = xdata, min = 0, max = 1, label = '$config'}]}).
|
#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,
|
-xml(muc_admin_item,
|
||||||
#elem{name = <<"item">>,
|
#elem{name = <<"item">>,
|
||||||
@ -2086,7 +2117,7 @@
|
|||||||
min = 0, max = 1, label = '$actor'},
|
min = 0, max = 1, label = '$actor'},
|
||||||
#ref{name = muc_admin_continue,
|
#ref{name = muc_admin_continue,
|
||||||
min = 0, max = 1, label = '$continue'},
|
min = 0, max = 1, label = '$continue'},
|
||||||
#ref{name = muc_admin_reason,
|
#ref{name = muc_reason, default = <<"">>,
|
||||||
min = 0, max = 1, label = '$reason'}],
|
min = 0, max = 1, label = '$reason'}],
|
||||||
attrs = [#attr{name = <<"affiliation">>,
|
attrs = [#attr{name = <<"affiliation">>,
|
||||||
dec = {dec_enum, [[admin, member, none,
|
dec = {dec_enum, [[admin, member, none,
|
||||||
@ -2116,11 +2147,6 @@
|
|||||||
result = '$thread',
|
result = '$thread',
|
||||||
attrs = [#attr{name = <<"thread">>}]}).
|
attrs = [#attr{name = <<"thread">>}]}).
|
||||||
|
|
||||||
-xml(muc_admin_reason,
|
|
||||||
#elem{name = <<"reason">>,
|
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#admin">>,
|
|
||||||
result = '$cdata'}).
|
|
||||||
|
|
||||||
-xml(muc_admin,
|
-xml(muc_admin,
|
||||||
#elem{name = <<"query">>,
|
#elem{name = <<"query">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc#admin">>,
|
xmlns = <<"http://jabber.org/protocol/muc#admin">>,
|
||||||
@ -2131,9 +2157,66 @@
|
|||||||
#elem{name = <<"x">>,
|
#elem{name = <<"x">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/muc">>,
|
xmlns = <<"http://jabber.org/protocol/muc">>,
|
||||||
result = {muc, '$history', '$password'},
|
result = {muc, '$history', '$password'},
|
||||||
attrs = [#attr{name = <<"password">>}],
|
|
||||||
refs = [#ref{name = muc_history, min = 0, max = 1,
|
refs = [#ref{name = muc_history, min = 0, max = 1,
|
||||||
label = '$history'}]}).
|
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,
|
-xml(rsm_after,
|
||||||
#elem{name = <<"after">>,
|
#elem{name = <<"after">>,
|
||||||
@ -2143,7 +2226,7 @@
|
|||||||
-xml(rsm_before,
|
-xml(rsm_before,
|
||||||
#elem{name = <<"before">>,
|
#elem{name = <<"before">>,
|
||||||
xmlns = <<"http://jabber.org/protocol/rsm">>,
|
xmlns = <<"http://jabber.org/protocol/rsm">>,
|
||||||
cdata = #cdata{default = none},
|
cdata = #cdata{default = <<"">>},
|
||||||
result = '$cdata'}).
|
result = '$cdata'}).
|
||||||
|
|
||||||
-xml(rsm_last,
|
-xml(rsm_last,
|
||||||
@ -2215,16 +2298,23 @@
|
|||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
enc = {enc_jid, []}}}).
|
enc = {enc_jid, []}}}).
|
||||||
|
|
||||||
|
-xml(mam_withtext,
|
||||||
|
#elem{name = <<"withtext">>,
|
||||||
|
xmlns = <<"urn:xmpp:mam:tmp">>,
|
||||||
|
result = '$cdata',
|
||||||
|
cdata = #cdata{required = true}}).
|
||||||
|
|
||||||
-xml(mam_query,
|
-xml(mam_query,
|
||||||
#elem{name = <<"query">>,
|
#elem{name = <<"query">>,
|
||||||
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
|
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
|
||||||
result = {mam_query, '$xmlns', '$id', '$start', '$end', '$with',
|
result = {mam_query, '$xmlns', '$id', '$start', '$end', '$with',
|
||||||
'$rsm', '$xdata'},
|
'$withtext', '$rsm', '$xdata'},
|
||||||
attrs = [#attr{name = <<"queryid">>, label = '$id'},
|
attrs = [#attr{name = <<"queryid">>, label = '$id'},
|
||||||
#attr{name = <<"xmlns">>}],
|
#attr{name = <<"xmlns">>}],
|
||||||
refs = [#ref{name = mam_start, min = 0, max = 1, label = '$start'},
|
refs = [#ref{name = mam_start, min = 0, max = 1, label = '$start'},
|
||||||
#ref{name = mam_end, min = 0, max = 1, label = '$end'},
|
#ref{name = mam_end, min = 0, max = 1, label = '$end'},
|
||||||
#ref{name = mam_with, min = 0, max = 1, label = '$with'},
|
#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 = rsm_set, min = 0, max = 1, label = '$rsm'},
|
||||||
#ref{name = xdata, min = 0, max = 1, label = '$xdata'}]}).
|
#ref{name = xdata, min = 0, max = 1, label = '$xdata'}]}).
|
||||||
|
|
||||||
@ -2248,7 +2338,7 @@
|
|||||||
|
|
||||||
-xml(mam_jid,
|
-xml(mam_jid,
|
||||||
#elem{name = <<"jid">>,
|
#elem{name = <<"jid">>,
|
||||||
xmlns = <<"urn:xmpp:mam:tmp">>,
|
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
|
||||||
result = '$cdata',
|
result = '$cdata',
|
||||||
cdata = #cdata{required = true,
|
cdata = #cdata{required = true,
|
||||||
dec = {dec_jid, []},
|
dec = {dec_jid, []},
|
||||||
@ -2256,15 +2346,15 @@
|
|||||||
|
|
||||||
-xml(mam_never,
|
-xml(mam_never,
|
||||||
#elem{name = <<"never">>,
|
#elem{name = <<"never">>,
|
||||||
xmlns = <<"urn:xmpp:mam:tmp">>,
|
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
|
||||||
result = '$jids',
|
result = '$jids',
|
||||||
refs = [#ref{name = mam_jid, label = '$jids', default = []}]}).
|
refs = [#ref{name = mam_jid, label = '$jids'}]}).
|
||||||
|
|
||||||
-xml(mam_always,
|
-xml(mam_always,
|
||||||
#elem{name = <<"always">>,
|
#elem{name = <<"always">>,
|
||||||
xmlns = <<"urn:xmpp:mam:tmp">>,
|
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
|
||||||
result = '$jids',
|
result = '$jids',
|
||||||
refs = [#ref{name = mam_jid, label = '$jids', default = []}]}).
|
refs = [#ref{name = mam_jid, label = '$jids'}]}).
|
||||||
|
|
||||||
-xml(mam_prefs,
|
-xml(mam_prefs,
|
||||||
#elem{name = <<"prefs">>,
|
#elem{name = <<"prefs">>,
|
||||||
@ -2275,9 +2365,9 @@
|
|||||||
enc = {enc_enum, []}},
|
enc = {enc_enum, []}},
|
||||||
#attr{name = <<"xmlns">>}],
|
#attr{name = <<"xmlns">>}],
|
||||||
refs = [#ref{name = mam_always, label = '$always',
|
refs = [#ref{name = mam_always, label = '$always',
|
||||||
min = 0, max = 1, default = []},
|
min = 0, max = 1},
|
||||||
#ref{name = mam_never, label = '$never',
|
#ref{name = mam_never, label = '$never',
|
||||||
min = 0, max = 1, default = []}]}).
|
min = 0, max = 1}]}).
|
||||||
|
|
||||||
-xml(mam_fin,
|
-xml(mam_fin,
|
||||||
#elem{name = <<"fin">>,
|
#elem{name = <<"fin">>,
|
||||||
@ -2538,8 +2628,8 @@
|
|||||||
#attr{name = <<"nick">>,
|
#attr{name = <<"nick">>,
|
||||||
label = '$nick'}]}).
|
label = '$nick'}]}).
|
||||||
|
|
||||||
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
|
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' |
|
||||||
'store' | 'no-permanent-store'}).
|
'no-permanent-store' | 'no-permanent-storage'}).
|
||||||
-type hint() :: #hint{}.
|
-type hint() :: #hint{}.
|
||||||
|
|
||||||
-xml(hint_no_copy,
|
-xml(hint_no_copy,
|
||||||
@ -2567,6 +2657,11 @@
|
|||||||
xmlns = <<"urn:xmpp:hints">>,
|
xmlns = <<"urn:xmpp:hints">>,
|
||||||
result = {hint, 'no-permanent-store'}}).
|
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,
|
-xml(search_instructions,
|
||||||
#elem{name = <<"instructions">>,
|
#elem{name = <<"instructions">>,
|
||||||
xmlns = <<"jabber:iq:search">>,
|
xmlns = <<"jabber:iq:search">>,
|
||||||
@ -2679,6 +2774,53 @@
|
|||||||
dec = {dec_int, [0, infinity]},
|
dec = {dec_int, [0, infinity]},
|
||||||
enc = {enc_int, []}}]}).
|
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}]}).
|
||||||
|
|
||||||
dec_tzo(Val) ->
|
dec_tzo(Val) ->
|
||||||
[H1, M1] = str:tokens(Val, <<":">>),
|
[H1, M1] = str:tokens(Val, <<":">>),
|
||||||
H = jlib:binary_to_integer(H1),
|
H = jlib:binary_to_integer(H1),
|
||||||
|
Loading…
Reference in New Issue
Block a user