Codebase list erlang-p1-xmpp / 6d8737a
Update upstream source from tag 'upstream/1.2.5' Update to upstream version '1.2.5' with Debian dir 4bbb5f2bff4502f3435c388fe15b80d93d294116 Philipp Huebner 5 years ago
45 changed file(s) with 9514 addition(s) and 396 deletion(s). Raw diff Collapse all Expand all
0 # Version 1.2.5
1
2 * Update packaging for hex
3
4 # Version 1.2.4
5
6 * Updating fast_xml to version 1.1.34.
7 * Updating stringprep to version 1.0.14.
8 * Updating p1_utils to version 1.0.13.
9 * Updating fast_tls to version 1.0.25.
10
11 # Version 1.2.3
12
13 * Updating fast_tls to version f36ea5b74526c2c1c9c38f8d473168d95804f59d.
14 * Updating fast_xml to version 1.1.33.
15 * Updating p1_utils to version 1.0.13.
16 * Updating stringprep to version 1.0.13.
17 * Added handling for xeps: 319, 047, 261, 260, 234, 300, 166
18 * Refactor of xmpp\_socket
19
020 # Version 1.2.2
121
222 * Updating fast_xml to version 1.1.32.
2626 dialyzer/erlang.plt:
2727 @mkdir -p dialyzer
2828 @dialyzer --build_plt --output_plt dialyzer/erlang.plt \
29 -o dialyzer/erlang.log --apps kernel stdlib sasl erts syntax_tools compiler; \
29 -o dialyzer/erlang.log --apps kernel stdlib sasl erts syntax_tools compiler asn1 crypto; \
3030 status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
3131
3232 dialyzer/deps.plt:
250250 - [XEP-0033](https://xmpp.org/extensions/xep-0033.html): Extended Stanza Addressing
251251 - [XEP-0039](https://xmpp.org/extensions/xep-0039.html): Statistics Gathering
252252 - [XEP-0045](https://xmpp.org/extensions/xep-0045.html): Multi-User Chat
253 - [XEP-0047](https://xmpp.org/extensions/xep-0047.html): In-Band Bytestreams
253254 - [XEP-0048](https://xmpp.org/extensions/xep-0048.html): Bookmarks
254255 - [XEP-0049](https://xmpp.org/extensions/xep-0049.html): Private XML Storage
255256 - [XEP-0050](https://xmpp.org/extensions/xep-0050.html): Ad-Hoc Commands
270271 - [XEP-0138](https://xmpp.org/extensions/xep-0138.html): Stream Compression
271272 - [XEP-0153](https://xmpp.org/extensions/xep-0153.html): vCard-Based Avatars
272273 - [XEP-0158](https://xmpp.org/extensions/xep-0158.html): CAPTCHA Forms
274 - [XEP-0166](https://xmpp.org/extensions/xep-0166.html): Jingle
273275 - [XEP-0172](https://xmpp.org/extensions/xep-0172.html): User Nickname
274276 - [XEP-0184](https://xmpp.org/extensions/xep-0184.html): Message Delivery Receipts
275277 - [XEP-0191](https://xmpp.org/extensions/xep-0191.html): Blocking Command
280282 - [XEP-0220](https://xmpp.org/extensions/xep-0220.html): Server Dialback
281283 - [XEP-0221](https://xmpp.org/extensions/xep-0221.html): Data Forms Media Element
282284 - [XEP-0231](https://xmpp.org/extensions/xep-0231.html): Bits of Binary
285 - [XEP-0234](https://xmpp.org/extensions/xep-0234.html): Jingle File Transfer
283286 - [XEP-0249](https://xmpp.org/extensions/xep-0249.html): Direct MUC Invitations
287 - [XEP-0260](https://xmpp.org/extensions/xep-0260.html): Jingle SOCKS5 Bytestreams Transport Method
288 - [XEP-0261](https://xmpp.org/extensions/xep-0261.html): Jingle In-Band Bytestreams Transport Method
284289 - [XEP-0264](https://xmpp.org/extensions/xep-0264.html): Jingle Content Thumbnails
285290 - [XEP-0279](https://xmpp.org/extensions/xep-0279.html): Server IP Check
286291 - [XEP-0280](https://xmpp.org/extensions/xep-0280.html): Message Carbons
287292 - [XEP-0297](https://xmpp.org/extensions/xep-0297.html): Stanza Forwarding
293 - [XEP-0300](https://xmpp.org/extensions/xep-0300.html): Use of Cryptographic Hash Functions in XMPP
288294 - [XEP-0313](https://xmpp.org/extensions/xep-0313.html): Message Archive Management
295 - [XEP-0319](https://xmpp.org/extensions/xep-0319.html): Last User Interaction in Presence
289296 - [XEP-0334](https://xmpp.org/extensions/xep-0334.html): Message Processing Hints
290297 - [XEP-0352](https://xmpp.org/extensions/xep-0352.html): Client State Indication
291298 - [XEP-0355](https://xmpp.org/extensions/xep-0355.html): Namespace Delegation
0 XmppAddr { iso(1) identified-organization(3)
1 dod(6) internet(1) security(5) mechanisms(5) pkix(7)
2 id-on(8) id-on-xmppAddr(5) }
3
4 DEFINITIONS EXPLICIT TAGS ::=
5 BEGIN
6
7 id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
8 dod(6) internet(1) security(5) mechanisms(5) pkix(7)
9 id-on(8) 5 }
10
11 XmppAddr ::= UTF8String
12
13 END
355355 > xmpp:get_id(#message{id = <<"abcd">>}).
356356 <<"abcd">>
357357 ```
358 **Example 2**: obtaining `id` of `xmlele()`
358 **Example 2**: obtaining `id` of `xmlel()`
359359 ```erlang
360360 > xmpp:get_id(#xmlel{attrs = [{<<"id">>, <<"abcd">>}]}).
361361 <<"abcd">>
132132 -define(NS_FEATURE_COMPRESS,
133133 <<"http://jabber.org/features/compress">>).
134134 -define(NS_FEATURE_MSGOFFLINE, <<"msgoffline">>).
135 -define(NS_FEATURE_PRESENCE, <<"presence">>).
136 -define(NS_FEATURE_IQ, <<"iq">>).
135137 -define(NS_FLEX_OFFLINE, <<"http://jabber.org/protocol/offline">>).
136138 -define(NS_COMPRESS,
137139 <<"http://jabber.org/protocol/compress">>).
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -record(scram, {storedkey = <<"">> :: binary(),
18 serverkey = <<"">> :: binary(),
19 salt = <<"">> :: binary(),
20 iterationcount = 0 :: integer()}).
21
22 -type scram() :: #scram{}.
23
24 -define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
9696 'no-permanent-store' | 'no-permanent-storage'}).
9797 -type hint() :: #hint{}.
9898
99 -record(jingle_error, {reason :: 'out-of-order' | 'tie-break' |
100 'unknown-session' | 'unsupported-info' |
101 'security-required'}).
102 -type jingle_error() :: #jingle_error{}.
103
104 -record(jingle_ft_error, {reason :: 'file-not-available' | 'file-too-large'}).
105 -type jingle_ft_error() :: #jingle_ft_error{}.
106
99107 -type xmpp_host() :: binary() | inet:ip_address() |
100108 {binary() | inet:ip_address(), inet:port_number()}.
101109
134142 port = 1080 :: non_neg_integer()}).
135143 -type streamhost() :: #streamhost{}.
136144
145 -record(carbons_enable, {}).
146 -type carbons_enable() :: #carbons_enable{}.
147
148 -record(carbons_private, {}).
149 -type carbons_private() :: #carbons_private{}.
150
151 -record(receipt_response, {id = <<>> :: binary()}).
152 -type receipt_response() :: #receipt_response{}.
153
154 -record(expire, {seconds :: non_neg_integer(),
155 stored :: 'undefined' | non_neg_integer()}).
156 -type expire() :: #expire{}.
157
158 -record(mix_leave, {}).
159 -type mix_leave() :: #mix_leave{}.
160
161 -record(muc_unsubscribe, {nick = <<>> :: binary(),
162 jid :: undefined | jid:jid()}).
163 -type muc_unsubscribe() :: #muc_unsubscribe{}.
164
165 -record(ps_unsubscribe, {node = <<>> :: binary(),
166 jid :: jid:jid(),
167 subid = <<>> :: binary()}).
168 -type ps_unsubscribe() :: #ps_unsubscribe{}.
169
137170 -record(sm_resume, {h :: non_neg_integer(),
138171 previd = <<>> :: binary(),
139172 xmlns = <<>> :: binary()}).
140173 -type sm_resume() :: #sm_resume{}.
141
142 -record(carbons_enable, {}).
143 -type carbons_enable() :: #carbons_enable{}.
144
145 -record(carbons_private, {}).
146 -type carbons_private() :: #carbons_private{}.
147
148 -record(receipt_response, {id = <<>> :: binary()}).
149 -type receipt_response() :: #receipt_response{}.
150
151 -record(expire, {seconds :: non_neg_integer(),
152 stored :: 'undefined' | non_neg_integer()}).
153 -type expire() :: #expire{}.
154
155 -record(mix_leave, {}).
156 -type mix_leave() :: #mix_leave{}.
157
158 -record(muc_unsubscribe, {nick = <<>> :: binary(),
159 jid :: undefined | jid:jid()}).
160 -type muc_unsubscribe() :: #muc_unsubscribe{}.
161
162 -record(ps_unsubscribe, {node = <<>> :: binary(),
163 jid :: jid:jid(),
164 subid = <<>> :: binary()}).
165 -type ps_unsubscribe() :: #ps_unsubscribe{}.
166174
167175 -record(ping, {}).
168176 -type ping() :: #ping{}.
188196 type :: 'both' | 'get' | 'managed_entity' | 'none' | 'outgoing' | 'roster' | 'set'}).
189197 -type privilege_perm() :: #privilege_perm{}.
190198
199 -record(ibb_open, {sid = <<>> :: binary(),
200 'block-size' :: non_neg_integer(),
201 stanza = iq :: 'iq' | 'message'}).
202 -type ibb_open() :: #ibb_open{}.
203
191204 -record(muc_decline, {reason = <<>> :: binary(),
192205 from :: undefined | jid:jid(),
193206 to :: undefined | jid:jid()}).
204217 xmlns = <<>> :: binary()}).
205218 -type upload_request_0() :: #upload_request_0{}.
206219
220 -record(ibb_data, {sid = <<>> :: binary(),
221 seq :: non_neg_integer(),
222 data = <<>> :: binary()}).
223 -type ibb_data() :: #ibb_data{}.
224
207225 -record(sm_a, {h :: non_neg_integer(),
208226 xmlns = <<>> :: binary()}).
209227 -type sm_a() :: #sm_a{}.
228
229 -record(jingle_ft_received, {creator :: 'initiator' | 'responder' | 'undefined',
230 name = <<>> :: binary()}).
231 -type jingle_ft_received() :: #jingle_ft_received{}.
232
233 -record(jingle_content, {creator :: 'initiator' | 'responder',
234 disposition = <<>> :: binary(),
235 name = <<>> :: binary(),
236 senders = both :: 'both' | 'initiator' | 'none' | 'responder',
237 sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
238 -type jingle_content() :: #jingle_content{}.
210239
211240 -record(starttls_proceed, {}).
212241 -type starttls_proceed() :: #starttls_proceed{}.
232261 events = [] :: [binary()]}).
233262 -type muc_subscribe() :: #muc_subscribe{}.
234263
264 -record(jingle_ibb_transport, {sid = <<>> :: binary(),
265 'block-size' :: non_neg_integer(),
266 stanza = iq :: 'iq' | 'message'}).
267 -type jingle_ibb_transport() :: #jingle_ibb_transport{}.
268
235269 -record(stanza_id, {by :: jid:jid(),
236270 id = <<>> :: binary()}).
237271 -type stanza_id() :: #stanza_id{}.
267301 -record(private, {sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
268302 -type private() :: #private{}.
269303
304 -record(ibb_close, {sid = <<>> :: binary()}).
305 -type ibb_close() :: #ibb_close{}.
306
270307 -record(sm_enable, {max :: 'undefined' | non_neg_integer(),
271308 resume = false :: boolean(),
272309 xmlns = <<>> :: binary()}).
277314
278315 -record(gone, {uri = <<>> :: binary()}).
279316 -type gone() :: #gone{}.
317
318 -record(hash, {algo = <<>> :: binary(),
319 data = <<>> :: binary()}).
320 -type hash() :: #hash{}.
321
322 -record(jingle_ft_range, {offset = 0 :: non_neg_integer(),
323 length :: 'undefined' | non_neg_integer(),
324 hash = [] :: [#hash{}]}).
325 -type jingle_ft_range() :: #jingle_ft_range{}.
280326
281327 -record(delegation_query, {to :: jid:jid(),
282328 delegate = [] :: [binary()]}).
326372 sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
327373 -type avatar_pointer() :: #avatar_pointer{}.
328374
375 -record(sm_r, {xmlns = <<>> :: binary()}).
376 -type sm_r() :: #sm_r{}.
377
329378 -record(muc_actor, {jid :: undefined | jid:jid(),
330379 nick = <<>> :: binary()}).
331380 -type muc_actor() :: #muc_actor{}.
340389 error :: 'undefined' | #stat_error{}}).
341390 -type stat() :: #stat{}.
342391
343 -record(sm_r, {xmlns = <<>> :: binary()}).
344 -type sm_r() :: #sm_r{}.
345
346392 -record(addresses, {list = [] :: [#address{}]}).
347393 -type addresses() :: #addresses{}.
348394
352398 -record(last, {seconds :: 'undefined' | non_neg_integer(),
353399 status = <<>> :: binary()}).
354400 -type last() :: #last{}.
401
402 -record('see-other-host', {host :: binary() | inet:ip_address() | {binary() | inet:ip_address(),inet:port_number()}}).
403 -type 'see-other-host'() :: #'see-other-host'{}.
404
405 -record(compress, {methods = [] :: [binary()]}).
406 -type compress() :: #compress{}.
355407
356408 -record(redirect, {uri = <<>> :: binary()}).
357409 -type redirect() :: #redirect{}.
363415 xmlns = <<>> :: binary()}).
364416 -type sm_enabled() :: #sm_enabled{}.
365417
366 -record('see-other-host', {host :: binary() | inet:ip_address() | {binary() | inet:ip_address(),inet:port_number()}}).
367 -type 'see-other-host'() :: #'see-other-host'{}.
368
369 -record(compress, {methods = [] :: [binary()]}).
370 -type compress() :: #compress{}.
371
372418 -record(upload_file_too_large, {'max-file-size' :: 'undefined' | integer(),
373419 xmlns = <<>> :: binary()}).
374420 -type upload_file_too_large() :: #upload_file_too_large{}.
383429 jid :: jid:jid()}).
384430 -type ps_subscribe() :: #ps_subscribe{}.
385431
432 -record(idle, {since :: erlang:timestamp()}).
433 -type idle() :: #idle{}.
434
386435 -record(muc_unique, {name = <<>> :: binary()}).
387436 -type muc_unique() :: #muc_unique{}.
388437
421470 -record(compression, {methods = [] :: [binary()]}).
422471 -type compression() :: #compression{}.
423472
424 -record(muc_subscriptions, {list = [] :: [jid:jid()]}).
473 -record(muc_subscription, {jid :: jid:jid(),
474 events = [] :: [binary()]}).
475 -type muc_subscription() :: #muc_subscription{}.
476
477 -record(muc_subscriptions, {list = [] :: [#muc_subscription{}]}).
425478 -type muc_subscriptions() :: #muc_subscriptions{}.
426479
427480 -record(ps_subscription, {xmlns = <<>> :: binary(),
479532 node = <<>> :: binary()}).
480533 -type stats() :: #stats{}.
481534
482 -record(sic, {ip :: undefined | inet:ip_address(),
483 port :: 'undefined' | non_neg_integer(),
484 xmlns = <<>> :: binary()}).
485 -type sic() :: #sic{}.
486
487535 -record(ps_items, {xmlns = <<>> :: binary(),
488536 node = <<>> :: binary(),
489537 items = [] :: [#ps_item{}],
501549 -record(stream_features, {sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
502550 -type stream_features() :: #stream_features{}.
503551
552 -record(sic, {ip :: undefined | inet:ip_address(),
553 port :: 'undefined' | non_neg_integer(),
554 xmlns = <<>> :: binary()}).
555 -type sic() :: #sic{}.
556
504557 -record(sasl_abort, {}).
505558 -type sasl_abort() :: #sasl_abort{}.
506559
535588 xmlns = <<>> :: binary()}).
536589 -type upload_slot() :: #upload_slot{}.
537590
538 -record(mix_participant, {jid :: jid:jid(),
539 nick = <<>> :: binary()}).
540 -type mix_participant() :: #mix_participant{}.
541
542591 -record(vcard_geo, {lat :: 'undefined' | binary(),
543592 lon :: 'undefined' | binary()}).
544593 -type vcard_geo() :: #vcard_geo{}.
545594
546 -record(compressed, {}).
547 -type compressed() :: #compressed{}.
548
549595 -record(sasl_failure, {reason :: 'aborted' | 'account-disabled' | 'bad-protocol' | 'credentials-expired' | 'encryption-required' | 'incorrect-encoding' | 'invalid-authzid' | 'invalid-mechanism' | 'malformed-request' | 'mechanism-too-weak' | 'not-authorized' | 'temporary-auth-failure' | 'undefined',
550596 text = [] :: [#text{}]}).
551597 -type sasl_failure() :: #sasl_failure{}.
552598
553 -record(block_list, {items = [] :: [#block_item{}]}).
554 -type block_list() :: #block_list{}.
599 -record(hash_used, {algo = <<>> :: binary()}).
600 -type hash_used() :: #hash_used{}.
555601
556602 -record(xevent, {offline = false :: boolean(),
557603 delivered = false :: boolean(),
573619 -record(origin_id, {id = <<>> :: binary()}).
574620 -type origin_id() :: #origin_id{}.
575621
622 -record(mix_participant, {jid :: jid:jid(),
623 nick = <<>> :: binary()}).
624 -type mix_participant() :: #mix_participant{}.
625
626 -record(compressed, {}).
627 -type compressed() :: #compressed{}.
628
629 -record(block_list, {items = [] :: [#block_item{}]}).
630 -type block_list() :: #block_list{}.
631
576632 -record(version, {name :: 'undefined' | binary(),
577633 ver :: 'undefined' | binary(),
578634 os :: 'undefined' | binary()}).
579635 -type version() :: #version{}.
636
637 -record(jingle_ft_file, {date :: undefined | erlang:timestamp(),
638 desc = [] :: [#text{}],
639 hash = [] :: [#hash{}],
640 'hash-used' :: 'undefined' | #hash_used{},
641 'media-type' :: 'undefined' | binary(),
642 name :: 'undefined' | binary(),
643 size :: 'undefined' | non_neg_integer(),
644 range :: 'undefined' | #jingle_ft_range{}}).
645 -type jingle_ft_file() :: #jingle_ft_file{}.
646
647 -record(jingle_ft_checksum, {creator :: 'initiator' | 'responder' | 'undefined',
648 name = <<>> :: binary(),
649 file :: #jingle_ft_file{}}).
650 -type jingle_ft_checksum() :: #jingle_ft_checksum{}.
651
652 -record(jingle_ft_description, {file :: 'undefined' | #jingle_ft_file{}}).
653 -type jingle_ft_description() :: #jingle_ft_description{}.
580654
581655 -record(legacy_auth_feature, {}).
582656 -type legacy_auth_feature() :: #legacy_auth_feature{}.
596670
597671 -record(vcard_xupdate, {hash :: 'undefined' | binary()}).
598672 -type vcard_xupdate() :: #vcard_xupdate{}.
599
600 -record(delegated, {ns = <<>> :: binary(),
601 attrs = [] :: [binary()]}).
602 -type delegated() :: #delegated{}.
603
604 -record(carbons_disable, {}).
605 -type carbons_disable() :: #carbons_disable{}.
606673
607674 -record(bytestreams, {hosts = [] :: [#streamhost{}],
608675 used :: undefined | jid:jid(),
636703 node = <<>> :: binary()}).
637704 -type push_disable() :: #push_disable{}.
638705
706 -record(jingle_s5b_candidate, {cid = <<>> :: binary(),
707 host :: inet:ip_address(),
708 port :: 'undefined' | non_neg_integer(),
709 jid :: jid:jid(),
710 type = direct :: 'assisted' | 'direct' | 'proxy' | 'tunnel',
711 priority :: non_neg_integer()}).
712 -type jingle_s5b_candidate() :: #jingle_s5b_candidate{}.
713
714 -record(jingle_s5b_transport, {sid = <<>> :: binary(),
715 dstaddr = <<>> :: binary(),
716 mode = tcp :: 'tcp' | 'udp',
717 candidates = [] :: [#jingle_s5b_candidate{}],
718 'candidate-used' :: 'undefined' | binary(),
719 activated :: 'undefined' | binary(),
720 error :: 'candidate-error' | 'proxy-error' | 'undefined'}).
721 -type jingle_s5b_transport() :: #jingle_s5b_transport{}.
722
723 -record(delegated, {ns = <<>> :: binary(),
724 attrs = [] :: [binary()]}).
725 -type delegated() :: #delegated{}.
726
727 -record(carbons_disable, {}).
728 -type carbons_disable() :: #carbons_disable{}.
729
639730 -record(adhoc_actions, {execute :: 'complete' | 'next' | 'prev' | 'undefined',
640731 prev = false :: boolean(),
641732 next = false :: boolean(),
645736 -record(vcard_org, {name :: 'undefined' | binary(),
646737 units = [] :: [binary()]}).
647738 -type vcard_org() :: #vcard_org{}.
648
649 -record(rsm_set, {'after' :: 'undefined' | binary(),
650 before :: 'undefined' | binary(),
651 count :: 'undefined' | non_neg_integer(),
652 first :: 'undefined' | #rsm_first{},
653 index :: 'undefined' | non_neg_integer(),
654 last :: 'undefined' | binary(),
655 max :: 'undefined' | non_neg_integer()}).
656 -type rsm_set() :: #rsm_set{}.
657
658 -record(mam_fin, {xmlns = <<>> :: binary(),
659 id = <<>> :: binary(),
660 rsm :: 'undefined' | #rsm_set{},
661 stable :: 'false' | 'true' | 'undefined',
662 complete :: 'false' | 'true' | 'undefined'}).
663 -type mam_fin() :: #mam_fin{}.
664
665 -record(disco_items, {node = <<>> :: binary(),
666 items = [] :: [#disco_item{}],
667 rsm :: 'undefined' | #rsm_set{}}).
668 -type disco_items() :: #disco_items{}.
669739
670740 -record(vcard_tel, {home = false :: boolean(),
671741 work = false :: boolean(),
683753 number :: 'undefined' | binary()}).
684754 -type vcard_tel() :: #vcard_tel{}.
685755
756 -record(rsm_set, {'after' :: 'undefined' | binary(),
757 before :: 'undefined' | binary(),
758 count :: 'undefined' | non_neg_integer(),
759 first :: 'undefined' | #rsm_first{},
760 index :: 'undefined' | non_neg_integer(),
761 last :: 'undefined' | binary(),
762 max :: 'undefined' | non_neg_integer()}).
763 -type rsm_set() :: #rsm_set{}.
764
765 -record(mam_fin, {xmlns = <<>> :: binary(),
766 id = <<>> :: binary(),
767 rsm :: 'undefined' | #rsm_set{},
768 stable :: 'false' | 'true' | 'undefined',
769 complete :: 'false' | 'true' | 'undefined'}).
770 -type mam_fin() :: #mam_fin{}.
771
772 -record(disco_items, {node = <<>> :: binary(),
773 items = [] :: [#disco_item{}],
774 rsm :: 'undefined' | #rsm_set{}}).
775 -type disco_items() :: #disco_items{}.
776
686777 -record(muc_destroy, {xmlns = <<>> :: binary(),
687778 jid :: undefined | jid:jid(),
688779 reason = <<>> :: binary(),
704795 suffix :: 'undefined' | binary()}).
705796 -type vcard_name() :: #vcard_name{}.
706797
798 -record(media_uri, {type = <<>> :: binary(),
799 uri = <<>> :: binary()}).
800 -type media_uri() :: #media_uri{}.
801
802 -record(media, {height :: 'undefined' | non_neg_integer(),
803 width :: 'undefined' | non_neg_integer(),
804 uri = [] :: [#media_uri{}]}).
805 -type media() :: #media{}.
806
807 -record(vcard_key, {type :: 'undefined' | binary(),
808 cred :: 'undefined' | binary()}).
809 -type vcard_key() :: #vcard_key{}.
810
707811 -record(identity, {category = <<>> :: binary(),
708812 type = <<>> :: binary(),
709813 lang = <<>> :: binary(),
710814 name = <<>> :: binary()}).
711815 -type identity() :: #identity{}.
712
713 -record(media_uri, {type = <<>> :: binary(),
714 uri = <<>> :: binary()}).
715 -type media_uri() :: #media_uri{}.
716
717 -record(media, {height :: 'undefined' | non_neg_integer(),
718 width :: 'undefined' | non_neg_integer(),
719 uri = [] :: [#media_uri{}]}).
720 -type media() :: #media{}.
721
722 -record(vcard_key, {type :: 'undefined' | binary(),
723 cred :: 'undefined' | binary()}).
724 -type vcard_key() :: #vcard_key{}.
725816
726817 -record(bookmark_conference, {name = <<>> :: binary(),
727818 jid :: jid:jid(),
843934 -type pubsub_owner() :: #pubsub_owner{}.
844935
845936 -record(ps_options, {node = <<>> :: binary(),
846 jid :: jid:jid(),
937 jid :: undefined | jid:jid(),
847938 subid = <<>> :: binary(),
848939 xdata :: 'undefined' | #xdata{}}).
849940 -type ps_options() :: #ps_options{}.
9281019 -record(push_notification, {xdata :: 'undefined' | #xdata{}}).
9291020 -type push_notification() :: #push_notification{}.
9301021
1022 -record(jingle_reason, {reason :: atom(),
1023 text = [] :: [#text{}],
1024 sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
1025 -type jingle_reason() :: #jingle_reason{}.
1026
1027 -record(jingle, {action :: atom(),
1028 sid = <<>> :: binary(),
1029 initiator :: undefined | jid:jid(),
1030 responder :: undefined | jid:jid(),
1031 content = [] :: [#jingle_content{}],
1032 reason :: 'undefined' | #jingle_reason{},
1033 sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
1034 -type jingle() :: #jingle{}.
1035
9311036 -record(mix_join, {jid :: undefined | jid:jid(),
9321037 subscribe = [] :: [binary()]}).
9331038 -type mix_join() :: #mix_join{}.
9511056 active :: 'none' | 'undefined' | binary()}).
9521057 -type privacy_query() :: #privacy_query{}.
9531058
954 -record(stream_error, {reason :: atom() | #'see-other-host'{},
955 text = [] :: [#text{}]}).
956 -type stream_error() :: #stream_error{}.
957
9581059 -record(sm_failed, {reason :: atom() | #gone{} | #redirect{},
9591060 text = [] :: [#text{}],
9601061 h :: 'undefined' | non_neg_integer(),
9681069 text = [] :: [#text{}],
9691070 sub_els = [] :: [xmpp_element() | fxml:xmlel()]}).
9701071 -type stanza_error() :: #stanza_error{}.
1072
1073 -record(stream_error, {reason :: atom() | #'see-other-host'{},
1074 text = [] :: [#text{}]}).
1075 -type stream_error() :: #stream_error{}.
9711076
9721077 -record(vcard_logo, {type :: 'undefined' | binary(),
9731078 binval :: 'undefined' | binary(),
10091114 utc :: undefined | erlang:timestamp()}).
10101115 -type time() :: #time{}.
10111116
1012 -type xmpp_element() :: ps_options() |
1117 -type xmpp_element() :: stats() |
1118 jingle_reason() |
1119 starttls_failure() |
1120 jingle_s5b_transport() |
1121 vcard_name() |
1122 sm_enable() |
1123 stat() |
1124 ps_error() |
1125 feature_register() |
1126 privacy_query() |
1127 feature_csi() |
1128 vcard_photo() |
1129 jingle_ft_checksum() |
1130 ping() |
1131 receipt_request() |
1132 sm_resumed() |
1133 hash() |
1134 message_thread() |
1135 presence() |
1136 muc() |
1137 jingle() |
1138 sm_failed() |
1139 media_uri() |
1140 jingle_ft_error() |
1141 delegation_query() |
1142 upload_request_0() |
1143 search_item() |
1144 block_item() |
1145 media() |
1146 muc_owner() |
10131147 ps_publish() |
1148 muc_unsubscribe() |
1149 jingle_ft_file() |
1150 adhoc_actions() |
1151 mam_result() |
1152 muc_actor() |
1153 'see-other-host'() |
1154 handshake() |
1155 search() |
1156 sasl_challenge() |
1157 xcaptcha() |
1158 xmpp_session() |
1159 version() |
1160 db_result() |
1161 db_verify() |
1162 carbons_received() |
1163 ibb_close() |
1164 jingle_ft_received() |
1165 redirect() |
1166 privilege_perm() |
1167 sm_enabled() |
1168 privilege() |
1169 idle() |
1170 ps_affiliation() |
1171 disco_info() |
1172 upload_file_too_large() |
1173 mix_leave() |
1174 compress_failure() |
1175 caps() |
1176 block_list() |
1177 vcard_sound() |
1178 chatstate() |
1179 jingle_ft_range() |
1180 rosterver_feature() |
1181 mam_prefs() |
1182 sm_a() |
1183 ps_options() |
1184 avatar_data() |
1185 muc_subscribe() |
1186 jingle_ibb_transport() |
1187 upload_retry() |
1188 stream_start() |
1189 disco_item() |
1190 bookmark_url() |
1191 upload_slot_0() |
1192 muc_item() |
10141193 address() |
1015 roster_item() |
1016 bookmark_url() |
1017 push_enable() |
1194 muc_subscription() |
1195 text() |
1196 db_feature() |
1197 xdata_option() |
1198 push_disable() |
1199 compression() |
1200 oob_x() |
1201 roster_query() |
1202 ibb_data() |
10181203 delegation() |
1019 disco_info() |
1020 ps_retract() |
1204 pubsub() |
1205 hint() |
1206 block() |
1207 nick() |
1208 mam_archived() |
1209 rsm_first() |
1210 muc_invite() |
1211 adhoc_command() |
1212 identity() |
1213 vcard_geo() |
1214 carbons_private() |
1215 vcard_adr() |
1216 mam_query() |
1217 offline_item() |
1218 hash_used() |
1219 bookmark_storage() |
1220 muc_user() |
1221 ps_event() |
1222 adhoc_note() |
1223 expire() |
1224 carbons_sent() |
1225 ibb_open() |
1226 shim() |
1227 privacy_item() |
1228 ps_item() |
1229 ps_items() |
1230 muc_admin() |
1231 bytestreams() |
1232 carbons_enable() |
1233 muc_destroy() |
1234 upload_slot() |
1235 privacy_list() |
1236 unblock() |
10211237 stanza_error() |
1022 streamhost() |
1023 pubsub() |
1024 vcard_name() |
1025 xmpp_session() |
1026 sm_a() |
1027 upload_file_too_large() |
1028 vcard_geo() |
1029 mam_query() |
1030 delay() |
1031 mam_archived() |
1032 adhoc_actions() |
1033 mix_join() |
1034 legacy_auth() |
1035 media() |
1036 xdata_option() |
1037 vcard_org() |
1038 stanza_id() |
1039 starttls() |
1040 upload_request_0() |
1041 x_conference() |
1042 bind() |
1043 starttls_proceed() |
1238 avatar_pointer() |
1239 sasl_response() |
1240 thumbnail() |
1241 muc_decline() |
1242 muc_subscriptions() |
1243 avatar_info() |
1244 addresses() |
1245 delegated() |
1246 muc_unique() |
10441247 vcard_tel() |
1045 muc_item() |
1046 forwarded() |
1047 privacy_query() |
1048 addresses() |
1049 sasl_challenge() |
1050 vcard_xupdate() |
1051 disco_item() |
1052 offline() |
1053 carbons_private() |
1054 xcaptcha() |
1055 push_notification() |
1056 sm_resume() |
1057 muc_invite() |
1058 sasl_auth() |
1059 ps_error() |
1060 last() |
10611248 avatar_meta() |
1062 time() |
1063 db_feature() |
1064 mix_leave() |
1065 vcard_temp() |
1066 sm_resumed() |
10671249 stream_error() |
1068 identity() |
1069 vcard_adr() |
1070 'see-other-host'() |
1071 roster_query() |
1072 xevent() |
1073 presence() |
1074 sic() |
1075 bob_data() |
1076 register() |
1077 muc_subscriptions() |
1078 private() |
1079 stream_features() |
1080 text() |
1081 pubsub_owner() |
1082 feature_sm() |
1083 mam_prefs() |
1084 expire() |
1085 redirect() |
1086 sm_r() |
1087 xdata_field() |
1088 upload_request() |
1089 vcard_label() |
1090 sasl_failure() |
1091 media_uri() |
1092 sm_enabled() |
1093 ps_event() |
1094 vcard_sound() |
1095 handshake() |
1096 sasl_success() |
1097 block_item() |
1098 carbons_received() |
1099 muc_unsubscribe() |
1100 stat_error() |
1101 feature_register() |
1102 muc_admin() |
1103 xdata() |
1104 sm_enable() |
1105 compression() |
1106 upload_slot() |
1107 vcard_photo() |
1108 sm_failed() |
1109 ps_subscribe() |
1110 message_thread() |
1111 adhoc_command() |
1112 unblock() |
1113 bookmark_storage() |
1114 privilege() |
1250 compressed() |
11151251 vcard_email() |
11161252 report() |
1117 compress_failure() |
1118 shim() |
1119 delegated() |
1253 xdata_field() |
1254 xdata() |
1255 jingle_content() |
1256 vcard_org() |
1257 vcard_xupdate() |
1258 legacy_auth() |
1259 sm_r() |
1260 stream_features() |
1261 origin_id() |
1262 sasl_auth() |
1263 push_notification() |
1264 feature_sm() |
1265 xevent() |
1266 vcard_temp() |
1267 sasl_mechanisms() |
11201268 vcard_key() |
1121 nick() |
1122 stats() |
1123 push_disable() |
1124 search() |
1125 search_item() |
1126 ps_items() |
1269 x_conference() |
1270 mam_fin() |
1271 vcard_label() |
1272 upload_request() |
1273 mix_participant() |
1274 ps_retract() |
1275 sasl_failure() |
1276 last() |
1277 ps_subscribe() |
1278 jingle_error() |
1279 private() |
1280 rsm_set() |
1281 gone() |
1282 streamhost() |
1283 roster_item() |
1284 sasl_success() |
1285 jingle_ft_description() |
11271286 bookmark_conference() |
1128 privacy_list() |
1129 offline_item() |
1130 privacy_item() |
1131 origin_id() |
1132 receipt_request() |
1133 rsm_first() |
1134 mam_result() |
1135 carbons_enable() |
1136 ps_item() |
1137 sasl_mechanisms() |
1138 muc_user() |
1139 stream_start() |
1140 stat() |
1141 hint() |
1142 gone() |
1143 thumbnail() |
1287 stat_error() |
11441288 legacy_auth_feature() |
1145 sasl_response() |
1146 rsm_set() |
1289 starttls() |
1290 bob_data() |
1291 receipt_response() |
1292 sasl_abort() |
1293 jingle_s5b_candidate() |
1294 sic() |
1295 forwarded() |
1296 muc_history() |
1297 bind() |
1298 ps_subscription() |
1299 mix_join() |
1300 delay() |
11471301 compress() |
1148 chatstate() |
1149 upload_slot_0() |
1150 privilege_perm() |
1151 block_list() |
1152 caps() |
1153 mam_fin() |
1154 block() |
1155 feature_csi() |
1156 avatar_data() |
1157 oob_x() |
1158 adhoc_note() |
1159 carbons_disable() |
1160 muc_decline() |
1161 compressed() |
1162 muc() |
1302 ps_unsubscribe() |
1303 stanza_id() |
1304 vcard_logo() |
1305 offline() |
1306 disco_items() |
1307 csi() |
1308 sm_resume() |
11631309 iq() |
1164 ps_unsubscribe() |
1165 sasl_abort() |
1310 register() |
1311 time() |
1312 push_enable() |
1313 starttls_proceed() |
1314 pubsub_owner() |
11661315 message() |
1167 muc_destroy() |
1168 ps_affiliation() |
1169 delegation_query() |
1170 csi() |
1171 upload_retry() |
1172 disco_items() |
1173 starttls_failure() |
1174 vcard_logo() |
1175 ping() |
1176 muc_owner() |
1177 ps_subscription() |
1178 muc_subscribe() |
1179 mix_participant() |
1180 version() |
1181 bytestreams() |
1182 avatar_pointer() |
1183 muc_actor() |
1184 avatar_info() |
1185 receipt_response() |
1186 muc_history() |
1187 db_result() |
1188 db_verify() |
1189 muc_unique() |
1190 carbons_sent() |
1191 rosterver_feature().
1316 carbons_disable().
1818 %%%
1919 %%%----------------------------------------------------------------------
2020
21 {erl_opts, [debug_info, {src_dirs, ["src"]},
21 {erl_opts, [debug_info, {src_dirs, ["asn1", "src"]},
2222 nowarn_export_all,
2323 {i, "include"},
2424 {i, "../fast_xml/include"},
3030 {"priv/lib/xmpp_uri.so", ["c_src/xmpp_uri.c"]},
3131 {"priv/lib/xmpp_lang.so", ["c_src/xmpp_lang.c"]}]}.
3232
33 {deps, [{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.32"}}},
34 {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.12"}}},
35 {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.12"}}}]}.
33 {deps, [{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.13"}}},
34 {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.34"}}},
35 {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.25"}}},
36 {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.4"}}},
37 {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.14"}}}]}.
3638
3739 {clean_files, ["c_src/jid.gcda", "c_src/jid.gcno"]}.
3840
21892189 attrs = [#attr{name = <<"node">>},
21902190 #attr{name = <<"subid">>},
21912191 #attr{name = <<"jid">>,
2192 required = true,
21932192 dec = {jid, decode, []},
21942193 enc = {jid, encode, []}}],
21952194 refs = [#ref{name = xdata, min = 0, max = 1,
29132912 #elem{name = <<"subscription">>,
29142913 xmlns = <<"urn:xmpp:mucsub:0">>,
29152914 module = p1_mucsub,
2916 result = '$jid',
2917 attrs = [#attr{name = <<"jid">>,
2915 result = {muc_subscription, '$jid', '$events'},
2916 attrs = [#attr{name = <<"jid">>,
29182917 required = true,
29192918 dec = {jid, decode, []},
2920 enc = {jid, encode, []}}]}).
2919 enc = {jid, encode, []}}],
2920 refs = [#ref{name = muc_subscribe_event, label = '$events'}]}).
29212921
29222922 -xml(muc_subscriptions,
29232923 #elem{name = <<"subscriptions">>,
41974197 #ref{name = avatar_pointer, label = '$pointer',
41984198 min = 0, max = 1}]}).
41994199
4200 -xml(hash,
4201 #elem{name = <<"hash">>,
4202 xmlns = <<"urn:xmpp:hashes:2">>,
4203 module = 'xep0300',
4204 result = {hash, '$algo', '$data'},
4205 attrs = [#attr{name = <<"algo">>,
4206 required = true}],
4207 cdata = #cdata{label = '$data',
4208 enc = {base64, encode, []},
4209 dec = {base64, decode, []}}}).
4210
4211 -xml(hash_used,
4212 #elem{name = <<"hash-used">>,
4213 xmlns = <<"urn:xmpp:hashes:2">>,
4214 module = 'xep0300',
4215 result = {hash_used, '$algo'},
4216 attrs = [#attr{name = <<"algo">>,
4217 required = true}]}).
4218
4219 -xml(ibb_open,
4220 #elem{name = <<"open">>,
4221 xmlns = <<"http://jabber.org/protocol/ibb">>,
4222 module = 'xep0047',
4223 result = {ibb_open, '$sid', '$block-size', '$stanza'},
4224 attrs = [#attr{name = <<"sid">>, required = true},
4225 #attr{name = <<"block-size">>, required = true,
4226 enc = {enc_int, []},
4227 dec = {dec_int, [0, infinity]}},
4228 #attr{name = <<"stanza">>,
4229 default = iq,
4230 enc = {enc_enum, []},
4231 dec = {dec_enum, [[iq, message]]}}]}).
4232
4233 -xml(ibb_data,
4234 #elem{name = <<"data">>,
4235 xmlns = <<"http://jabber.org/protocol/ibb">>,
4236 module = 'xep0047',
4237 result = {ibb_data, '$sid', '$seq', '$data'},
4238 attrs = [#attr{name = <<"sid">>, required = true},
4239 #attr{name = <<"seq">>, required = true,
4240 enc = {enc_int, []},
4241 dec = {dec_int, [0, infinity]}}],
4242 cdata = #cdata{label = '$data',
4243 enc = {base64, encode, []},
4244 dec = {base64, decode, []}}}).
4245
4246 -xml(ibb_close,
4247 #elem{name = <<"close">>,
4248 xmlns = <<"http://jabber.org/protocol/ibb">>,
4249 module = 'xep0047',
4250 result = {ibb_close, '$sid'},
4251 attrs = [#attr{name = <<"sid">>, required = true}]}).
4252
4253 -xml(idle,
4254 #elem{name = <<"idle">>,
4255 xmlns = <<"urn:xmpp:idle:1">>,
4256 module = 'xep0319',
4257 result = {idle, '$since'},
4258 attrs = [#attr{name = <<"since">>, required = true,
4259 enc = {enc_utc, []}, dec = {dec_utc, []}}]}).
4260
4261 -record(jingle_error, {reason :: 'out-of-order' | 'tie-break' |
4262 'unknown-session' | 'unsupported-info' |
4263 'security-required'}).
4264 -type jingle_error() :: #jingle_error{}.
4265
4266 -xml(jingle_error_out_of_order,
4267 #elem{name = <<"out-of-order">>,
4268 xmlns = <<"urn:xmpp:jingle:errors:1">>,
4269 module = 'xep0166',
4270 result = {jingle_error, 'out-of-order'}}).
4271 -xml(jingle_error_tie_break,
4272 #elem{name = <<"tie-break">>,
4273 xmlns = <<"urn:xmpp:jingle:errors:1">>,
4274 module = 'xep0166',
4275 result = {jingle_error, 'tie-break'}}).
4276 -xml(jingle_error_unknown_session,
4277 #elem{name = <<"unknown-session">>,
4278 xmlns = <<"urn:xmpp:jingle:errors:1">>,
4279 module = 'xep0166',
4280 result = {jingle_error, 'unknown-session'}}).
4281 -xml(jingle_error_unsupported_info,
4282 #elem{name = <<"unsupported-info">>,
4283 xmlns = <<"urn:xmpp:jingle:errors:1">>,
4284 module = 'xep0166',
4285 result = {jingle_error, 'unsupported-info'}}).
4286 -xml(jingle_error_security_required,
4287 #elem{name = <<"security-required">>,
4288 xmlns = <<"urn:xmpp:jingle:errors:1">>,
4289 module = 'xep0166',
4290 result = {jingle_error, 'security-required'}}).
4291
4292 -xml(jingle_reason_alternative_session,
4293 #elem{name = <<"alternative-session">>,
4294 xmlns = <<"urn:xmpp:jingle:1">>,
4295 module = 'xep0166',
4296 result = 'alternative-session'}).
4297 -xml(jingle_reason_busy,
4298 #elem{name = <<"busy">>,
4299 xmlns = <<"urn:xmpp:jingle:1">>,
4300 module = 'xep0166',
4301 result = 'busy'}).
4302 -xml(jingle_reason_cancel,
4303 #elem{name = <<"cancel">>,
4304 xmlns = <<"urn:xmpp:jingle:1">>,
4305 module = 'xep0166',
4306 result = 'cancel'}).
4307 -xml(jingle_reason_connectivity_error,
4308 #elem{name = <<"connectivity-error">>,
4309 xmlns = <<"urn:xmpp:jingle:1">>,
4310 module = 'xep0166',
4311 result = 'connectivity-error'}).
4312 -xml(jingle_reason_decline,
4313 #elem{name = <<"decline">>,
4314 xmlns = <<"urn:xmpp:jingle:1">>,
4315 module = 'xep0166',
4316 result = 'decline'}).
4317 -xml(jingle_reason_expired,
4318 #elem{name = <<"expired">>,
4319 xmlns = <<"urn:xmpp:jingle:1">>,
4320 module = 'xep0166',
4321 result = 'expired'}).
4322 -xml(jingle_reason_failed_application,
4323 #elem{name = <<"failed-application">>,
4324 xmlns = <<"urn:xmpp:jingle:1">>,
4325 module = 'xep0166',
4326 result = 'failed-application'}).
4327 -xml(jingle_reason_failed_transport,
4328 #elem{name = <<"failed-transport">>,
4329 xmlns = <<"urn:xmpp:jingle:1">>,
4330 module = 'xep0166',
4331 result = 'failed-transport'}).
4332 -xml(jingle_reason_general_error,
4333 #elem{name = <<"general-error">>,
4334 xmlns = <<"urn:xmpp:jingle:1">>,
4335 module = 'xep0166',
4336 result = 'general-error'}).
4337 -xml(jingle_reason_gone,
4338 #elem{name = <<"gone">>,
4339 xmlns = <<"urn:xmpp:jingle:1">>,
4340 module = 'xep0166',
4341 result = 'gone'}).
4342 -xml(jingle_reason_incompatible_parameters,
4343 #elem{name = <<"incompatible-parameters">>,
4344 xmlns = <<"urn:xmpp:jingle:1">>,
4345 module = 'xep0166',
4346 result = 'incompatible-parameters'}).
4347 -xml(jingle_reason_media_error,
4348 #elem{name = <<"media-error">>,
4349 xmlns = <<"urn:xmpp:jingle:1">>,
4350 module = 'xep0166',
4351 result = 'media-error'}).
4352 -xml(jingle_reason_security_error,
4353 #elem{name = <<"security-error">>,
4354 xmlns = <<"urn:xmpp:jingle:1">>,
4355 module = 'xep0166',
4356 result = 'security-error'}).
4357 -xml(jingle_reason_success,
4358 #elem{name = <<"success">>,
4359 xmlns = <<"urn:xmpp:jingle:1">>,
4360 module = 'xep0166',
4361 result = 'success'}).
4362 -xml(jingle_reason_timeout,
4363 #elem{name = <<"timeout">>,
4364 xmlns = <<"urn:xmpp:jingle:1">>,
4365 module = 'xep0166',
4366 result = 'timeout'}).
4367 -xml(jingle_reason_unsupported_applications,
4368 #elem{name = <<"unsupported-applications">>,
4369 xmlns = <<"urn:xmpp:jingle:1">>,
4370 module = 'xep0166',
4371 result = 'unsupported-applications'}).
4372 -xml(jingle_reason_unsupported_transports,
4373 #elem{name = <<"unsupported-transports">>,
4374 xmlns = <<"urn:xmpp:jingle:1">>,
4375 module = 'xep0166',
4376 result = 'unsupported-transports'}).
4377
4378 -xml(jingle_reason_text,
4379 #elem{name = <<"text">>,
4380 xmlns = <<"urn:xmpp:jingle:1">>,
4381 module = 'xep0166',
4382 result = {text, '$lang', '$data'},
4383 cdata = #cdata{label = '$data'},
4384 attrs = [#attr{name = <<"xml:lang">>,
4385 label = '$lang'}]}).
4386
4387 -xml(jingle_reason,
4388 #elem{name = <<"reason">>,
4389 xmlns = <<"urn:xmpp:jingle:1">>,
4390 module = 'xep0166',
4391 result = {jingle_reason, '$reason', '$text', '$_els'},
4392 refs = [#ref{name = jingle_reason_text, label = '$text'},
4393 #ref{name = jingle_reason_alternative_session,
4394 min = 0, max = 1, label = '$reason'},
4395 #ref{name = jingle_reason_busy,
4396 min = 0, max = 1, label = '$reason'},
4397 #ref{name = jingle_reason_cancel,
4398 min = 0, max = 1, label = '$reason'},
4399 #ref{name = jingle_reason_connectivity_error,
4400 min = 0, max = 1, label = '$reason'},
4401 #ref{name = jingle_reason_decline,
4402 min = 0, max = 1, label = '$reason'},
4403 #ref{name = jingle_reason_expired,
4404 min = 0, max = 1, label = '$reason'},
4405 #ref{name = jingle_reason_failed_application,
4406 min = 0, max = 1, label = '$reason'},
4407 #ref{name = jingle_reason_failed_transport,
4408 min = 0, max = 1, label = '$reason'},
4409 #ref{name = jingle_reason_general_error,
4410 min = 0, max = 1, label = '$reason'},
4411 #ref{name = jingle_reason_gone,
4412 min = 0, max = 1, label = '$reason'},
4413 #ref{name = jingle_reason_incompatible_parameters,
4414 min = 0, max = 1, label = '$reason'},
4415 #ref{name = jingle_reason_media_error,
4416 min = 0, max = 1, label = '$reason'},
4417 #ref{name = jingle_reason_security_error,
4418 min = 0, max = 1, label = '$reason'},
4419 #ref{name = jingle_reason_success,
4420 min = 0, max = 1, label = '$reason'},
4421 #ref{name = jingle_reason_timeout,
4422 min = 0, max = 1, label = '$reason'},
4423 #ref{name = jingle_reason_unsupported_applications,
4424 min = 0, max = 1, label = '$reason'},
4425 #ref{name = jingle_reason_unsupported_transports,
4426 min = 0, max = 1, label = '$reason'}]}).
4427
4428 -xml(jingle_content,
4429 #elem{name = <<"content">>,
4430 xmlns = <<"urn:xmpp:jingle:1">>,
4431 module = 'xep0166',
4432 result = {jingle_content, '$creator', '$disposition',
4433 '$name', '$senders', '$_els'},
4434 attrs = [#attr{name = <<"creator">>,
4435 required = true,
4436 enc = {enc_enum, []},
4437 dec = {dec_enum, [[initiator, responder]]}},
4438 #attr{name = <<"disposition">>},
4439 #attr{name = <<"name">>,
4440 required = true},
4441 #attr{name = <<"senders">>,
4442 enc = {enc_enum, []},
4443 dec = {dec_enum, [[both, initiator,
4444 none, responder]]},
4445 default = both}]}).
4446
4447 -xml(jingle,
4448 #elem{name = <<"jingle">>,
4449 xmlns = <<"urn:xmpp:jingle:1">>,
4450 module = 'xep0166',
4451 result = {jingle, '$action', '$sid', '$initiator', '$responder',
4452 '$content', '$reason', '$_els'},
4453 attrs = [#attr{name = <<"action">>,
4454 required = true,
4455 enc = {enc_enum, []},
4456 dec = {dec_enum,
4457 [['content-accept',
4458 'content-add',
4459 'content-modify',
4460 'content-reject',
4461 'content-remove',
4462 'description-info',
4463 'security-info',
4464 'session-accept',
4465 'session-info',
4466 'session-initiate',
4467 'session-terminate',
4468 'transport-accept',
4469 'transport-info',
4470 'transport-reject',
4471 'transport-replace']]}},
4472 #attr{name = <<"sid">>,
4473 required = true},
4474 #attr{name = <<"initiator">>,
4475 enc = {jid, encode, []},
4476 dec = {jid, decode, []}},
4477 #attr{name = <<"responder">>,
4478 enc = {jid, encode, []},
4479 dec = {jid, decode, []}}],
4480 refs = [#ref{name = jingle_content, label = '$content'},
4481 #ref{name = jingle_reason, label = '$reason',
4482 min = 0, max = 1}]}).
4483
4484 -xml(jingle_ft_date,
4485 #elem{name = <<"date">>,
4486 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4487 module = 'xep0234',
4488 cdata = #cdata{required = true,
4489 enc = {enc_utc, []},
4490 dec = {dec_utc, []}},
4491 result = '$cdata'}).
4492
4493 -xml(jingle_ft_desc,
4494 #elem{name = <<"desc">>,
4495 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4496 module = 'xep0234',
4497 result = {text, '$lang', '$data'},
4498 cdata = #cdata{label = '$data'},
4499 attrs = [#attr{name = <<"xml:lang">>,
4500 label = '$lang'}]}).
4501
4502 -xml(jingle_ft_media_type,
4503 #elem{name = <<"media-type">>,
4504 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4505 module = 'xep0234',
4506 cdata = #cdata{required = true},
4507 result = '$cdata'}).
4508
4509 -xml(jingle_ft_name,
4510 #elem{name = <<"name">>,
4511 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4512 module = 'xep0234',
4513 cdata = #cdata{required = true},
4514 result = '$cdata'}).
4515
4516 -xml(jingle_ft_size,
4517 #elem{name = <<"size">>,
4518 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4519 module = 'xep0234',
4520 cdata = #cdata{required = true,
4521 dec = {dec_int, [0, infinity]},
4522 enc = {enc_int, []}},
4523 result = '$cdata'}).
4524
4525 -xml(jingle_ft_range,
4526 #elem{name = <<"range">>,
4527 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4528 module = 'xep0234',
4529 result = {jingle_ft_range, '$offset', '$length', '$hash'},
4530 attrs = [#attr{name = <<"offset">>,
4531 default = 0,
4532 dec = {dec_int, [0, infinity]},
4533 enc = {enc_int, []}},
4534 #attr{name = <<"length">>,
4535 dec = {dec_int, [0, infinity]},
4536 enc = {enc_int, []}}],
4537 refs = [#ref{name = hash, label = '$hash'}]}).
4538
4539 -xml(jingle_ft_file,
4540 #elem{name = <<"file">>,
4541 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4542 module = 'xep0234',
4543 result = {jingle_ft_file, '$date', '$desc', '$hash', '$hash-used',
4544 '$media-type', '$name', '$size', '$range'},
4545 refs = [#ref{name = jingle_ft_date, label = '$date', max = 1},
4546 #ref{name = jingle_ft_desc, label = '$desc'},
4547 #ref{name = hash, label = '$hash'},
4548 #ref{name = hash_used, label = '$hash-used', max = 1},
4549 #ref{name = jingle_ft_media_type, label = '$media-type', max = 1},
4550 #ref{name = jingle_ft_name, label = '$name', max = 1},
4551 #ref{name = jingle_ft_size, label = '$size', max = 1},
4552 #ref{name = jingle_ft_range, label = '$range', max = 1}]}).
4553
4554 -xml(jingle_ft_description,
4555 #elem{name = <<"description">>,
4556 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4557 module = 'xep0234',
4558 result = {jingle_ft_description, '$file'},
4559 refs = [#ref{name = jingle_ft_file, label = '$file',
4560 min = 0, max = 1}]}).
4561
4562 -xml(jingle_ft_received,
4563 #elem{name = <<"received">>,
4564 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4565 module = 'xep0234',
4566 result = {jingle_ft_received, '$creator', '$name'},
4567 attrs = [#attr{name = <<"creator">>,
4568 enc = {enc_enum, []},
4569 dec = {dec_enum, [[initiator, responder]]}},
4570 #attr{name = <<"name">>}]}).
4571
4572 -xml(jingle_ft_checksum,
4573 #elem{name = <<"checksum">>,
4574 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:5">>,
4575 module = 'xep0234',
4576 result = {jingle_ft_checksum, '$creator', '$name', '$file'},
4577 attrs = [#attr{name = <<"creator">>,
4578 enc = {enc_enum, []},
4579 dec = {dec_enum, [[initiator, responder]]}},
4580 #attr{name = <<"name">>}],
4581 refs = [#ref{name = jingle_ft_file, label = '$file',
4582 min = 1, max = 1}]}).
4583
4584 -record(jingle_ft_error, {reason :: 'file-not-available' | 'file-too-large'}).
4585 -type jingle_ft_error() :: #jingle_ft_error{}.
4586
4587 -xml(jingle_ft_error_file_not_available,
4588 #elem{name = <<"file-not-available">>,
4589 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
4590 module = 'xep0234',
4591 result = {jingle_ft_error, 'file-not-available'}}).
4592
4593 -xml(jingle_ft_error_file_too_large,
4594 #elem{name = <<"file-too-large">>,
4595 xmlns = <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
4596 module = 'xep0234',
4597 result = {jingle_ft_error, 'file-too-large'}}).
4598
4599 -xml(jingle_s5b_candidate_used,
4600 #elem{name = <<"candidate-used">>,
4601 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4602 module = 'xep0260',
4603 result = '$cid',
4604 attrs = [#attr{name = <<"cid">>, required = true}]}).
4605
4606 -xml(jingle_s5b_candidate,
4607 #elem{name = <<"candidate">>,
4608 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4609 module = 'xep0260',
4610 result = {jingle_s5b_candidate, '$cid', '$host', '$port',
4611 '$jid', '$type', '$priority'},
4612 attrs = [#attr{name = <<"cid">>, required = true},
4613 #attr{name = <<"host">>, required = true,
4614 enc = {enc_ip, []}, dec = {dec_ip, []}},
4615 #attr{name = <<"jid">>, required = true,
4616 enc = {jid, encode, []},
4617 dec = {jid, decode, []}},
4618 #attr{name = <<"port">>,
4619 enc = {enc_int, []},
4620 dec = {dec_int, [0, 65535]}},
4621 #attr{name = <<"priority">>,
4622 required = true,
4623 enc = {enc_int, []},
4624 dec = {dec_int, [0, infinity]}},
4625 #attr{name = <<"type">>,
4626 default = direct,
4627 enc = {enc_enum, []},
4628 dec = {dec_enum, [[assisted, direct, proxy, tunnel]]}}]}).
4629
4630 -xml(jingle_s5b_activated,
4631 #elem{name = <<"activated">>,
4632 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4633 module = 'xep0260',
4634 result = '$cid',
4635 attrs = [#attr{name = <<"cid">>, required = true}]}).
4636
4637 -xml(jingle_s5b_candidate_error,
4638 #elem{name = <<"candidate-error">>,
4639 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4640 module = 'xep0260',
4641 result = 'candidate-error'}).
4642
4643 -xml(jingle_s5b_proxy_error,
4644 #elem{name = <<"proxy-error">>,
4645 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4646 module = 'xep0260',
4647 result = 'proxy-error'}).
4648
4649 -xml(jingle_s5b_transport,
4650 #elem{name = <<"transport">>,
4651 xmlns = <<"urn:xmpp:jingle:transports:s5b:1">>,
4652 module = 'xep0260',
4653 result = {jingle_s5b_transport, '$sid', '$dstaddr', '$mode',
4654 '$candidates', '$candidate-used', '$activated', '$error'},
4655 attrs = [#attr{name = <<"sid">>, required = true},
4656 #attr{name = <<"dstaddr">>},
4657 #attr{name = <<"mode">>,
4658 default = tcp,
4659 enc = {enc_enum, []},
4660 dec = {dec_enum, [[tcp, udp]]}}],
4661 refs = [#ref{name = jingle_s5b_candidate, label = '$candidates'},
4662 #ref{name = jingle_s5b_candidate_used,
4663 label = '$candidate-used', max = 1},
4664 #ref{name = jingle_s5b_activated,
4665 label = '$activated', max = 1},
4666 #ref{name = jingle_s5b_candidate_error,
4667 label = '$error', max = 1},
4668 #ref{name = jingle_s5b_proxy_error,
4669 label = '$error', max = 1}]}).
4670
4671 -xml(jingle_ibb_transport,
4672 #elem{name = <<"transport">>,
4673 xmlns = <<"urn:xmpp:jingle:transports:ibb:1">>,
4674 module = 'xep0261',
4675 result = {jingle_ibb_transport, '$sid', '$block-size', '$stanza'},
4676 attrs = [#attr{name = <<"sid">>, required = true},
4677 #attr{name = <<"block-size">>, required = true,
4678 enc = {enc_int, []},
4679 dec = {dec_int, [0, infinity]}},
4680 #attr{name = <<"stanza">>,
4681 default = iq,
4682 enc = {enc_enum, []},
4683 dec = {dec_enum, [[iq, message]]}}]}).
4684
42004685 -spec dec_tzo(_) -> {integer(), integer()}.
42014686 dec_tzo(Val) ->
42024687 [H1, M1] = binary:split(Val, <<":">>),
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
3535 {<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>},
3636 {<<"subscription">>, <<"urn:xmpp:mucsub:0">>}].
3737
38 do_encode({muc_subscription, _, _} = Subscription,
39 TopXMLNS) ->
40 encode_muc_subscription(Subscription, TopXMLNS);
3841 do_encode({muc_subscriptions, _} = Subscriptions,
3942 TopXMLNS) ->
4043 encode_muc_subscriptions(Subscriptions, TopXMLNS);
4750
4851 do_get_name({muc_subscribe, _, _, _, _}) ->
4952 <<"subscribe">>;
53 do_get_name({muc_subscription, _, _}) ->
54 <<"subscription">>;
5055 do_get_name({muc_subscriptions, _}) ->
5156 <<"subscriptions">>;
5257 do_get_name({muc_unsubscribe, _, _}) ->
5358 <<"unsubscribe">>.
5459
5560 do_get_ns({muc_subscribe, _, _, _, _}) ->
61 <<"urn:xmpp:mucsub:0">>;
62 do_get_ns({muc_subscription, _, _}) ->
5663 <<"urn:xmpp:mucsub:0">>;
5764 do_get_ns({muc_subscriptions, _}) ->
5865 <<"urn:xmpp:mucsub:0">>;
5966 do_get_ns({muc_unsubscribe, _, _}) ->
6067 <<"urn:xmpp:mucsub:0">>.
6168
69 pp(muc_subscription, 2) -> [jid, events];
6270 pp(muc_subscriptions, 1) -> [list];
6371 pp(muc_subscribe, 4) -> [nick, password, jid, events];
6472 pp(muc_unsubscribe, 2) -> [nick, jid];
6573 pp(_, _) -> no.
6674
6775 records() ->
68 [{muc_subscriptions, 1}, {muc_subscribe, 4},
69 {muc_unsubscribe, 2}].
76 [{muc_subscription, 2}, {muc_subscriptions, 1},
77 {muc_subscribe, 4}, {muc_unsubscribe, 2}].
7078
7179 decode_muc_unsubscribe(__TopXMLNS, __Opts,
7280 {xmlel, <<"unsubscribe">>, _attrs, _els}) ->
340348
341349 decode_muc_subscription(__TopXMLNS, __Opts,
342350 {xmlel, <<"subscription">>, _attrs, _els}) ->
351 Events = decode_muc_subscription_els(__TopXMLNS, __Opts,
352 _els, []),
343353 Jid = decode_muc_subscription_attrs(__TopXMLNS, _attrs,
344354 undefined),
345 Jid.
355 {muc_subscription, Jid, Events}.
356
357 decode_muc_subscription_els(__TopXMLNS, __Opts, [],
358 Events) ->
359 lists:reverse(Events);
360 decode_muc_subscription_els(__TopXMLNS, __Opts,
361 [{xmlel, <<"event">>, _attrs, _} = _el | _els],
362 Events) ->
363 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
364 __TopXMLNS)
365 of
366 <<"urn:xmpp:mucsub:0">> ->
367 decode_muc_subscription_els(__TopXMLNS, __Opts, _els,
368 [decode_muc_subscribe_event(<<"urn:xmpp:mucsub:0">>,
369 __Opts, _el)
370 | Events]);
371 _ ->
372 decode_muc_subscription_els(__TopXMLNS, __Opts, _els,
373 Events)
374 end;
375 decode_muc_subscription_els(__TopXMLNS, __Opts,
376 [_ | _els], Events) ->
377 decode_muc_subscription_els(__TopXMLNS, __Opts, _els,
378 Events).
346379
347380 decode_muc_subscription_attrs(__TopXMLNS,
348381 [{<<"jid">>, _val} | _attrs], _Jid) ->
353386 decode_muc_subscription_attrs(__TopXMLNS, [], Jid) ->
354387 decode_muc_subscription_attr_jid(__TopXMLNS, Jid).
355388
356 encode_muc_subscription(Jid, __TopXMLNS) ->
389 encode_muc_subscription({muc_subscription, Jid, Events},
390 __TopXMLNS) ->
357391 __NewTopXMLNS =
358392 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:mucsub:0">>, [],
359393 __TopXMLNS),
360 _els = [],
394 _els =
395 lists:reverse('encode_muc_subscription_$events'(Events,
396 __NewTopXMLNS, [])),
361397 _attrs = encode_muc_subscription_attr_jid(Jid,
362398 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
363399 __TopXMLNS)),
364400 {xmlel, <<"subscription">>, _attrs, _els}.
401
402 'encode_muc_subscription_$events'([], __TopXMLNS,
403 _acc) ->
404 _acc;
405 'encode_muc_subscription_$events'([Events | _els],
406 __TopXMLNS, _acc) ->
407 'encode_muc_subscription_$events'(_els, __TopXMLNS,
408 [encode_muc_subscribe_event(Events,
409 __TopXMLNS)
410 | _acc]).
365411
366412 decode_muc_subscription_attr_jid(__TopXMLNS,
367413 undefined) ->
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
1515
1616 -export_type([property/0, result/0, form/0]).
1717
18 -dialyzer({nowarn_function, {dec_int, 3}}).
19
1820 dec_int(Val, Min, Max) ->
19 case list_to_integer(binary_to_list(Val)) of
21 case erlang:binary_to_integer(Val) of
2022 Int when Int =< Max, Min == infinity -> Int;
2123 Int when Int =< Max, Int >= Min -> Int
2224 end.
0 %%%-------------------------------------------------------------------
1 %%% @author Stephen Röttger <stephen.roettger@googlemail.com>
2 %%%
3 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
4 %%%
5 %%% Licensed under the Apache License, Version 2.0 (the "License");
6 %%% you may not use this file except in compliance with the License.
7 %%% You may obtain a copy of the License at
8 %%%
9 %%% http://www.apache.org/licenses/LICENSE-2.0
10 %%%
11 %%% Unless required by applicable law or agreed to in writing, software
12 %%% distributed under the License is distributed on an "AS IS" BASIS,
13 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %%% See the License for the specific language governing permissions and
15 %%% limitations under the License.
16 %%%
17 %%%-------------------------------------------------------------------
18 -module(scram).
19 -author('stephen.roettger@googlemail.com').
20
21 %% External exports
22 %% SASLPREP is not implemented, so we use the similar RESOURCEPREP instead
23 -export([salted_password/3, stored_key/1, server_key/1,
24 server_signature/2, client_signature/2, client_key/1,
25 client_key/2]).
26
27 -spec salted_password(binary(), binary(), non_neg_integer()) -> binary().
28
29 salted_password(Password, Salt, IterationCount) ->
30 hi(jid:resourceprep(Password), Salt, IterationCount).
31
32 -spec client_key(binary()) -> binary().
33
34 client_key(SaltedPassword) ->
35 sha_mac(SaltedPassword, <<"Client Key">>).
36
37 -spec stored_key(binary()) -> binary().
38
39 stored_key(ClientKey) -> crypto:hash(sha, ClientKey).
40
41 -spec server_key(binary()) -> binary().
42
43 server_key(SaltedPassword) ->
44 sha_mac(SaltedPassword, <<"Server Key">>).
45
46 -spec client_signature(binary(), binary()) -> binary().
47
48 client_signature(StoredKey, AuthMessage) ->
49 sha_mac(StoredKey, AuthMessage).
50
51 -spec client_key(binary(), binary()) -> binary().
52
53 client_key(ClientProof, ClientSignature) ->
54 crypto:exor(ClientProof, ClientSignature).
55
56 -spec server_signature(binary(), binary()) -> binary().
57
58 server_signature(ServerKey, AuthMessage) ->
59 sha_mac(ServerKey, AuthMessage).
60
61 hi(Password, Salt, IterationCount) ->
62 U1 = sha_mac(Password, <<Salt/binary, 0, 0, 0, 1>>),
63 crypto:exor(U1, hi_round(Password, U1, IterationCount - 1)).
64
65 hi_round(Password, UPrev, 1) ->
66 sha_mac(Password, UPrev);
67 hi_round(Password, UPrev, IterationCount) ->
68 U = sha_mac(Password, UPrev),
69 crypto:exor(U, hi_round(Password, U, IterationCount - 1)).
70
71 sha_mac(Key, Data) ->
72 crypto:hmac(sha, Key, Data).
120120 dec_int(Val, infinity, infinity).
121121
122122 dec_int(Val, Min, Max) ->
123 case list_to_integer(binary_to_list(Val)) of
123 case erlang:binary_to_integer(Val) of
124124 Int when Int =< Max, Min == infinity ->
125125 Int;
126126 Int when Int =< Max, Int >= Min ->
292292 emit("-export([decode/1, decode/2, format_error/1, io_format_error/1]).~n"),
293293 emit("-include(\"xmpp_codec.hrl\").~n"),
294294 emit("-include(\"~s\").~n", [Include]),
295 emit("-export_type([property/0, result/0, form/0]).~n").
295 emit("-export_type([property/0, result/0, form/0]).~n"),
296 case lists:any(
297 fun({_, {dec_int, _}}) -> true;
298 (_) -> false
299 end, State#state.dec_mfas) of
300 true ->
301 emit("-dialyzer({nowarn_function, dec_int/3}).~n");
302 false ->
303 ok
304 end.
296305
297306 mk_type_definitions(Fs, State) ->
298307 mk_comment_header(State),
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0047).
4
5 -compile(export_all).
6
7 do_decode(<<"close">>,
8 <<"http://jabber.org/protocol/ibb">>, El, Opts) ->
9 decode_ibb_close(<<"http://jabber.org/protocol/ibb">>,
10 Opts, El);
11 do_decode(<<"data">>,
12 <<"http://jabber.org/protocol/ibb">>, El, Opts) ->
13 decode_ibb_data(<<"http://jabber.org/protocol/ibb">>,
14 Opts, El);
15 do_decode(<<"open">>,
16 <<"http://jabber.org/protocol/ibb">>, El, Opts) ->
17 decode_ibb_open(<<"http://jabber.org/protocol/ibb">>,
18 Opts, El);
19 do_decode(Name, <<>>, _, _) ->
20 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
21 do_decode(Name, XMLNS, _, _) ->
22 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
23
24 tags() ->
25 [{<<"close">>, <<"http://jabber.org/protocol/ibb">>},
26 {<<"data">>, <<"http://jabber.org/protocol/ibb">>},
27 {<<"open">>, <<"http://jabber.org/protocol/ibb">>}].
28
29 do_encode({ibb_open, _, _, _} = Open, TopXMLNS) ->
30 encode_ibb_open(Open, TopXMLNS);
31 do_encode({ibb_data, _, _, _} = Data, TopXMLNS) ->
32 encode_ibb_data(Data, TopXMLNS);
33 do_encode({ibb_close, _} = Close, TopXMLNS) ->
34 encode_ibb_close(Close, TopXMLNS).
35
36 do_get_name({ibb_close, _}) -> <<"close">>;
37 do_get_name({ibb_data, _, _, _}) -> <<"data">>;
38 do_get_name({ibb_open, _, _, _}) -> <<"open">>.
39
40 do_get_ns({ibb_close, _}) ->
41 <<"http://jabber.org/protocol/ibb">>;
42 do_get_ns({ibb_data, _, _, _}) ->
43 <<"http://jabber.org/protocol/ibb">>;
44 do_get_ns({ibb_open, _, _, _}) ->
45 <<"http://jabber.org/protocol/ibb">>.
46
47 pp(ibb_open, 3) -> [sid, 'block-size', stanza];
48 pp(ibb_data, 3) -> [sid, seq, data];
49 pp(ibb_close, 1) -> [sid];
50 pp(_, _) -> no.
51
52 records() ->
53 [{ibb_open, 3}, {ibb_data, 3}, {ibb_close, 1}].
54
55 dec_enum(Val, Enums) ->
56 AtomVal = erlang:binary_to_existing_atom(Val, utf8),
57 case lists:member(AtomVal, Enums) of
58 true -> AtomVal
59 end.
60
61 dec_int(Val, Min, Max) ->
62 case erlang:binary_to_integer(Val) of
63 Int when Int =< Max, Min == infinity -> Int;
64 Int when Int =< Max, Int >= Min -> Int
65 end.
66
67 enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
68
69 enc_int(Int) -> erlang:integer_to_binary(Int).
70
71 decode_ibb_close(__TopXMLNS, __Opts,
72 {xmlel, <<"close">>, _attrs, _els}) ->
73 Sid = decode_ibb_close_attrs(__TopXMLNS, _attrs,
74 undefined),
75 {ibb_close, Sid}.
76
77 decode_ibb_close_attrs(__TopXMLNS,
78 [{<<"sid">>, _val} | _attrs], _Sid) ->
79 decode_ibb_close_attrs(__TopXMLNS, _attrs, _val);
80 decode_ibb_close_attrs(__TopXMLNS, [_ | _attrs], Sid) ->
81 decode_ibb_close_attrs(__TopXMLNS, _attrs, Sid);
82 decode_ibb_close_attrs(__TopXMLNS, [], Sid) ->
83 decode_ibb_close_attr_sid(__TopXMLNS, Sid).
84
85 encode_ibb_close({ibb_close, Sid}, __TopXMLNS) ->
86 __NewTopXMLNS =
87 xmpp_codec:choose_top_xmlns(<<"http://jabber.org/protocol/ibb">>,
88 [], __TopXMLNS),
89 _els = [],
90 _attrs = encode_ibb_close_attr_sid(Sid,
91 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
92 __TopXMLNS)),
93 {xmlel, <<"close">>, _attrs, _els}.
94
95 decode_ibb_close_attr_sid(__TopXMLNS, undefined) ->
96 erlang:error({xmpp_codec,
97 {missing_attr, <<"sid">>, <<"close">>, __TopXMLNS}});
98 decode_ibb_close_attr_sid(__TopXMLNS, _val) -> _val.
99
100 encode_ibb_close_attr_sid(_val, _acc) ->
101 [{<<"sid">>, _val} | _acc].
102
103 decode_ibb_data(__TopXMLNS, __Opts,
104 {xmlel, <<"data">>, _attrs, _els}) ->
105 Data = decode_ibb_data_els(__TopXMLNS, __Opts, _els,
106 <<>>),
107 {Sid, Seq} = decode_ibb_data_attrs(__TopXMLNS, _attrs,
108 undefined, undefined),
109 {ibb_data, Sid, Seq, Data}.
110
111 decode_ibb_data_els(__TopXMLNS, __Opts, [], Data) ->
112 decode_ibb_data_cdata(__TopXMLNS, Data);
113 decode_ibb_data_els(__TopXMLNS, __Opts,
114 [{xmlcdata, _data} | _els], Data) ->
115 decode_ibb_data_els(__TopXMLNS, __Opts, _els,
116 <<Data/binary, _data/binary>>);
117 decode_ibb_data_els(__TopXMLNS, __Opts, [_ | _els],
118 Data) ->
119 decode_ibb_data_els(__TopXMLNS, __Opts, _els, Data).
120
121 decode_ibb_data_attrs(__TopXMLNS,
122 [{<<"sid">>, _val} | _attrs], _Sid, Seq) ->
123 decode_ibb_data_attrs(__TopXMLNS, _attrs, _val, Seq);
124 decode_ibb_data_attrs(__TopXMLNS,
125 [{<<"seq">>, _val} | _attrs], Sid, _Seq) ->
126 decode_ibb_data_attrs(__TopXMLNS, _attrs, Sid, _val);
127 decode_ibb_data_attrs(__TopXMLNS, [_ | _attrs], Sid,
128 Seq) ->
129 decode_ibb_data_attrs(__TopXMLNS, _attrs, Sid, Seq);
130 decode_ibb_data_attrs(__TopXMLNS, [], Sid, Seq) ->
131 {decode_ibb_data_attr_sid(__TopXMLNS, Sid),
132 decode_ibb_data_attr_seq(__TopXMLNS, Seq)}.
133
134 encode_ibb_data({ibb_data, Sid, Seq, Data},
135 __TopXMLNS) ->
136 __NewTopXMLNS =
137 xmpp_codec:choose_top_xmlns(<<"http://jabber.org/protocol/ibb">>,
138 [], __TopXMLNS),
139 _els = encode_ibb_data_cdata(Data, []),
140 _attrs = encode_ibb_data_attr_seq(Seq,
141 encode_ibb_data_attr_sid(Sid,
142 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
143 __TopXMLNS))),
144 {xmlel, <<"data">>, _attrs, _els}.
145
146 decode_ibb_data_attr_sid(__TopXMLNS, undefined) ->
147 erlang:error({xmpp_codec,
148 {missing_attr, <<"sid">>, <<"data">>, __TopXMLNS}});
149 decode_ibb_data_attr_sid(__TopXMLNS, _val) -> _val.
150
151 encode_ibb_data_attr_sid(_val, _acc) ->
152 [{<<"sid">>, _val} | _acc].
153
154 decode_ibb_data_attr_seq(__TopXMLNS, undefined) ->
155 erlang:error({xmpp_codec,
156 {missing_attr, <<"seq">>, <<"data">>, __TopXMLNS}});
157 decode_ibb_data_attr_seq(__TopXMLNS, _val) ->
158 case catch dec_int(_val, 0, infinity) of
159 {'EXIT', _} ->
160 erlang:error({xmpp_codec,
161 {bad_attr_value, <<"seq">>, <<"data">>, __TopXMLNS}});
162 _res -> _res
163 end.
164
165 encode_ibb_data_attr_seq(_val, _acc) ->
166 [{<<"seq">>, enc_int(_val)} | _acc].
167
168 decode_ibb_data_cdata(__TopXMLNS, <<>>) -> <<>>;
169 decode_ibb_data_cdata(__TopXMLNS, _val) ->
170 case catch base64:decode(_val) of
171 {'EXIT', _} ->
172 erlang:error({xmpp_codec,
173 {bad_cdata_value, <<>>, <<"data">>, __TopXMLNS}});
174 _res -> _res
175 end.
176
177 encode_ibb_data_cdata(<<>>, _acc) -> _acc;
178 encode_ibb_data_cdata(_val, _acc) ->
179 [{xmlcdata, base64:encode(_val)} | _acc].
180
181 decode_ibb_open(__TopXMLNS, __Opts,
182 {xmlel, <<"open">>, _attrs, _els}) ->
183 {Sid, Block_size, Stanza} =
184 decode_ibb_open_attrs(__TopXMLNS, _attrs, undefined,
185 undefined, undefined),
186 {ibb_open, Sid, Block_size, Stanza}.
187
188 decode_ibb_open_attrs(__TopXMLNS,
189 [{<<"sid">>, _val} | _attrs], _Sid, Block_size,
190 Stanza) ->
191 decode_ibb_open_attrs(__TopXMLNS, _attrs, _val,
192 Block_size, Stanza);
193 decode_ibb_open_attrs(__TopXMLNS,
194 [{<<"block-size">>, _val} | _attrs], Sid, _Block_size,
195 Stanza) ->
196 decode_ibb_open_attrs(__TopXMLNS, _attrs, Sid, _val,
197 Stanza);
198 decode_ibb_open_attrs(__TopXMLNS,
199 [{<<"stanza">>, _val} | _attrs], Sid, Block_size,
200 _Stanza) ->
201 decode_ibb_open_attrs(__TopXMLNS, _attrs, Sid,
202 Block_size, _val);
203 decode_ibb_open_attrs(__TopXMLNS, [_ | _attrs], Sid,
204 Block_size, Stanza) ->
205 decode_ibb_open_attrs(__TopXMLNS, _attrs, Sid,
206 Block_size, Stanza);
207 decode_ibb_open_attrs(__TopXMLNS, [], Sid, Block_size,
208 Stanza) ->
209 {decode_ibb_open_attr_sid(__TopXMLNS, Sid),
210 'decode_ibb_open_attr_block-size'(__TopXMLNS,
211 Block_size),
212 decode_ibb_open_attr_stanza(__TopXMLNS, Stanza)}.
213
214 encode_ibb_open({ibb_open, Sid, Block_size, Stanza},
215 __TopXMLNS) ->
216 __NewTopXMLNS =
217 xmpp_codec:choose_top_xmlns(<<"http://jabber.org/protocol/ibb">>,
218 [], __TopXMLNS),
219 _els = [],
220 _attrs = encode_ibb_open_attr_stanza(Stanza,
221 'encode_ibb_open_attr_block-size'(Block_size,
222 encode_ibb_open_attr_sid(Sid,
223 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
224 __TopXMLNS)))),
225 {xmlel, <<"open">>, _attrs, _els}.
226
227 decode_ibb_open_attr_sid(__TopXMLNS, undefined) ->
228 erlang:error({xmpp_codec,
229 {missing_attr, <<"sid">>, <<"open">>, __TopXMLNS}});
230 decode_ibb_open_attr_sid(__TopXMLNS, _val) -> _val.
231
232 encode_ibb_open_attr_sid(_val, _acc) ->
233 [{<<"sid">>, _val} | _acc].
234
235 'decode_ibb_open_attr_block-size'(__TopXMLNS,
236 undefined) ->
237 erlang:error({xmpp_codec,
238 {missing_attr, <<"block-size">>, <<"open">>,
239 __TopXMLNS}});
240 'decode_ibb_open_attr_block-size'(__TopXMLNS, _val) ->
241 case catch dec_int(_val, 0, infinity) of
242 {'EXIT', _} ->
243 erlang:error({xmpp_codec,
244 {bad_attr_value, <<"block-size">>, <<"open">>,
245 __TopXMLNS}});
246 _res -> _res
247 end.
248
249 'encode_ibb_open_attr_block-size'(_val, _acc) ->
250 [{<<"block-size">>, enc_int(_val)} | _acc].
251
252 decode_ibb_open_attr_stanza(__TopXMLNS, undefined) ->
253 iq;
254 decode_ibb_open_attr_stanza(__TopXMLNS, _val) ->
255 case catch dec_enum(_val, [iq, message]) of
256 {'EXIT', _} ->
257 erlang:error({xmpp_codec,
258 {bad_attr_value, <<"stanza">>, <<"open">>,
259 __TopXMLNS}});
260 _res -> _res
261 end.
262
263 encode_ibb_open_attr_stanza(iq, _acc) -> _acc;
264 encode_ibb_open_attr_stanza(_val, _acc) ->
265 [{<<"stanza">>, enc_enum(_val)} | _acc].
26862686 [{<<"subid">>, _val} | _acc].
26872687
26882688 decode_pubsub_options_attr_jid(__TopXMLNS, undefined) ->
2689 erlang:error({xmpp_codec,
2690 {missing_attr, <<"jid">>, <<"options">>, __TopXMLNS}});
2689 undefined;
26912690 decode_pubsub_options_attr_jid(__TopXMLNS, _val) ->
26922691 case catch jid:decode(_val) of
26932692 {'EXIT', _} ->
26972696 _res -> _res
26982697 end.
26992698
2699 encode_pubsub_options_attr_jid(undefined, _acc) -> _acc;
27002700 encode_pubsub_options_attr_jid(_val, _acc) ->
27012701 [{<<"jid">>, jid:encode(_val)} | _acc].
27022702
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0166).
4
5 -compile(export_all).
6
7 do_decode(<<"jingle">>, <<"urn:xmpp:jingle:1">>, El,
8 Opts) ->
9 decode_jingle(<<"urn:xmpp:jingle:1">>, Opts, El);
10 do_decode(<<"content">>, <<"urn:xmpp:jingle:1">>, El,
11 Opts) ->
12 decode_jingle_content(<<"urn:xmpp:jingle:1">>, Opts,
13 El);
14 do_decode(<<"reason">>, <<"urn:xmpp:jingle:1">>, El,
15 Opts) ->
16 decode_jingle_reason(<<"urn:xmpp:jingle:1">>, Opts, El);
17 do_decode(<<"text">>, <<"urn:xmpp:jingle:1">>, El,
18 Opts) ->
19 decode_jingle_reason_text(<<"urn:xmpp:jingle:1">>, Opts,
20 El);
21 do_decode(<<"unsupported-transports">>,
22 <<"urn:xmpp:jingle:1">>, El, Opts) ->
23 decode_jingle_reason_unsupported_transports(<<"urn:xmpp:jingle:1">>,
24 Opts, El);
25 do_decode(<<"unsupported-applications">>,
26 <<"urn:xmpp:jingle:1">>, El, Opts) ->
27 decode_jingle_reason_unsupported_applications(<<"urn:xmpp:jingle:1">>,
28 Opts, El);
29 do_decode(<<"timeout">>, <<"urn:xmpp:jingle:1">>, El,
30 Opts) ->
31 decode_jingle_reason_timeout(<<"urn:xmpp:jingle:1">>,
32 Opts, El);
33 do_decode(<<"success">>, <<"urn:xmpp:jingle:1">>, El,
34 Opts) ->
35 decode_jingle_reason_success(<<"urn:xmpp:jingle:1">>,
36 Opts, El);
37 do_decode(<<"security-error">>, <<"urn:xmpp:jingle:1">>,
38 El, Opts) ->
39 decode_jingle_reason_security_error(<<"urn:xmpp:jingle:1">>,
40 Opts, El);
41 do_decode(<<"media-error">>, <<"urn:xmpp:jingle:1">>,
42 El, Opts) ->
43 decode_jingle_reason_media_error(<<"urn:xmpp:jingle:1">>,
44 Opts, El);
45 do_decode(<<"incompatible-parameters">>,
46 <<"urn:xmpp:jingle:1">>, El, Opts) ->
47 decode_jingle_reason_incompatible_parameters(<<"urn:xmpp:jingle:1">>,
48 Opts, El);
49 do_decode(<<"gone">>, <<"urn:xmpp:jingle:1">>, El,
50 Opts) ->
51 decode_jingle_reason_gone(<<"urn:xmpp:jingle:1">>, Opts,
52 El);
53 do_decode(<<"general-error">>, <<"urn:xmpp:jingle:1">>,
54 El, Opts) ->
55 decode_jingle_reason_general_error(<<"urn:xmpp:jingle:1">>,
56 Opts, El);
57 do_decode(<<"failed-transport">>,
58 <<"urn:xmpp:jingle:1">>, El, Opts) ->
59 decode_jingle_reason_failed_transport(<<"urn:xmpp:jingle:1">>,
60 Opts, El);
61 do_decode(<<"failed-application">>,
62 <<"urn:xmpp:jingle:1">>, El, Opts) ->
63 decode_jingle_reason_failed_application(<<"urn:xmpp:jingle:1">>,
64 Opts, El);
65 do_decode(<<"expired">>, <<"urn:xmpp:jingle:1">>, El,
66 Opts) ->
67 decode_jingle_reason_expired(<<"urn:xmpp:jingle:1">>,
68 Opts, El);
69 do_decode(<<"decline">>, <<"urn:xmpp:jingle:1">>, El,
70 Opts) ->
71 decode_jingle_reason_decline(<<"urn:xmpp:jingle:1">>,
72 Opts, El);
73 do_decode(<<"connectivity-error">>,
74 <<"urn:xmpp:jingle:1">>, El, Opts) ->
75 decode_jingle_reason_connectivity_error(<<"urn:xmpp:jingle:1">>,
76 Opts, El);
77 do_decode(<<"cancel">>, <<"urn:xmpp:jingle:1">>, El,
78 Opts) ->
79 decode_jingle_reason_cancel(<<"urn:xmpp:jingle:1">>,
80 Opts, El);
81 do_decode(<<"busy">>, <<"urn:xmpp:jingle:1">>, El,
82 Opts) ->
83 decode_jingle_reason_busy(<<"urn:xmpp:jingle:1">>, Opts,
84 El);
85 do_decode(<<"alternative-session">>,
86 <<"urn:xmpp:jingle:1">>, El, Opts) ->
87 decode_jingle_reason_alternative_session(<<"urn:xmpp:jingle:1">>,
88 Opts, El);
89 do_decode(<<"security-required">>,
90 <<"urn:xmpp:jingle:errors:1">>, El, Opts) ->
91 decode_jingle_error_security_required(<<"urn:xmpp:jingle:errors:1">>,
92 Opts, El);
93 do_decode(<<"unsupported-info">>,
94 <<"urn:xmpp:jingle:errors:1">>, El, Opts) ->
95 decode_jingle_error_unsupported_info(<<"urn:xmpp:jingle:errors:1">>,
96 Opts, El);
97 do_decode(<<"unknown-session">>,
98 <<"urn:xmpp:jingle:errors:1">>, El, Opts) ->
99 decode_jingle_error_unknown_session(<<"urn:xmpp:jingle:errors:1">>,
100 Opts, El);
101 do_decode(<<"tie-break">>,
102 <<"urn:xmpp:jingle:errors:1">>, El, Opts) ->
103 decode_jingle_error_tie_break(<<"urn:xmpp:jingle:errors:1">>,
104 Opts, El);
105 do_decode(<<"out-of-order">>,
106 <<"urn:xmpp:jingle:errors:1">>, El, Opts) ->
107 decode_jingle_error_out_of_order(<<"urn:xmpp:jingle:errors:1">>,
108 Opts, El);
109 do_decode(Name, <<>>, _, _) ->
110 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
111 do_decode(Name, XMLNS, _, _) ->
112 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
113
114 tags() ->
115 [{<<"jingle">>, <<"urn:xmpp:jingle:1">>},
116 {<<"content">>, <<"urn:xmpp:jingle:1">>},
117 {<<"reason">>, <<"urn:xmpp:jingle:1">>},
118 {<<"text">>, <<"urn:xmpp:jingle:1">>},
119 {<<"unsupported-transports">>, <<"urn:xmpp:jingle:1">>},
120 {<<"unsupported-applications">>,
121 <<"urn:xmpp:jingle:1">>},
122 {<<"timeout">>, <<"urn:xmpp:jingle:1">>},
123 {<<"success">>, <<"urn:xmpp:jingle:1">>},
124 {<<"security-error">>, <<"urn:xmpp:jingle:1">>},
125 {<<"media-error">>, <<"urn:xmpp:jingle:1">>},
126 {<<"incompatible-parameters">>,
127 <<"urn:xmpp:jingle:1">>},
128 {<<"gone">>, <<"urn:xmpp:jingle:1">>},
129 {<<"general-error">>, <<"urn:xmpp:jingle:1">>},
130 {<<"failed-transport">>, <<"urn:xmpp:jingle:1">>},
131 {<<"failed-application">>, <<"urn:xmpp:jingle:1">>},
132 {<<"expired">>, <<"urn:xmpp:jingle:1">>},
133 {<<"decline">>, <<"urn:xmpp:jingle:1">>},
134 {<<"connectivity-error">>, <<"urn:xmpp:jingle:1">>},
135 {<<"cancel">>, <<"urn:xmpp:jingle:1">>},
136 {<<"busy">>, <<"urn:xmpp:jingle:1">>},
137 {<<"alternative-session">>, <<"urn:xmpp:jingle:1">>},
138 {<<"security-required">>,
139 <<"urn:xmpp:jingle:errors:1">>},
140 {<<"unsupported-info">>,
141 <<"urn:xmpp:jingle:errors:1">>},
142 {<<"unknown-session">>, <<"urn:xmpp:jingle:errors:1">>},
143 {<<"tie-break">>, <<"urn:xmpp:jingle:errors:1">>},
144 {<<"out-of-order">>, <<"urn:xmpp:jingle:errors:1">>}].
145
146 do_encode({jingle_error, 'out-of-order'} = Out_of_order,
147 TopXMLNS) ->
148 encode_jingle_error_out_of_order(Out_of_order,
149 TopXMLNS);
150 do_encode({jingle_error, 'tie-break'} = Tie_break,
151 TopXMLNS) ->
152 encode_jingle_error_tie_break(Tie_break, TopXMLNS);
153 do_encode({jingle_error, 'unknown-session'} =
154 Unknown_session,
155 TopXMLNS) ->
156 encode_jingle_error_unknown_session(Unknown_session,
157 TopXMLNS);
158 do_encode({jingle_error, 'unsupported-info'} =
159 Unsupported_info,
160 TopXMLNS) ->
161 encode_jingle_error_unsupported_info(Unsupported_info,
162 TopXMLNS);
163 do_encode({jingle_error, 'security-required'} =
164 Security_required,
165 TopXMLNS) ->
166 encode_jingle_error_security_required(Security_required,
167 TopXMLNS);
168 do_encode({text, _, _} = Text, TopXMLNS) ->
169 encode_jingle_reason_text(Text, TopXMLNS);
170 do_encode({jingle_reason, _, _, _} = Reason,
171 TopXMLNS) ->
172 encode_jingle_reason(Reason, TopXMLNS);
173 do_encode({jingle_content, _, _, _, _, _} = Content,
174 TopXMLNS) ->
175 encode_jingle_content(Content, TopXMLNS);
176 do_encode({jingle, _, _, _, _, _, _, _} = Jingle,
177 TopXMLNS) ->
178 encode_jingle(Jingle, TopXMLNS).
179
180 do_get_name({jingle, _, _, _, _, _, _, _}) ->
181 <<"jingle">>;
182 do_get_name({jingle_content, _, _, _, _, _}) ->
183 <<"content">>;
184 do_get_name({jingle_error, 'out-of-order'}) ->
185 <<"out-of-order">>;
186 do_get_name({jingle_error, 'security-required'}) ->
187 <<"security-required">>;
188 do_get_name({jingle_error, 'tie-break'}) ->
189 <<"tie-break">>;
190 do_get_name({jingle_error, 'unknown-session'}) ->
191 <<"unknown-session">>;
192 do_get_name({jingle_error, 'unsupported-info'}) ->
193 <<"unsupported-info">>;
194 do_get_name({jingle_reason, _, _, _}) -> <<"reason">>;
195 do_get_name({text, _, _}) -> <<"text">>.
196
197 do_get_ns({jingle, _, _, _, _, _, _, _}) ->
198 <<"urn:xmpp:jingle:1">>;
199 do_get_ns({jingle_content, _, _, _, _, _}) ->
200 <<"urn:xmpp:jingle:1">>;
201 do_get_ns({jingle_error, 'out-of-order'}) ->
202 <<"urn:xmpp:jingle:errors:1">>;
203 do_get_ns({jingle_error, 'security-required'}) ->
204 <<"urn:xmpp:jingle:errors:1">>;
205 do_get_ns({jingle_error, 'tie-break'}) ->
206 <<"urn:xmpp:jingle:errors:1">>;
207 do_get_ns({jingle_error, 'unknown-session'}) ->
208 <<"urn:xmpp:jingle:errors:1">>;
209 do_get_ns({jingle_error, 'unsupported-info'}) ->
210 <<"urn:xmpp:jingle:errors:1">>;
211 do_get_ns({jingle_reason, _, _, _}) ->
212 <<"urn:xmpp:jingle:1">>;
213 do_get_ns({text, _, _}) -> <<"urn:xmpp:jingle:1">>.
214
215 get_els({jingle_reason, _reason, _text, _sub_els}) ->
216 _sub_els;
217 get_els({jingle_content, _creator, _disposition, _name,
218 _senders, _sub_els}) ->
219 _sub_els;
220 get_els({jingle, _action, _sid, _initiator, _responder,
221 _content, _reason, _sub_els}) ->
222 _sub_els.
223
224 set_els({jingle_reason, _reason, _text, _}, _sub_els) ->
225 {jingle_reason, _reason, _text, _sub_els};
226 set_els({jingle_content, _creator, _disposition, _name,
227 _senders, _},
228 _sub_els) ->
229 {jingle_content, _creator, _disposition, _name,
230 _senders, _sub_els};
231 set_els({jingle, _action, _sid, _initiator, _responder,
232 _content, _reason, _},
233 _sub_els) ->
234 {jingle, _action, _sid, _initiator, _responder,
235 _content, _reason, _sub_els}.
236
237 pp(jingle_error, 1) -> [reason];
238 pp(text, 2) -> [lang, data];
239 pp(jingle_reason, 3) -> [reason, text, sub_els];
240 pp(jingle_content, 5) ->
241 [creator, disposition, name, senders, sub_els];
242 pp(jingle, 7) ->
243 [action, sid, initiator, responder, content, reason,
244 sub_els];
245 pp(_, _) -> no.
246
247 records() ->
248 [{jingle_error, 1}, {text, 2}, {jingle_reason, 3},
249 {jingle_content, 5}, {jingle, 7}].
250
251 dec_enum(Val, Enums) ->
252 AtomVal = erlang:binary_to_existing_atom(Val, utf8),
253 case lists:member(AtomVal, Enums) of
254 true -> AtomVal
255 end.
256
257 enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
258
259 decode_jingle(__TopXMLNS, __Opts,
260 {xmlel, <<"jingle">>, _attrs, _els}) ->
261 {Content, Reason, __Els} = decode_jingle_els(__TopXMLNS,
262 __Opts, _els, [], undefined,
263 []),
264 {Action, Sid, Initiator, Responder} =
265 decode_jingle_attrs(__TopXMLNS, _attrs, undefined,
266 undefined, undefined, undefined),
267 {jingle, Action, Sid, Initiator, Responder, Content,
268 Reason, __Els}.
269
270 decode_jingle_els(__TopXMLNS, __Opts, [], Content,
271 Reason, __Els) ->
272 {lists:reverse(Content), Reason, lists:reverse(__Els)};
273 decode_jingle_els(__TopXMLNS, __Opts,
274 [{xmlel, <<"content">>, _attrs, _} = _el | _els],
275 Content, Reason, __Els) ->
276 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
277 __TopXMLNS)
278 of
279 <<"urn:xmpp:jingle:1">> ->
280 decode_jingle_els(__TopXMLNS, __Opts, _els,
281 [decode_jingle_content(<<"urn:xmpp:jingle:1">>,
282 __Opts, _el)
283 | Content],
284 Reason, __Els);
285 _ ->
286 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
287 Reason, [_el | __Els])
288 end;
289 decode_jingle_els(__TopXMLNS, __Opts,
290 [{xmlel, <<"reason">>, _attrs, _} = _el | _els],
291 Content, Reason, __Els) ->
292 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
293 __TopXMLNS)
294 of
295 <<"urn:xmpp:jingle:1">> ->
296 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
297 decode_jingle_reason(<<"urn:xmpp:jingle:1">>,
298 __Opts, _el),
299 __Els);
300 _ ->
301 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
302 Reason, [_el | __Els])
303 end;
304 decode_jingle_els(__TopXMLNS, __Opts,
305 [{xmlel, _name, _attrs, _} = _el | _els], Content,
306 Reason, __Els) ->
307 case proplists:get_bool(ignore_els, __Opts) of
308 true ->
309 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
310 Reason, [_el | __Els]);
311 false ->
312 __XMLNS = xmpp_codec:get_attr(<<"xmlns">>, _attrs,
313 __TopXMLNS),
314 case xmpp_codec:get_mod(_name, __XMLNS) of
315 undefined ->
316 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
317 Reason, [_el | __Els]);
318 Mod ->
319 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
320 Reason,
321 [Mod:do_decode(_name, __XMLNS, _el, __Opts)
322 | __Els])
323 end
324 end;
325 decode_jingle_els(__TopXMLNS, __Opts, [_ | _els],
326 Content, Reason, __Els) ->
327 decode_jingle_els(__TopXMLNS, __Opts, _els, Content,
328 Reason, __Els).
329
330 decode_jingle_attrs(__TopXMLNS,
331 [{<<"action">>, _val} | _attrs], _Action, Sid,
332 Initiator, Responder) ->
333 decode_jingle_attrs(__TopXMLNS, _attrs, _val, Sid,
334 Initiator, Responder);
335 decode_jingle_attrs(__TopXMLNS,
336 [{<<"sid">>, _val} | _attrs], Action, _Sid, Initiator,
337 Responder) ->
338 decode_jingle_attrs(__TopXMLNS, _attrs, Action, _val,
339 Initiator, Responder);
340 decode_jingle_attrs(__TopXMLNS,
341 [{<<"initiator">>, _val} | _attrs], Action, Sid,
342 _Initiator, Responder) ->
343 decode_jingle_attrs(__TopXMLNS, _attrs, Action, Sid,
344 _val, Responder);
345 decode_jingle_attrs(__TopXMLNS,
346 [{<<"responder">>, _val} | _attrs], Action, Sid,
347 Initiator, _Responder) ->
348 decode_jingle_attrs(__TopXMLNS, _attrs, Action, Sid,
349 Initiator, _val);
350 decode_jingle_attrs(__TopXMLNS, [_ | _attrs], Action,
351 Sid, Initiator, Responder) ->
352 decode_jingle_attrs(__TopXMLNS, _attrs, Action, Sid,
353 Initiator, Responder);
354 decode_jingle_attrs(__TopXMLNS, [], Action, Sid,
355 Initiator, Responder) ->
356 {decode_jingle_attr_action(__TopXMLNS, Action),
357 decode_jingle_attr_sid(__TopXMLNS, Sid),
358 decode_jingle_attr_initiator(__TopXMLNS, Initiator),
359 decode_jingle_attr_responder(__TopXMLNS, Responder)}.
360
361 encode_jingle({jingle, Action, Sid, Initiator,
362 Responder, Content, Reason, __Els},
363 __TopXMLNS) ->
364 __NewTopXMLNS =
365 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
366 __TopXMLNS),
367 _els = [xmpp_codec:encode(_el, __NewTopXMLNS)
368 || _el <- __Els]
369 ++
370 lists:reverse('encode_jingle_$content'(Content,
371 __NewTopXMLNS,
372 'encode_jingle_$reason'(Reason,
373 __NewTopXMLNS,
374 []))),
375 _attrs = encode_jingle_attr_responder(Responder,
376 encode_jingle_attr_initiator(Initiator,
377 encode_jingle_attr_sid(Sid,
378 encode_jingle_attr_action(Action,
379 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
380 __TopXMLNS))))),
381 {xmlel, <<"jingle">>, _attrs, _els}.
382
383 'encode_jingle_$content'([], __TopXMLNS, _acc) -> _acc;
384 'encode_jingle_$content'([Content | _els], __TopXMLNS,
385 _acc) ->
386 'encode_jingle_$content'(_els, __TopXMLNS,
387 [encode_jingle_content(Content, __TopXMLNS)
388 | _acc]).
389
390 'encode_jingle_$reason'(undefined, __TopXMLNS, _acc) ->
391 _acc;
392 'encode_jingle_$reason'(Reason, __TopXMLNS, _acc) ->
393 [encode_jingle_reason(Reason, __TopXMLNS) | _acc].
394
395 decode_jingle_attr_action(__TopXMLNS, undefined) ->
396 erlang:error({xmpp_codec,
397 {missing_attr, <<"action">>, <<"jingle">>,
398 __TopXMLNS}});
399 decode_jingle_attr_action(__TopXMLNS, _val) ->
400 case catch dec_enum(_val,
401 ['content-accept', 'content-add', 'content-modify',
402 'content-reject', 'content-remove', 'description-info',
403 'security-info', 'session-accept', 'session-info',
404 'session-initiate', 'session-terminate',
405 'transport-accept', 'transport-info',
406 'transport-reject', 'transport-replace'])
407 of
408 {'EXIT', _} ->
409 erlang:error({xmpp_codec,
410 {bad_attr_value, <<"action">>, <<"jingle">>,
411 __TopXMLNS}});
412 _res -> _res
413 end.
414
415 encode_jingle_attr_action(_val, _acc) ->
416 [{<<"action">>, enc_enum(_val)} | _acc].
417
418 decode_jingle_attr_sid(__TopXMLNS, undefined) ->
419 erlang:error({xmpp_codec,
420 {missing_attr, <<"sid">>, <<"jingle">>, __TopXMLNS}});
421 decode_jingle_attr_sid(__TopXMLNS, _val) -> _val.
422
423 encode_jingle_attr_sid(_val, _acc) ->
424 [{<<"sid">>, _val} | _acc].
425
426 decode_jingle_attr_initiator(__TopXMLNS, undefined) ->
427 undefined;
428 decode_jingle_attr_initiator(__TopXMLNS, _val) ->
429 case catch jid:decode(_val) of
430 {'EXIT', _} ->
431 erlang:error({xmpp_codec,
432 {bad_attr_value, <<"initiator">>, <<"jingle">>,
433 __TopXMLNS}});
434 _res -> _res
435 end.
436
437 encode_jingle_attr_initiator(undefined, _acc) -> _acc;
438 encode_jingle_attr_initiator(_val, _acc) ->
439 [{<<"initiator">>, jid:encode(_val)} | _acc].
440
441 decode_jingle_attr_responder(__TopXMLNS, undefined) ->
442 undefined;
443 decode_jingle_attr_responder(__TopXMLNS, _val) ->
444 case catch jid:decode(_val) of
445 {'EXIT', _} ->
446 erlang:error({xmpp_codec,
447 {bad_attr_value, <<"responder">>, <<"jingle">>,
448 __TopXMLNS}});
449 _res -> _res
450 end.
451
452 encode_jingle_attr_responder(undefined, _acc) -> _acc;
453 encode_jingle_attr_responder(_val, _acc) ->
454 [{<<"responder">>, jid:encode(_val)} | _acc].
455
456 decode_jingle_content(__TopXMLNS, __Opts,
457 {xmlel, <<"content">>, _attrs, _els}) ->
458 __Els = decode_jingle_content_els(__TopXMLNS, __Opts,
459 _els, []),
460 {Creator, Disposition, Name, Senders} =
461 decode_jingle_content_attrs(__TopXMLNS, _attrs,
462 undefined, undefined, undefined, undefined),
463 {jingle_content, Creator, Disposition, Name, Senders,
464 __Els}.
465
466 decode_jingle_content_els(__TopXMLNS, __Opts, [],
467 __Els) ->
468 lists:reverse(__Els);
469 decode_jingle_content_els(__TopXMLNS, __Opts,
470 [{xmlel, _name, _attrs, _} = _el | _els], __Els) ->
471 case proplists:get_bool(ignore_els, __Opts) of
472 true ->
473 decode_jingle_content_els(__TopXMLNS, __Opts, _els,
474 [_el | __Els]);
475 false ->
476 __XMLNS = xmpp_codec:get_attr(<<"xmlns">>, _attrs,
477 __TopXMLNS),
478 case xmpp_codec:get_mod(_name, __XMLNS) of
479 undefined ->
480 decode_jingle_content_els(__TopXMLNS, __Opts, _els,
481 [_el | __Els]);
482 Mod ->
483 decode_jingle_content_els(__TopXMLNS, __Opts, _els,
484 [Mod:do_decode(_name, __XMLNS, _el,
485 __Opts)
486 | __Els])
487 end
488 end;
489 decode_jingle_content_els(__TopXMLNS, __Opts,
490 [_ | _els], __Els) ->
491 decode_jingle_content_els(__TopXMLNS, __Opts, _els,
492 __Els).
493
494 decode_jingle_content_attrs(__TopXMLNS,
495 [{<<"creator">>, _val} | _attrs], _Creator,
496 Disposition, Name, Senders) ->
497 decode_jingle_content_attrs(__TopXMLNS, _attrs, _val,
498 Disposition, Name, Senders);
499 decode_jingle_content_attrs(__TopXMLNS,
500 [{<<"disposition">>, _val} | _attrs], Creator,
501 _Disposition, Name, Senders) ->
502 decode_jingle_content_attrs(__TopXMLNS, _attrs, Creator,
503 _val, Name, Senders);
504 decode_jingle_content_attrs(__TopXMLNS,
505 [{<<"name">>, _val} | _attrs], Creator, Disposition,
506 _Name, Senders) ->
507 decode_jingle_content_attrs(__TopXMLNS, _attrs, Creator,
508 Disposition, _val, Senders);
509 decode_jingle_content_attrs(__TopXMLNS,
510 [{<<"senders">>, _val} | _attrs], Creator,
511 Disposition, Name, _Senders) ->
512 decode_jingle_content_attrs(__TopXMLNS, _attrs, Creator,
513 Disposition, Name, _val);
514 decode_jingle_content_attrs(__TopXMLNS, [_ | _attrs],
515 Creator, Disposition, Name, Senders) ->
516 decode_jingle_content_attrs(__TopXMLNS, _attrs, Creator,
517 Disposition, Name, Senders);
518 decode_jingle_content_attrs(__TopXMLNS, [], Creator,
519 Disposition, Name, Senders) ->
520 {decode_jingle_content_attr_creator(__TopXMLNS,
521 Creator),
522 decode_jingle_content_attr_disposition(__TopXMLNS,
523 Disposition),
524 decode_jingle_content_attr_name(__TopXMLNS, Name),
525 decode_jingle_content_attr_senders(__TopXMLNS,
526 Senders)}.
527
528 encode_jingle_content({jingle_content, Creator,
529 Disposition, Name, Senders, __Els},
530 __TopXMLNS) ->
531 __NewTopXMLNS =
532 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
533 __TopXMLNS),
534 _els = [xmpp_codec:encode(_el, __NewTopXMLNS)
535 || _el <- __Els],
536 _attrs = encode_jingle_content_attr_senders(Senders,
537 encode_jingle_content_attr_name(Name,
538 encode_jingle_content_attr_disposition(Disposition,
539 encode_jingle_content_attr_creator(Creator,
540 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
541 __TopXMLNS))))),
542 {xmlel, <<"content">>, _attrs, _els}.
543
544 decode_jingle_content_attr_creator(__TopXMLNS,
545 undefined) ->
546 erlang:error({xmpp_codec,
547 {missing_attr, <<"creator">>, <<"content">>,
548 __TopXMLNS}});
549 decode_jingle_content_attr_creator(__TopXMLNS, _val) ->
550 case catch dec_enum(_val, [initiator, responder]) of
551 {'EXIT', _} ->
552 erlang:error({xmpp_codec,
553 {bad_attr_value, <<"creator">>, <<"content">>,
554 __TopXMLNS}});
555 _res -> _res
556 end.
557
558 encode_jingle_content_attr_creator(_val, _acc) ->
559 [{<<"creator">>, enc_enum(_val)} | _acc].
560
561 decode_jingle_content_attr_disposition(__TopXMLNS,
562 undefined) ->
563 <<>>;
564 decode_jingle_content_attr_disposition(__TopXMLNS,
565 _val) ->
566 _val.
567
568 encode_jingle_content_attr_disposition(<<>>, _acc) ->
569 _acc;
570 encode_jingle_content_attr_disposition(_val, _acc) ->
571 [{<<"disposition">>, _val} | _acc].
572
573 decode_jingle_content_attr_name(__TopXMLNS,
574 undefined) ->
575 erlang:error({xmpp_codec,
576 {missing_attr, <<"name">>, <<"content">>, __TopXMLNS}});
577 decode_jingle_content_attr_name(__TopXMLNS, _val) ->
578 _val.
579
580 encode_jingle_content_attr_name(_val, _acc) ->
581 [{<<"name">>, _val} | _acc].
582
583 decode_jingle_content_attr_senders(__TopXMLNS,
584 undefined) ->
585 both;
586 decode_jingle_content_attr_senders(__TopXMLNS, _val) ->
587 case catch dec_enum(_val,
588 [both, initiator, none, responder])
589 of
590 {'EXIT', _} ->
591 erlang:error({xmpp_codec,
592 {bad_attr_value, <<"senders">>, <<"content">>,
593 __TopXMLNS}});
594 _res -> _res
595 end.
596
597 encode_jingle_content_attr_senders(both, _acc) -> _acc;
598 encode_jingle_content_attr_senders(_val, _acc) ->
599 [{<<"senders">>, enc_enum(_val)} | _acc].
600
601 decode_jingle_reason(__TopXMLNS, __Opts,
602 {xmlel, <<"reason">>, _attrs, _els}) ->
603 {Text, Reason, __Els} =
604 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, [],
605 undefined, []),
606 {jingle_reason, Reason, Text, __Els}.
607
608 decode_jingle_reason_els(__TopXMLNS, __Opts, [], Text,
609 Reason, __Els) ->
610 {lists:reverse(Text), Reason, lists:reverse(__Els)};
611 decode_jingle_reason_els(__TopXMLNS, __Opts,
612 [{xmlel, <<"text">>, _attrs, _} = _el | _els], Text,
613 Reason, __Els) ->
614 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
615 __TopXMLNS)
616 of
617 <<"urn:xmpp:jingle:1">> ->
618 decode_jingle_reason_els(__TopXMLNS, __Opts, _els,
619 [decode_jingle_reason_text(<<"urn:xmpp:jingle:1">>,
620 __Opts, _el)
621 | Text],
622 Reason, __Els);
623 _ ->
624 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
625 Reason, [_el | __Els])
626 end;
627 decode_jingle_reason_els(__TopXMLNS, __Opts,
628 [{xmlel, <<"alternative-session">>, _attrs, _} = _el
629 | _els],
630 Text, Reason, __Els) ->
631 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
632 __TopXMLNS)
633 of
634 <<"urn:xmpp:jingle:1">> ->
635 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
636 decode_jingle_reason_alternative_session(<<"urn:xmpp:jingle:1">>,
637 __Opts,
638 _el),
639 __Els);
640 _ ->
641 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
642 Reason, [_el | __Els])
643 end;
644 decode_jingle_reason_els(__TopXMLNS, __Opts,
645 [{xmlel, <<"busy">>, _attrs, _} = _el | _els], Text,
646 Reason, __Els) ->
647 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
648 __TopXMLNS)
649 of
650 <<"urn:xmpp:jingle:1">> ->
651 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
652 decode_jingle_reason_busy(<<"urn:xmpp:jingle:1">>,
653 __Opts, _el),
654 __Els);
655 _ ->
656 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
657 Reason, [_el | __Els])
658 end;
659 decode_jingle_reason_els(__TopXMLNS, __Opts,
660 [{xmlel, <<"cancel">>, _attrs, _} = _el | _els], Text,
661 Reason, __Els) ->
662 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
663 __TopXMLNS)
664 of
665 <<"urn:xmpp:jingle:1">> ->
666 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
667 decode_jingle_reason_cancel(<<"urn:xmpp:jingle:1">>,
668 __Opts, _el),
669 __Els);
670 _ ->
671 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
672 Reason, [_el | __Els])
673 end;
674 decode_jingle_reason_els(__TopXMLNS, __Opts,
675 [{xmlel, <<"connectivity-error">>, _attrs, _} = _el
676 | _els],
677 Text, Reason, __Els) ->
678 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
679 __TopXMLNS)
680 of
681 <<"urn:xmpp:jingle:1">> ->
682 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
683 decode_jingle_reason_connectivity_error(<<"urn:xmpp:jingle:1">>,
684 __Opts,
685 _el),
686 __Els);
687 _ ->
688 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
689 Reason, [_el | __Els])
690 end;
691 decode_jingle_reason_els(__TopXMLNS, __Opts,
692 [{xmlel, <<"decline">>, _attrs, _} = _el | _els], Text,
693 Reason, __Els) ->
694 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
695 __TopXMLNS)
696 of
697 <<"urn:xmpp:jingle:1">> ->
698 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
699 decode_jingle_reason_decline(<<"urn:xmpp:jingle:1">>,
700 __Opts, _el),
701 __Els);
702 _ ->
703 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
704 Reason, [_el | __Els])
705 end;
706 decode_jingle_reason_els(__TopXMLNS, __Opts,
707 [{xmlel, <<"expired">>, _attrs, _} = _el | _els], Text,
708 Reason, __Els) ->
709 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
710 __TopXMLNS)
711 of
712 <<"urn:xmpp:jingle:1">> ->
713 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
714 decode_jingle_reason_expired(<<"urn:xmpp:jingle:1">>,
715 __Opts, _el),
716 __Els);
717 _ ->
718 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
719 Reason, [_el | __Els])
720 end;
721 decode_jingle_reason_els(__TopXMLNS, __Opts,
722 [{xmlel, <<"failed-application">>, _attrs, _} = _el
723 | _els],
724 Text, Reason, __Els) ->
725 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
726 __TopXMLNS)
727 of
728 <<"urn:xmpp:jingle:1">> ->
729 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
730 decode_jingle_reason_failed_application(<<"urn:xmpp:jingle:1">>,
731 __Opts,
732 _el),
733 __Els);
734 _ ->
735 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
736 Reason, [_el | __Els])
737 end;
738 decode_jingle_reason_els(__TopXMLNS, __Opts,
739 [{xmlel, <<"failed-transport">>, _attrs, _} = _el
740 | _els],
741 Text, Reason, __Els) ->
742 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
743 __TopXMLNS)
744 of
745 <<"urn:xmpp:jingle:1">> ->
746 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
747 decode_jingle_reason_failed_transport(<<"urn:xmpp:jingle:1">>,
748 __Opts,
749 _el),
750 __Els);
751 _ ->
752 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
753 Reason, [_el | __Els])
754 end;
755 decode_jingle_reason_els(__TopXMLNS, __Opts,
756 [{xmlel, <<"general-error">>, _attrs, _} = _el | _els],
757 Text, Reason, __Els) ->
758 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
759 __TopXMLNS)
760 of
761 <<"urn:xmpp:jingle:1">> ->
762 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
763 decode_jingle_reason_general_error(<<"urn:xmpp:jingle:1">>,
764 __Opts,
765 _el),
766 __Els);
767 _ ->
768 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
769 Reason, [_el | __Els])
770 end;
771 decode_jingle_reason_els(__TopXMLNS, __Opts,
772 [{xmlel, <<"gone">>, _attrs, _} = _el | _els], Text,
773 Reason, __Els) ->
774 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
775 __TopXMLNS)
776 of
777 <<"urn:xmpp:jingle:1">> ->
778 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
779 decode_jingle_reason_gone(<<"urn:xmpp:jingle:1">>,
780 __Opts, _el),
781 __Els);
782 _ ->
783 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
784 Reason, [_el | __Els])
785 end;
786 decode_jingle_reason_els(__TopXMLNS, __Opts,
787 [{xmlel, <<"incompatible-parameters">>, _attrs, _} =
788 _el
789 | _els],
790 Text, Reason, __Els) ->
791 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
792 __TopXMLNS)
793 of
794 <<"urn:xmpp:jingle:1">> ->
795 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
796 decode_jingle_reason_incompatible_parameters(<<"urn:xmpp:jingle:1">>,
797 __Opts,
798 _el),
799 __Els);
800 _ ->
801 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
802 Reason, [_el | __Els])
803 end;
804 decode_jingle_reason_els(__TopXMLNS, __Opts,
805 [{xmlel, <<"media-error">>, _attrs, _} = _el | _els],
806 Text, Reason, __Els) ->
807 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
808 __TopXMLNS)
809 of
810 <<"urn:xmpp:jingle:1">> ->
811 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
812 decode_jingle_reason_media_error(<<"urn:xmpp:jingle:1">>,
813 __Opts,
814 _el),
815 __Els);
816 _ ->
817 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
818 Reason, [_el | __Els])
819 end;
820 decode_jingle_reason_els(__TopXMLNS, __Opts,
821 [{xmlel, <<"security-error">>, _attrs, _} = _el
822 | _els],
823 Text, Reason, __Els) ->
824 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
825 __TopXMLNS)
826 of
827 <<"urn:xmpp:jingle:1">> ->
828 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
829 decode_jingle_reason_security_error(<<"urn:xmpp:jingle:1">>,
830 __Opts,
831 _el),
832 __Els);
833 _ ->
834 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
835 Reason, [_el | __Els])
836 end;
837 decode_jingle_reason_els(__TopXMLNS, __Opts,
838 [{xmlel, <<"success">>, _attrs, _} = _el | _els], Text,
839 Reason, __Els) ->
840 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
841 __TopXMLNS)
842 of
843 <<"urn:xmpp:jingle:1">> ->
844 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
845 decode_jingle_reason_success(<<"urn:xmpp:jingle:1">>,
846 __Opts, _el),
847 __Els);
848 _ ->
849 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
850 Reason, [_el | __Els])
851 end;
852 decode_jingle_reason_els(__TopXMLNS, __Opts,
853 [{xmlel, <<"timeout">>, _attrs, _} = _el | _els], Text,
854 Reason, __Els) ->
855 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
856 __TopXMLNS)
857 of
858 <<"urn:xmpp:jingle:1">> ->
859 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
860 decode_jingle_reason_timeout(<<"urn:xmpp:jingle:1">>,
861 __Opts, _el),
862 __Els);
863 _ ->
864 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
865 Reason, [_el | __Els])
866 end;
867 decode_jingle_reason_els(__TopXMLNS, __Opts,
868 [{xmlel, <<"unsupported-applications">>, _attrs, _} =
869 _el
870 | _els],
871 Text, Reason, __Els) ->
872 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
873 __TopXMLNS)
874 of
875 <<"urn:xmpp:jingle:1">> ->
876 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
877 decode_jingle_reason_unsupported_applications(<<"urn:xmpp:jingle:1">>,
878 __Opts,
879 _el),
880 __Els);
881 _ ->
882 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
883 Reason, [_el | __Els])
884 end;
885 decode_jingle_reason_els(__TopXMLNS, __Opts,
886 [{xmlel, <<"unsupported-transports">>, _attrs, _} = _el
887 | _els],
888 Text, Reason, __Els) ->
889 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
890 __TopXMLNS)
891 of
892 <<"urn:xmpp:jingle:1">> ->
893 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
894 decode_jingle_reason_unsupported_transports(<<"urn:xmpp:jingle:1">>,
895 __Opts,
896 _el),
897 __Els);
898 _ ->
899 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
900 Reason, [_el | __Els])
901 end;
902 decode_jingle_reason_els(__TopXMLNS, __Opts,
903 [{xmlel, _name, _attrs, _} = _el | _els], Text, Reason,
904 __Els) ->
905 case proplists:get_bool(ignore_els, __Opts) of
906 true ->
907 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
908 Reason, [_el | __Els]);
909 false ->
910 __XMLNS = xmpp_codec:get_attr(<<"xmlns">>, _attrs,
911 __TopXMLNS),
912 case xmpp_codec:get_mod(_name, __XMLNS) of
913 undefined ->
914 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
915 Reason, [_el | __Els]);
916 Mod ->
917 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
918 Reason,
919 [Mod:do_decode(_name, __XMLNS, _el,
920 __Opts)
921 | __Els])
922 end
923 end;
924 decode_jingle_reason_els(__TopXMLNS, __Opts, [_ | _els],
925 Text, Reason, __Els) ->
926 decode_jingle_reason_els(__TopXMLNS, __Opts, _els, Text,
927 Reason, __Els).
928
929 encode_jingle_reason({jingle_reason, Reason, Text,
930 __Els},
931 __TopXMLNS) ->
932 __NewTopXMLNS =
933 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
934 __TopXMLNS),
935 _els = [xmpp_codec:encode(_el, __NewTopXMLNS)
936 || _el <- __Els]
937 ++
938 lists:reverse('encode_jingle_reason_$text'(Text,
939 __NewTopXMLNS,
940 'encode_jingle_reason_$reason'(Reason,
941 __NewTopXMLNS,
942 []))),
943 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
944 __TopXMLNS),
945 {xmlel, <<"reason">>, _attrs, _els}.
946
947 'encode_jingle_reason_$text'([], __TopXMLNS, _acc) ->
948 _acc;
949 'encode_jingle_reason_$text'([Text | _els], __TopXMLNS,
950 _acc) ->
951 'encode_jingle_reason_$text'(_els, __TopXMLNS,
952 [encode_jingle_reason_text(Text, __TopXMLNS)
953 | _acc]).
954
955 'encode_jingle_reason_$reason'(undefined, __TopXMLNS,
956 _acc) ->
957 _acc;
958 'encode_jingle_reason_$reason'('alternative-session' =
959 Reason,
960 __TopXMLNS, _acc) ->
961 [encode_jingle_reason_alternative_session(Reason,
962 __TopXMLNS)
963 | _acc];
964 'encode_jingle_reason_$reason'(busy = Reason,
965 __TopXMLNS, _acc) ->
966 [encode_jingle_reason_busy(Reason, __TopXMLNS) | _acc];
967 'encode_jingle_reason_$reason'(cancel = Reason,
968 __TopXMLNS, _acc) ->
969 [encode_jingle_reason_cancel(Reason, __TopXMLNS)
970 | _acc];
971 'encode_jingle_reason_$reason'('connectivity-error' =
972 Reason,
973 __TopXMLNS, _acc) ->
974 [encode_jingle_reason_connectivity_error(Reason,
975 __TopXMLNS)
976 | _acc];
977 'encode_jingle_reason_$reason'(decline = Reason,
978 __TopXMLNS, _acc) ->
979 [encode_jingle_reason_decline(Reason, __TopXMLNS)
980 | _acc];
981 'encode_jingle_reason_$reason'(expired = Reason,
982 __TopXMLNS, _acc) ->
983 [encode_jingle_reason_expired(Reason, __TopXMLNS)
984 | _acc];
985 'encode_jingle_reason_$reason'('failed-application' =
986 Reason,
987 __TopXMLNS, _acc) ->
988 [encode_jingle_reason_failed_application(Reason,
989 __TopXMLNS)
990 | _acc];
991 'encode_jingle_reason_$reason'('failed-transport' =
992 Reason,
993 __TopXMLNS, _acc) ->
994 [encode_jingle_reason_failed_transport(Reason,
995 __TopXMLNS)
996 | _acc];
997 'encode_jingle_reason_$reason'('general-error' = Reason,
998 __TopXMLNS, _acc) ->
999 [encode_jingle_reason_general_error(Reason, __TopXMLNS)
1000 | _acc];
1001 'encode_jingle_reason_$reason'(gone = Reason,
1002 __TopXMLNS, _acc) ->
1003 [encode_jingle_reason_gone(Reason, __TopXMLNS) | _acc];
1004 'encode_jingle_reason_$reason'('incompatible-parameters' =
1005 Reason,
1006 __TopXMLNS, _acc) ->
1007 [encode_jingle_reason_incompatible_parameters(Reason,
1008 __TopXMLNS)
1009 | _acc];
1010 'encode_jingle_reason_$reason'('media-error' = Reason,
1011 __TopXMLNS, _acc) ->
1012 [encode_jingle_reason_media_error(Reason, __TopXMLNS)
1013 | _acc];
1014 'encode_jingle_reason_$reason'('security-error' =
1015 Reason,
1016 __TopXMLNS, _acc) ->
1017 [encode_jingle_reason_security_error(Reason, __TopXMLNS)
1018 | _acc];
1019 'encode_jingle_reason_$reason'(success = Reason,
1020 __TopXMLNS, _acc) ->
1021 [encode_jingle_reason_success(Reason, __TopXMLNS)
1022 | _acc];
1023 'encode_jingle_reason_$reason'(timeout = Reason,
1024 __TopXMLNS, _acc) ->
1025 [encode_jingle_reason_timeout(Reason, __TopXMLNS)
1026 | _acc];
1027 'encode_jingle_reason_$reason'('unsupported-applications' =
1028 Reason,
1029 __TopXMLNS, _acc) ->
1030 [encode_jingle_reason_unsupported_applications(Reason,
1031 __TopXMLNS)
1032 | _acc];
1033 'encode_jingle_reason_$reason'('unsupported-transports' =
1034 Reason,
1035 __TopXMLNS, _acc) ->
1036 [encode_jingle_reason_unsupported_transports(Reason,
1037 __TopXMLNS)
1038 | _acc].
1039
1040 decode_jingle_reason_text(__TopXMLNS, __Opts,
1041 {xmlel, <<"text">>, _attrs, _els}) ->
1042 Data = decode_jingle_reason_text_els(__TopXMLNS, __Opts,
1043 _els, <<>>),
1044 Lang = decode_jingle_reason_text_attrs(__TopXMLNS,
1045 _attrs, undefined),
1046 {text, Lang, Data}.
1047
1048 decode_jingle_reason_text_els(__TopXMLNS, __Opts, [],
1049 Data) ->
1050 decode_jingle_reason_text_cdata(__TopXMLNS, Data);
1051 decode_jingle_reason_text_els(__TopXMLNS, __Opts,
1052 [{xmlcdata, _data} | _els], Data) ->
1053 decode_jingle_reason_text_els(__TopXMLNS, __Opts, _els,
1054 <<Data/binary, _data/binary>>);
1055 decode_jingle_reason_text_els(__TopXMLNS, __Opts,
1056 [_ | _els], Data) ->
1057 decode_jingle_reason_text_els(__TopXMLNS, __Opts, _els,
1058 Data).
1059
1060 decode_jingle_reason_text_attrs(__TopXMLNS,
1061 [{<<"xml:lang">>, _val} | _attrs], _Lang) ->
1062 decode_jingle_reason_text_attrs(__TopXMLNS, _attrs,
1063 _val);
1064 decode_jingle_reason_text_attrs(__TopXMLNS,
1065 [_ | _attrs], Lang) ->
1066 decode_jingle_reason_text_attrs(__TopXMLNS, _attrs,
1067 Lang);
1068 decode_jingle_reason_text_attrs(__TopXMLNS, [], Lang) ->
1069 'decode_jingle_reason_text_attr_xml:lang'(__TopXMLNS,
1070 Lang).
1071
1072 encode_jingle_reason_text({text, Lang, Data},
1073 __TopXMLNS) ->
1074 __NewTopXMLNS =
1075 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1076 __TopXMLNS),
1077 _els = encode_jingle_reason_text_cdata(Data, []),
1078 _attrs = 'encode_jingle_reason_text_attr_xml:lang'(Lang,
1079 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1080 __TopXMLNS)),
1081 {xmlel, <<"text">>, _attrs, _els}.
1082
1083 'decode_jingle_reason_text_attr_xml:lang'(__TopXMLNS,
1084 undefined) ->
1085 <<>>;
1086 'decode_jingle_reason_text_attr_xml:lang'(__TopXMLNS,
1087 _val) ->
1088 _val.
1089
1090 'encode_jingle_reason_text_attr_xml:lang'(<<>>, _acc) ->
1091 _acc;
1092 'encode_jingle_reason_text_attr_xml:lang'(_val, _acc) ->
1093 [{<<"xml:lang">>, _val} | _acc].
1094
1095 decode_jingle_reason_text_cdata(__TopXMLNS, <<>>) ->
1096 <<>>;
1097 decode_jingle_reason_text_cdata(__TopXMLNS, _val) ->
1098 _val.
1099
1100 encode_jingle_reason_text_cdata(<<>>, _acc) -> _acc;
1101 encode_jingle_reason_text_cdata(_val, _acc) ->
1102 [{xmlcdata, _val} | _acc].
1103
1104 decode_jingle_reason_unsupported_transports(__TopXMLNS,
1105 __Opts,
1106 {xmlel,
1107 <<"unsupported-transports">>,
1108 _attrs, _els}) ->
1109 'unsupported-transports'.
1110
1111 encode_jingle_reason_unsupported_transports('unsupported-transports',
1112 __TopXMLNS) ->
1113 __NewTopXMLNS =
1114 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1115 __TopXMLNS),
1116 _els = [],
1117 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1118 __TopXMLNS),
1119 {xmlel, <<"unsupported-transports">>, _attrs, _els}.
1120
1121 decode_jingle_reason_unsupported_applications(__TopXMLNS,
1122 __Opts,
1123 {xmlel,
1124 <<"unsupported-applications">>,
1125 _attrs, _els}) ->
1126 'unsupported-applications'.
1127
1128 encode_jingle_reason_unsupported_applications('unsupported-applications',
1129 __TopXMLNS) ->
1130 __NewTopXMLNS =
1131 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1132 __TopXMLNS),
1133 _els = [],
1134 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1135 __TopXMLNS),
1136 {xmlel, <<"unsupported-applications">>, _attrs, _els}.
1137
1138 decode_jingle_reason_timeout(__TopXMLNS, __Opts,
1139 {xmlel, <<"timeout">>, _attrs, _els}) ->
1140 timeout.
1141
1142 encode_jingle_reason_timeout(timeout, __TopXMLNS) ->
1143 __NewTopXMLNS =
1144 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1145 __TopXMLNS),
1146 _els = [],
1147 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1148 __TopXMLNS),
1149 {xmlel, <<"timeout">>, _attrs, _els}.
1150
1151 decode_jingle_reason_success(__TopXMLNS, __Opts,
1152 {xmlel, <<"success">>, _attrs, _els}) ->
1153 success.
1154
1155 encode_jingle_reason_success(success, __TopXMLNS) ->
1156 __NewTopXMLNS =
1157 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1158 __TopXMLNS),
1159 _els = [],
1160 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1161 __TopXMLNS),
1162 {xmlel, <<"success">>, _attrs, _els}.
1163
1164 decode_jingle_reason_security_error(__TopXMLNS, __Opts,
1165 {xmlel, <<"security-error">>, _attrs,
1166 _els}) ->
1167 'security-error'.
1168
1169 encode_jingle_reason_security_error('security-error',
1170 __TopXMLNS) ->
1171 __NewTopXMLNS =
1172 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1173 __TopXMLNS),
1174 _els = [],
1175 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1176 __TopXMLNS),
1177 {xmlel, <<"security-error">>, _attrs, _els}.
1178
1179 decode_jingle_reason_media_error(__TopXMLNS, __Opts,
1180 {xmlel, <<"media-error">>, _attrs, _els}) ->
1181 'media-error'.
1182
1183 encode_jingle_reason_media_error('media-error',
1184 __TopXMLNS) ->
1185 __NewTopXMLNS =
1186 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1187 __TopXMLNS),
1188 _els = [],
1189 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1190 __TopXMLNS),
1191 {xmlel, <<"media-error">>, _attrs, _els}.
1192
1193 decode_jingle_reason_incompatible_parameters(__TopXMLNS,
1194 __Opts,
1195 {xmlel,
1196 <<"incompatible-parameters">>,
1197 _attrs, _els}) ->
1198 'incompatible-parameters'.
1199
1200 encode_jingle_reason_incompatible_parameters('incompatible-parameters',
1201 __TopXMLNS) ->
1202 __NewTopXMLNS =
1203 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1204 __TopXMLNS),
1205 _els = [],
1206 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1207 __TopXMLNS),
1208 {xmlel, <<"incompatible-parameters">>, _attrs, _els}.
1209
1210 decode_jingle_reason_gone(__TopXMLNS, __Opts,
1211 {xmlel, <<"gone">>, _attrs, _els}) ->
1212 gone.
1213
1214 encode_jingle_reason_gone(gone, __TopXMLNS) ->
1215 __NewTopXMLNS =
1216 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1217 __TopXMLNS),
1218 _els = [],
1219 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1220 __TopXMLNS),
1221 {xmlel, <<"gone">>, _attrs, _els}.
1222
1223 decode_jingle_reason_general_error(__TopXMLNS, __Opts,
1224 {xmlel, <<"general-error">>, _attrs,
1225 _els}) ->
1226 'general-error'.
1227
1228 encode_jingle_reason_general_error('general-error',
1229 __TopXMLNS) ->
1230 __NewTopXMLNS =
1231 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1232 __TopXMLNS),
1233 _els = [],
1234 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1235 __TopXMLNS),
1236 {xmlel, <<"general-error">>, _attrs, _els}.
1237
1238 decode_jingle_reason_failed_transport(__TopXMLNS,
1239 __Opts,
1240 {xmlel, <<"failed-transport">>, _attrs,
1241 _els}) ->
1242 'failed-transport'.
1243
1244 encode_jingle_reason_failed_transport('failed-transport',
1245 __TopXMLNS) ->
1246 __NewTopXMLNS =
1247 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1248 __TopXMLNS),
1249 _els = [],
1250 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1251 __TopXMLNS),
1252 {xmlel, <<"failed-transport">>, _attrs, _els}.
1253
1254 decode_jingle_reason_failed_application(__TopXMLNS,
1255 __Opts,
1256 {xmlel, <<"failed-application">>,
1257 _attrs, _els}) ->
1258 'failed-application'.
1259
1260 encode_jingle_reason_failed_application('failed-application',
1261 __TopXMLNS) ->
1262 __NewTopXMLNS =
1263 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1264 __TopXMLNS),
1265 _els = [],
1266 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1267 __TopXMLNS),
1268 {xmlel, <<"failed-application">>, _attrs, _els}.
1269
1270 decode_jingle_reason_expired(__TopXMLNS, __Opts,
1271 {xmlel, <<"expired">>, _attrs, _els}) ->
1272 expired.
1273
1274 encode_jingle_reason_expired(expired, __TopXMLNS) ->
1275 __NewTopXMLNS =
1276 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1277 __TopXMLNS),
1278 _els = [],
1279 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1280 __TopXMLNS),
1281 {xmlel, <<"expired">>, _attrs, _els}.
1282
1283 decode_jingle_reason_decline(__TopXMLNS, __Opts,
1284 {xmlel, <<"decline">>, _attrs, _els}) ->
1285 decline.
1286
1287 encode_jingle_reason_decline(decline, __TopXMLNS) ->
1288 __NewTopXMLNS =
1289 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1290 __TopXMLNS),
1291 _els = [],
1292 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1293 __TopXMLNS),
1294 {xmlel, <<"decline">>, _attrs, _els}.
1295
1296 decode_jingle_reason_connectivity_error(__TopXMLNS,
1297 __Opts,
1298 {xmlel, <<"connectivity-error">>,
1299 _attrs, _els}) ->
1300 'connectivity-error'.
1301
1302 encode_jingle_reason_connectivity_error('connectivity-error',
1303 __TopXMLNS) ->
1304 __NewTopXMLNS =
1305 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1306 __TopXMLNS),
1307 _els = [],
1308 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1309 __TopXMLNS),
1310 {xmlel, <<"connectivity-error">>, _attrs, _els}.
1311
1312 decode_jingle_reason_cancel(__TopXMLNS, __Opts,
1313 {xmlel, <<"cancel">>, _attrs, _els}) ->
1314 cancel.
1315
1316 encode_jingle_reason_cancel(cancel, __TopXMLNS) ->
1317 __NewTopXMLNS =
1318 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1319 __TopXMLNS),
1320 _els = [],
1321 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1322 __TopXMLNS),
1323 {xmlel, <<"cancel">>, _attrs, _els}.
1324
1325 decode_jingle_reason_busy(__TopXMLNS, __Opts,
1326 {xmlel, <<"busy">>, _attrs, _els}) ->
1327 busy.
1328
1329 encode_jingle_reason_busy(busy, __TopXMLNS) ->
1330 __NewTopXMLNS =
1331 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1332 __TopXMLNS),
1333 _els = [],
1334 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1335 __TopXMLNS),
1336 {xmlel, <<"busy">>, _attrs, _els}.
1337
1338 decode_jingle_reason_alternative_session(__TopXMLNS,
1339 __Opts,
1340 {xmlel, <<"alternative-session">>,
1341 _attrs, _els}) ->
1342 'alternative-session'.
1343
1344 encode_jingle_reason_alternative_session('alternative-session',
1345 __TopXMLNS) ->
1346 __NewTopXMLNS =
1347 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:1">>, [],
1348 __TopXMLNS),
1349 _els = [],
1350 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1351 __TopXMLNS),
1352 {xmlel, <<"alternative-session">>, _attrs, _els}.
1353
1354 decode_jingle_error_security_required(__TopXMLNS,
1355 __Opts,
1356 {xmlel, <<"security-required">>, _attrs,
1357 _els}) ->
1358 {jingle_error, 'security-required'}.
1359
1360 encode_jingle_error_security_required({jingle_error,
1361 'security-required'},
1362 __TopXMLNS) ->
1363 __NewTopXMLNS =
1364 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:errors:1">>,
1365 [], __TopXMLNS),
1366 _els = [],
1367 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1368 __TopXMLNS),
1369 {xmlel, <<"security-required">>, _attrs, _els}.
1370
1371 decode_jingle_error_unsupported_info(__TopXMLNS, __Opts,
1372 {xmlel, <<"unsupported-info">>, _attrs,
1373 _els}) ->
1374 {jingle_error, 'unsupported-info'}.
1375
1376 encode_jingle_error_unsupported_info({jingle_error,
1377 'unsupported-info'},
1378 __TopXMLNS) ->
1379 __NewTopXMLNS =
1380 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:errors:1">>,
1381 [], __TopXMLNS),
1382 _els = [],
1383 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1384 __TopXMLNS),
1385 {xmlel, <<"unsupported-info">>, _attrs, _els}.
1386
1387 decode_jingle_error_unknown_session(__TopXMLNS, __Opts,
1388 {xmlel, <<"unknown-session">>, _attrs,
1389 _els}) ->
1390 {jingle_error, 'unknown-session'}.
1391
1392 encode_jingle_error_unknown_session({jingle_error,
1393 'unknown-session'},
1394 __TopXMLNS) ->
1395 __NewTopXMLNS =
1396 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:errors:1">>,
1397 [], __TopXMLNS),
1398 _els = [],
1399 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1400 __TopXMLNS),
1401 {xmlel, <<"unknown-session">>, _attrs, _els}.
1402
1403 decode_jingle_error_tie_break(__TopXMLNS, __Opts,
1404 {xmlel, <<"tie-break">>, _attrs, _els}) ->
1405 {jingle_error, 'tie-break'}.
1406
1407 encode_jingle_error_tie_break({jingle_error,
1408 'tie-break'},
1409 __TopXMLNS) ->
1410 __NewTopXMLNS =
1411 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:errors:1">>,
1412 [], __TopXMLNS),
1413 _els = [],
1414 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1415 __TopXMLNS),
1416 {xmlel, <<"tie-break">>, _attrs, _els}.
1417
1418 decode_jingle_error_out_of_order(__TopXMLNS, __Opts,
1419 {xmlel, <<"out-of-order">>, _attrs, _els}) ->
1420 {jingle_error, 'out-of-order'}.
1421
1422 encode_jingle_error_out_of_order({jingle_error,
1423 'out-of-order'},
1424 __TopXMLNS) ->
1425 __NewTopXMLNS =
1426 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:errors:1">>,
1427 [], __TopXMLNS),
1428 _els = [],
1429 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
1430 __TopXMLNS),
1431 {xmlel, <<"out-of-order">>, _attrs, _els}.
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0234).
4
5 -compile(export_all).
6
7 do_decode(<<"file-too-large">>,
8 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>, El,
9 Opts) ->
10 decode_jingle_ft_error_file_too_large(<<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
11 Opts, El);
12 do_decode(<<"file-not-available">>,
13 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>, El,
14 Opts) ->
15 decode_jingle_ft_error_file_not_available(<<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
16 Opts, El);
17 do_decode(<<"checksum">>,
18 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
19 decode_jingle_ft_checksum(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
20 Opts, El);
21 do_decode(<<"received">>,
22 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
23 decode_jingle_ft_received(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
24 Opts, El);
25 do_decode(<<"description">>,
26 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
27 decode_jingle_ft_description(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
28 Opts, El);
29 do_decode(<<"file">>,
30 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
31 decode_jingle_ft_file(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
32 Opts, El);
33 do_decode(<<"range">>,
34 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
35 decode_jingle_ft_range(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
36 Opts, El);
37 do_decode(<<"size">>,
38 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
39 decode_jingle_ft_size(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
40 Opts, El);
41 do_decode(<<"name">>,
42 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
43 decode_jingle_ft_name(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
44 Opts, El);
45 do_decode(<<"media-type">>,
46 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
47 decode_jingle_ft_media_type(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
48 Opts, El);
49 do_decode(<<"desc">>,
50 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
51 decode_jingle_ft_desc(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
52 Opts, El);
53 do_decode(<<"date">>,
54 <<"urn:xmpp:jingle:apps:file-transfer:5">>, El, Opts) ->
55 decode_jingle_ft_date(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
56 Opts, El);
57 do_decode(Name, <<>>, _, _) ->
58 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
59 do_decode(Name, XMLNS, _, _) ->
60 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
61
62 tags() ->
63 [{<<"file-too-large">>,
64 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>},
65 {<<"file-not-available">>,
66 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>},
67 {<<"checksum">>,
68 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
69 {<<"received">>,
70 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
71 {<<"description">>,
72 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
73 {<<"file">>,
74 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
75 {<<"range">>,
76 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
77 {<<"size">>,
78 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
79 {<<"name">>,
80 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
81 {<<"media-type">>,
82 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
83 {<<"desc">>,
84 <<"urn:xmpp:jingle:apps:file-transfer:5">>},
85 {<<"date">>,
86 <<"urn:xmpp:jingle:apps:file-transfer:5">>}].
87
88 do_encode({text, _, _} = Desc, TopXMLNS) ->
89 encode_jingle_ft_desc(Desc, TopXMLNS);
90 do_encode({jingle_ft_range, _, _, _} = Range,
91 TopXMLNS) ->
92 encode_jingle_ft_range(Range, TopXMLNS);
93 do_encode({jingle_ft_file, _, _, _, _, _, _, _, _} =
94 File,
95 TopXMLNS) ->
96 encode_jingle_ft_file(File, TopXMLNS);
97 do_encode({jingle_ft_description, _} = Description,
98 TopXMLNS) ->
99 encode_jingle_ft_description(Description, TopXMLNS);
100 do_encode({jingle_ft_received, _, _} = Received,
101 TopXMLNS) ->
102 encode_jingle_ft_received(Received, TopXMLNS);
103 do_encode({jingle_ft_checksum, _, _, _} = Checksum,
104 TopXMLNS) ->
105 encode_jingle_ft_checksum(Checksum, TopXMLNS);
106 do_encode({jingle_ft_error, 'file-not-available'} =
107 File_not_available,
108 TopXMLNS) ->
109 encode_jingle_ft_error_file_not_available(File_not_available,
110 TopXMLNS);
111 do_encode({jingle_ft_error, 'file-too-large'} =
112 File_too_large,
113 TopXMLNS) ->
114 encode_jingle_ft_error_file_too_large(File_too_large,
115 TopXMLNS).
116
117 do_get_name({jingle_ft_checksum, _, _, _}) ->
118 <<"checksum">>;
119 do_get_name({jingle_ft_description, _}) ->
120 <<"description">>;
121 do_get_name({jingle_ft_error, 'file-not-available'}) ->
122 <<"file-not-available">>;
123 do_get_name({jingle_ft_error, 'file-too-large'}) ->
124 <<"file-too-large">>;
125 do_get_name({jingle_ft_file, _, _, _, _, _, _, _, _}) ->
126 <<"file">>;
127 do_get_name({jingle_ft_range, _, _, _}) -> <<"range">>;
128 do_get_name({jingle_ft_received, _, _}) ->
129 <<"received">>;
130 do_get_name({text, _, _}) -> <<"desc">>.
131
132 do_get_ns({jingle_ft_checksum, _, _, _}) ->
133 <<"urn:xmpp:jingle:apps:file-transfer:5">>;
134 do_get_ns({jingle_ft_description, _}) ->
135 <<"urn:xmpp:jingle:apps:file-transfer:5">>;
136 do_get_ns({jingle_ft_error, 'file-not-available'}) ->
137 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>;
138 do_get_ns({jingle_ft_error, 'file-too-large'}) ->
139 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>;
140 do_get_ns({jingle_ft_file, _, _, _, _, _, _, _, _}) ->
141 <<"urn:xmpp:jingle:apps:file-transfer:5">>;
142 do_get_ns({jingle_ft_range, _, _, _}) ->
143 <<"urn:xmpp:jingle:apps:file-transfer:5">>;
144 do_get_ns({jingle_ft_received, _, _}) ->
145 <<"urn:xmpp:jingle:apps:file-transfer:5">>;
146 do_get_ns({text, _, _}) ->
147 <<"urn:xmpp:jingle:apps:file-transfer:5">>.
148
149 pp(text, 2) -> [lang, data];
150 pp(jingle_ft_range, 3) -> [offset, length, hash];
151 pp(jingle_ft_file, 8) ->
152 [date, desc, hash, 'hash-used', 'media-type', name,
153 size, range];
154 pp(jingle_ft_description, 1) -> [file];
155 pp(jingle_ft_received, 2) -> [creator, name];
156 pp(jingle_ft_checksum, 3) -> [creator, name, file];
157 pp(jingle_ft_error, 1) -> [reason];
158 pp(_, _) -> no.
159
160 records() ->
161 [{text, 2}, {jingle_ft_range, 3}, {jingle_ft_file, 8},
162 {jingle_ft_description, 1}, {jingle_ft_received, 2},
163 {jingle_ft_checksum, 3}, {jingle_ft_error, 1}].
164
165 dec_enum(Val, Enums) ->
166 AtomVal = erlang:binary_to_existing_atom(Val, utf8),
167 case lists:member(AtomVal, Enums) of
168 true -> AtomVal
169 end.
170
171 dec_int(Val, Min, Max) ->
172 case erlang:binary_to_integer(Val) of
173 Int when Int =< Max, Min == infinity -> Int;
174 Int when Int =< Max, Int >= Min -> Int
175 end.
176
177 dec_utc(Val) -> xmpp_util:decode_timestamp(Val).
178
179 enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
180
181 enc_int(Int) -> erlang:integer_to_binary(Int).
182
183 enc_utc(Val) -> xmpp_util:encode_timestamp(Val).
184
185 decode_jingle_ft_error_file_too_large(__TopXMLNS,
186 __Opts,
187 {xmlel, <<"file-too-large">>, _attrs,
188 _els}) ->
189 {jingle_ft_error, 'file-too-large'}.
190
191 encode_jingle_ft_error_file_too_large({jingle_ft_error,
192 'file-too-large'},
193 __TopXMLNS) ->
194 __NewTopXMLNS =
195 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
196 [], __TopXMLNS),
197 _els = [],
198 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
199 __TopXMLNS),
200 {xmlel, <<"file-too-large">>, _attrs, _els}.
201
202 decode_jingle_ft_error_file_not_available(__TopXMLNS,
203 __Opts,
204 {xmlel, <<"file-not-available">>,
205 _attrs, _els}) ->
206 {jingle_ft_error, 'file-not-available'}.
207
208 encode_jingle_ft_error_file_not_available({jingle_ft_error,
209 'file-not-available'},
210 __TopXMLNS) ->
211 __NewTopXMLNS =
212 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:errors:0">>,
213 [], __TopXMLNS),
214 _els = [],
215 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
216 __TopXMLNS),
217 {xmlel, <<"file-not-available">>, _attrs, _els}.
218
219 decode_jingle_ft_checksum(__TopXMLNS, __Opts,
220 {xmlel, <<"checksum">>, _attrs, _els}) ->
221 File = decode_jingle_ft_checksum_els(__TopXMLNS, __Opts,
222 _els, error),
223 {Creator, Name} =
224 decode_jingle_ft_checksum_attrs(__TopXMLNS, _attrs,
225 undefined, undefined),
226 {jingle_ft_checksum, Creator, Name, File}.
227
228 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts, [],
229 File) ->
230 case File of
231 error ->
232 erlang:error({xmpp_codec,
233 {missing_tag, <<"file">>, __TopXMLNS}});
234 {value, File1} -> File1
235 end;
236 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts,
237 [{xmlel, <<"file">>, _attrs, _} = _el | _els],
238 File) ->
239 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
240 __TopXMLNS)
241 of
242 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
243 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts, _els,
244 {value,
245 decode_jingle_ft_file(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
246 __Opts, _el)});
247 _ ->
248 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts, _els,
249 File)
250 end;
251 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts,
252 [_ | _els], File) ->
253 decode_jingle_ft_checksum_els(__TopXMLNS, __Opts, _els,
254 File).
255
256 decode_jingle_ft_checksum_attrs(__TopXMLNS,
257 [{<<"creator">>, _val} | _attrs], _Creator,
258 Name) ->
259 decode_jingle_ft_checksum_attrs(__TopXMLNS, _attrs,
260 _val, Name);
261 decode_jingle_ft_checksum_attrs(__TopXMLNS,
262 [{<<"name">>, _val} | _attrs], Creator,
263 _Name) ->
264 decode_jingle_ft_checksum_attrs(__TopXMLNS, _attrs,
265 Creator, _val);
266 decode_jingle_ft_checksum_attrs(__TopXMLNS,
267 [_ | _attrs], Creator, Name) ->
268 decode_jingle_ft_checksum_attrs(__TopXMLNS, _attrs,
269 Creator, Name);
270 decode_jingle_ft_checksum_attrs(__TopXMLNS, [], Creator,
271 Name) ->
272 {decode_jingle_ft_checksum_attr_creator(__TopXMLNS,
273 Creator),
274 decode_jingle_ft_checksum_attr_name(__TopXMLNS, Name)}.
275
276 encode_jingle_ft_checksum({jingle_ft_checksum, Creator,
277 Name, File},
278 __TopXMLNS) ->
279 __NewTopXMLNS =
280 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
281 [], __TopXMLNS),
282 _els =
283 lists:reverse('encode_jingle_ft_checksum_$file'(File,
284 __NewTopXMLNS, [])),
285 _attrs = encode_jingle_ft_checksum_attr_name(Name,
286 encode_jingle_ft_checksum_attr_creator(Creator,
287 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
288 __TopXMLNS))),
289 {xmlel, <<"checksum">>, _attrs, _els}.
290
291 'encode_jingle_ft_checksum_$file'(File, __TopXMLNS,
292 _acc) ->
293 [encode_jingle_ft_file(File, __TopXMLNS) | _acc].
294
295 decode_jingle_ft_checksum_attr_creator(__TopXMLNS,
296 undefined) ->
297 undefined;
298 decode_jingle_ft_checksum_attr_creator(__TopXMLNS,
299 _val) ->
300 case catch dec_enum(_val, [initiator, responder]) of
301 {'EXIT', _} ->
302 erlang:error({xmpp_codec,
303 {bad_attr_value, <<"creator">>, <<"checksum">>,
304 __TopXMLNS}});
305 _res -> _res
306 end.
307
308 encode_jingle_ft_checksum_attr_creator(undefined,
309 _acc) ->
310 _acc;
311 encode_jingle_ft_checksum_attr_creator(_val, _acc) ->
312 [{<<"creator">>, enc_enum(_val)} | _acc].
313
314 decode_jingle_ft_checksum_attr_name(__TopXMLNS,
315 undefined) ->
316 <<>>;
317 decode_jingle_ft_checksum_attr_name(__TopXMLNS, _val) ->
318 _val.
319
320 encode_jingle_ft_checksum_attr_name(<<>>, _acc) -> _acc;
321 encode_jingle_ft_checksum_attr_name(_val, _acc) ->
322 [{<<"name">>, _val} | _acc].
323
324 decode_jingle_ft_received(__TopXMLNS, __Opts,
325 {xmlel, <<"received">>, _attrs, _els}) ->
326 {Creator, Name} =
327 decode_jingle_ft_received_attrs(__TopXMLNS, _attrs,
328 undefined, undefined),
329 {jingle_ft_received, Creator, Name}.
330
331 decode_jingle_ft_received_attrs(__TopXMLNS,
332 [{<<"creator">>, _val} | _attrs], _Creator,
333 Name) ->
334 decode_jingle_ft_received_attrs(__TopXMLNS, _attrs,
335 _val, Name);
336 decode_jingle_ft_received_attrs(__TopXMLNS,
337 [{<<"name">>, _val} | _attrs], Creator,
338 _Name) ->
339 decode_jingle_ft_received_attrs(__TopXMLNS, _attrs,
340 Creator, _val);
341 decode_jingle_ft_received_attrs(__TopXMLNS,
342 [_ | _attrs], Creator, Name) ->
343 decode_jingle_ft_received_attrs(__TopXMLNS, _attrs,
344 Creator, Name);
345 decode_jingle_ft_received_attrs(__TopXMLNS, [], Creator,
346 Name) ->
347 {decode_jingle_ft_received_attr_creator(__TopXMLNS,
348 Creator),
349 decode_jingle_ft_received_attr_name(__TopXMLNS, Name)}.
350
351 encode_jingle_ft_received({jingle_ft_received, Creator,
352 Name},
353 __TopXMLNS) ->
354 __NewTopXMLNS =
355 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
356 [], __TopXMLNS),
357 _els = [],
358 _attrs = encode_jingle_ft_received_attr_name(Name,
359 encode_jingle_ft_received_attr_creator(Creator,
360 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
361 __TopXMLNS))),
362 {xmlel, <<"received">>, _attrs, _els}.
363
364 decode_jingle_ft_received_attr_creator(__TopXMLNS,
365 undefined) ->
366 undefined;
367 decode_jingle_ft_received_attr_creator(__TopXMLNS,
368 _val) ->
369 case catch dec_enum(_val, [initiator, responder]) of
370 {'EXIT', _} ->
371 erlang:error({xmpp_codec,
372 {bad_attr_value, <<"creator">>, <<"received">>,
373 __TopXMLNS}});
374 _res -> _res
375 end.
376
377 encode_jingle_ft_received_attr_creator(undefined,
378 _acc) ->
379 _acc;
380 encode_jingle_ft_received_attr_creator(_val, _acc) ->
381 [{<<"creator">>, enc_enum(_val)} | _acc].
382
383 decode_jingle_ft_received_attr_name(__TopXMLNS,
384 undefined) ->
385 <<>>;
386 decode_jingle_ft_received_attr_name(__TopXMLNS, _val) ->
387 _val.
388
389 encode_jingle_ft_received_attr_name(<<>>, _acc) -> _acc;
390 encode_jingle_ft_received_attr_name(_val, _acc) ->
391 [{<<"name">>, _val} | _acc].
392
393 decode_jingle_ft_description(__TopXMLNS, __Opts,
394 {xmlel, <<"description">>, _attrs, _els}) ->
395 File = decode_jingle_ft_description_els(__TopXMLNS,
396 __Opts, _els, undefined),
397 {jingle_ft_description, File}.
398
399 decode_jingle_ft_description_els(__TopXMLNS, __Opts, [],
400 File) ->
401 File;
402 decode_jingle_ft_description_els(__TopXMLNS, __Opts,
403 [{xmlel, <<"file">>, _attrs, _} = _el | _els],
404 File) ->
405 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
406 __TopXMLNS)
407 of
408 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
409 decode_jingle_ft_description_els(__TopXMLNS, __Opts,
410 _els,
411 decode_jingle_ft_file(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
412 __Opts, _el));
413 _ ->
414 decode_jingle_ft_description_els(__TopXMLNS, __Opts,
415 _els, File)
416 end;
417 decode_jingle_ft_description_els(__TopXMLNS, __Opts,
418 [_ | _els], File) ->
419 decode_jingle_ft_description_els(__TopXMLNS, __Opts,
420 _els, File).
421
422 encode_jingle_ft_description({jingle_ft_description,
423 File},
424 __TopXMLNS) ->
425 __NewTopXMLNS =
426 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
427 [], __TopXMLNS),
428 _els =
429 lists:reverse('encode_jingle_ft_description_$file'(File,
430 __NewTopXMLNS, [])),
431 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
432 __TopXMLNS),
433 {xmlel, <<"description">>, _attrs, _els}.
434
435 'encode_jingle_ft_description_$file'(undefined,
436 __TopXMLNS, _acc) ->
437 _acc;
438 'encode_jingle_ft_description_$file'(File, __TopXMLNS,
439 _acc) ->
440 [encode_jingle_ft_file(File, __TopXMLNS) | _acc].
441
442 decode_jingle_ft_file(__TopXMLNS, __Opts,
443 {xmlel, <<"file">>, _attrs, _els}) ->
444 {Desc, Hash_used, Range, Size, Name, Media_type, Date,
445 Hash} =
446 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els, [],
447 undefined, undefined, undefined, undefined,
448 undefined, undefined, []),
449 {jingle_ft_file, Date, Desc, Hash, Hash_used,
450 Media_type, Name, Size, Range}.
451
452 decode_jingle_ft_file_els(__TopXMLNS, __Opts, [], Desc,
453 Hash_used, Range, Size, Name, Media_type, Date,
454 Hash) ->
455 {lists:reverse(Desc), Hash_used, Range, Size, Name,
456 Media_type, Date, lists:reverse(Hash)};
457 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
458 [{xmlel, <<"date">>, _attrs, _} = _el | _els], Desc,
459 Hash_used, Range, Size, Name, Media_type, Date,
460 Hash) ->
461 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
462 __TopXMLNS)
463 of
464 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
465 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
466 Desc, Hash_used, Range, Size, Name,
467 Media_type,
468 decode_jingle_ft_date(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
469 __Opts, _el),
470 Hash);
471 _ ->
472 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
473 Desc, Hash_used, Range, Size, Name,
474 Media_type, Date, Hash)
475 end;
476 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
477 [{xmlel, <<"desc">>, _attrs, _} = _el | _els], Desc,
478 Hash_used, Range, Size, Name, Media_type, Date,
479 Hash) ->
480 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
481 __TopXMLNS)
482 of
483 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
484 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
485 [decode_jingle_ft_desc(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
486 __Opts, _el)
487 | Desc],
488 Hash_used, Range, Size, Name, Media_type,
489 Date, Hash);
490 _ ->
491 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
492 Desc, Hash_used, Range, Size, Name,
493 Media_type, Date, Hash)
494 end;
495 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
496 [{xmlel, <<"hash">>, _attrs, _} = _el | _els], Desc,
497 Hash_used, Range, Size, Name, Media_type, Date,
498 Hash) ->
499 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
500 __TopXMLNS)
501 of
502 <<"urn:xmpp:hashes:2">> ->
503 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
504 Desc, Hash_used, Range, Size, Name,
505 Media_type, Date,
506 [xep0300:decode_hash(<<"urn:xmpp:hashes:2">>,
507 __Opts, _el)
508 | Hash]);
509 _ ->
510 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
511 Desc, Hash_used, Range, Size, Name,
512 Media_type, Date, Hash)
513 end;
514 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
515 [{xmlel, <<"hash-used">>, _attrs, _} = _el | _els],
516 Desc, Hash_used, Range, Size, Name, Media_type, Date,
517 Hash) ->
518 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
519 __TopXMLNS)
520 of
521 <<"urn:xmpp:hashes:2">> ->
522 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
523 Desc,
524 xep0300:decode_hash_used(<<"urn:xmpp:hashes:2">>,
525 __Opts, _el),
526 Range, Size, Name, Media_type, Date, Hash);
527 _ ->
528 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
529 Desc, Hash_used, Range, Size, Name,
530 Media_type, Date, Hash)
531 end;
532 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
533 [{xmlel, <<"media-type">>, _attrs, _} = _el | _els],
534 Desc, Hash_used, Range, Size, Name, Media_type, Date,
535 Hash) ->
536 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
537 __TopXMLNS)
538 of
539 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
540 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
541 Desc, Hash_used, Range, Size, Name,
542 decode_jingle_ft_media_type(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
543 __Opts, _el),
544 Date, Hash);
545 _ ->
546 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
547 Desc, Hash_used, Range, Size, Name,
548 Media_type, Date, Hash)
549 end;
550 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
551 [{xmlel, <<"name">>, _attrs, _} = _el | _els], Desc,
552 Hash_used, Range, Size, Name, Media_type, Date,
553 Hash) ->
554 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
555 __TopXMLNS)
556 of
557 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
558 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
559 Desc, Hash_used, Range, Size,
560 decode_jingle_ft_name(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
561 __Opts, _el),
562 Media_type, Date, Hash);
563 _ ->
564 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
565 Desc, Hash_used, Range, Size, Name,
566 Media_type, Date, Hash)
567 end;
568 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
569 [{xmlel, <<"size">>, _attrs, _} = _el | _els], Desc,
570 Hash_used, Range, Size, Name, Media_type, Date,
571 Hash) ->
572 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
573 __TopXMLNS)
574 of
575 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
576 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
577 Desc, Hash_used, Range,
578 decode_jingle_ft_size(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
579 __Opts, _el),
580 Name, Media_type, Date, Hash);
581 _ ->
582 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
583 Desc, Hash_used, Range, Size, Name,
584 Media_type, Date, Hash)
585 end;
586 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
587 [{xmlel, <<"range">>, _attrs, _} = _el | _els], Desc,
588 Hash_used, Range, Size, Name, Media_type, Date,
589 Hash) ->
590 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
591 __TopXMLNS)
592 of
593 <<"urn:xmpp:jingle:apps:file-transfer:5">> ->
594 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
595 Desc, Hash_used,
596 decode_jingle_ft_range(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
597 __Opts, _el),
598 Size, Name, Media_type, Date, Hash);
599 _ ->
600 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
601 Desc, Hash_used, Range, Size, Name,
602 Media_type, Date, Hash)
603 end;
604 decode_jingle_ft_file_els(__TopXMLNS, __Opts,
605 [_ | _els], Desc, Hash_used, Range, Size, Name,
606 Media_type, Date, Hash) ->
607 decode_jingle_ft_file_els(__TopXMLNS, __Opts, _els,
608 Desc, Hash_used, Range, Size, Name, Media_type,
609 Date, Hash).
610
611 encode_jingle_ft_file({jingle_ft_file, Date, Desc, Hash,
612 Hash_used, Media_type, Name, Size, Range},
613 __TopXMLNS) ->
614 __NewTopXMLNS =
615 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
616 [], __TopXMLNS),
617 _els = lists:reverse('encode_jingle_ft_file_$desc'(Desc,
618 __NewTopXMLNS,
619 'encode_jingle_ft_file_$hash-used'(Hash_used,
620 __NewTopXMLNS,
621 'encode_jingle_ft_file_$range'(Range,
622 __NewTopXMLNS,
623 'encode_jingle_ft_file_$size'(Size,
624 __NewTopXMLNS,
625 'encode_jingle_ft_file_$name'(Name,
626 __NewTopXMLNS,
627 'encode_jingle_ft_file_$media-type'(Media_type,
628 __NewTopXMLNS,
629 'encode_jingle_ft_file_$date'(Date,
630 __NewTopXMLNS,
631 'encode_jingle_ft_file_$hash'(Hash,
632 __NewTopXMLNS,
633 []))))))))),
634 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
635 __TopXMLNS),
636 {xmlel, <<"file">>, _attrs, _els}.
637
638 'encode_jingle_ft_file_$desc'([], __TopXMLNS, _acc) ->
639 _acc;
640 'encode_jingle_ft_file_$desc'([Desc | _els], __TopXMLNS,
641 _acc) ->
642 'encode_jingle_ft_file_$desc'(_els, __TopXMLNS,
643 [encode_jingle_ft_desc(Desc, __TopXMLNS)
644 | _acc]).
645
646 'encode_jingle_ft_file_$hash-used'(undefined,
647 __TopXMLNS, _acc) ->
648 _acc;
649 'encode_jingle_ft_file_$hash-used'(Hash_used,
650 __TopXMLNS, _acc) ->
651 [xep0300:encode_hash_used(Hash_used, __TopXMLNS)
652 | _acc].
653
654 'encode_jingle_ft_file_$range'(undefined, __TopXMLNS,
655 _acc) ->
656 _acc;
657 'encode_jingle_ft_file_$range'(Range, __TopXMLNS,
658 _acc) ->
659 [encode_jingle_ft_range(Range, __TopXMLNS) | _acc].
660
661 'encode_jingle_ft_file_$size'(undefined, __TopXMLNS,
662 _acc) ->
663 _acc;
664 'encode_jingle_ft_file_$size'(Size, __TopXMLNS, _acc) ->
665 [encode_jingle_ft_size(Size, __TopXMLNS) | _acc].
666
667 'encode_jingle_ft_file_$name'(undefined, __TopXMLNS,
668 _acc) ->
669 _acc;
670 'encode_jingle_ft_file_$name'(Name, __TopXMLNS, _acc) ->
671 [encode_jingle_ft_name(Name, __TopXMLNS) | _acc].
672
673 'encode_jingle_ft_file_$media-type'(undefined,
674 __TopXMLNS, _acc) ->
675 _acc;
676 'encode_jingle_ft_file_$media-type'(Media_type,
677 __TopXMLNS, _acc) ->
678 [encode_jingle_ft_media_type(Media_type, __TopXMLNS)
679 | _acc].
680
681 'encode_jingle_ft_file_$date'(undefined, __TopXMLNS,
682 _acc) ->
683 _acc;
684 'encode_jingle_ft_file_$date'(Date, __TopXMLNS, _acc) ->
685 [encode_jingle_ft_date(Date, __TopXMLNS) | _acc].
686
687 'encode_jingle_ft_file_$hash'([], __TopXMLNS, _acc) ->
688 _acc;
689 'encode_jingle_ft_file_$hash'([Hash | _els], __TopXMLNS,
690 _acc) ->
691 'encode_jingle_ft_file_$hash'(_els, __TopXMLNS,
692 [xep0300:encode_hash(Hash, __TopXMLNS)
693 | _acc]).
694
695 decode_jingle_ft_range(__TopXMLNS, __Opts,
696 {xmlel, <<"range">>, _attrs, _els}) ->
697 Hash = decode_jingle_ft_range_els(__TopXMLNS, __Opts,
698 _els, []),
699 {Offset, Length} =
700 decode_jingle_ft_range_attrs(__TopXMLNS, _attrs,
701 undefined, undefined),
702 {jingle_ft_range, Offset, Length, Hash}.
703
704 decode_jingle_ft_range_els(__TopXMLNS, __Opts, [],
705 Hash) ->
706 lists:reverse(Hash);
707 decode_jingle_ft_range_els(__TopXMLNS, __Opts,
708 [{xmlel, <<"hash">>, _attrs, _} = _el | _els],
709 Hash) ->
710 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
711 __TopXMLNS)
712 of
713 <<"urn:xmpp:hashes:2">> ->
714 decode_jingle_ft_range_els(__TopXMLNS, __Opts, _els,
715 [xep0300:decode_hash(<<"urn:xmpp:hashes:2">>,
716 __Opts, _el)
717 | Hash]);
718 _ ->
719 decode_jingle_ft_range_els(__TopXMLNS, __Opts, _els,
720 Hash)
721 end;
722 decode_jingle_ft_range_els(__TopXMLNS, __Opts,
723 [_ | _els], Hash) ->
724 decode_jingle_ft_range_els(__TopXMLNS, __Opts, _els,
725 Hash).
726
727 decode_jingle_ft_range_attrs(__TopXMLNS,
728 [{<<"offset">>, _val} | _attrs], _Offset,
729 Length) ->
730 decode_jingle_ft_range_attrs(__TopXMLNS, _attrs, _val,
731 Length);
732 decode_jingle_ft_range_attrs(__TopXMLNS,
733 [{<<"length">>, _val} | _attrs], Offset,
734 _Length) ->
735 decode_jingle_ft_range_attrs(__TopXMLNS, _attrs, Offset,
736 _val);
737 decode_jingle_ft_range_attrs(__TopXMLNS, [_ | _attrs],
738 Offset, Length) ->
739 decode_jingle_ft_range_attrs(__TopXMLNS, _attrs, Offset,
740 Length);
741 decode_jingle_ft_range_attrs(__TopXMLNS, [], Offset,
742 Length) ->
743 {decode_jingle_ft_range_attr_offset(__TopXMLNS, Offset),
744 decode_jingle_ft_range_attr_length(__TopXMLNS, Length)}.
745
746 encode_jingle_ft_range({jingle_ft_range, Offset, Length,
747 Hash},
748 __TopXMLNS) ->
749 __NewTopXMLNS =
750 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
751 [], __TopXMLNS),
752 _els =
753 lists:reverse('encode_jingle_ft_range_$hash'(Hash,
754 __NewTopXMLNS, [])),
755 _attrs = encode_jingle_ft_range_attr_length(Length,
756 encode_jingle_ft_range_attr_offset(Offset,
757 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
758 __TopXMLNS))),
759 {xmlel, <<"range">>, _attrs, _els}.
760
761 'encode_jingle_ft_range_$hash'([], __TopXMLNS, _acc) ->
762 _acc;
763 'encode_jingle_ft_range_$hash'([Hash | _els],
764 __TopXMLNS, _acc) ->
765 'encode_jingle_ft_range_$hash'(_els, __TopXMLNS,
766 [xep0300:encode_hash(Hash, __TopXMLNS)
767 | _acc]).
768
769 decode_jingle_ft_range_attr_offset(__TopXMLNS,
770 undefined) ->
771 0;
772 decode_jingle_ft_range_attr_offset(__TopXMLNS, _val) ->
773 case catch dec_int(_val, 0, infinity) of
774 {'EXIT', _} ->
775 erlang:error({xmpp_codec,
776 {bad_attr_value, <<"offset">>, <<"range">>,
777 __TopXMLNS}});
778 _res -> _res
779 end.
780
781 encode_jingle_ft_range_attr_offset(0, _acc) -> _acc;
782 encode_jingle_ft_range_attr_offset(_val, _acc) ->
783 [{<<"offset">>, enc_int(_val)} | _acc].
784
785 decode_jingle_ft_range_attr_length(__TopXMLNS,
786 undefined) ->
787 undefined;
788 decode_jingle_ft_range_attr_length(__TopXMLNS, _val) ->
789 case catch dec_int(_val, 0, infinity) of
790 {'EXIT', _} ->
791 erlang:error({xmpp_codec,
792 {bad_attr_value, <<"length">>, <<"range">>,
793 __TopXMLNS}});
794 _res -> _res
795 end.
796
797 encode_jingle_ft_range_attr_length(undefined, _acc) ->
798 _acc;
799 encode_jingle_ft_range_attr_length(_val, _acc) ->
800 [{<<"length">>, enc_int(_val)} | _acc].
801
802 decode_jingle_ft_size(__TopXMLNS, __Opts,
803 {xmlel, <<"size">>, _attrs, _els}) ->
804 Cdata = decode_jingle_ft_size_els(__TopXMLNS, __Opts,
805 _els, <<>>),
806 Cdata.
807
808 decode_jingle_ft_size_els(__TopXMLNS, __Opts, [],
809 Cdata) ->
810 decode_jingle_ft_size_cdata(__TopXMLNS, Cdata);
811 decode_jingle_ft_size_els(__TopXMLNS, __Opts,
812 [{xmlcdata, _data} | _els], Cdata) ->
813 decode_jingle_ft_size_els(__TopXMLNS, __Opts, _els,
814 <<Cdata/binary, _data/binary>>);
815 decode_jingle_ft_size_els(__TopXMLNS, __Opts,
816 [_ | _els], Cdata) ->
817 decode_jingle_ft_size_els(__TopXMLNS, __Opts, _els,
818 Cdata).
819
820 encode_jingle_ft_size(Cdata, __TopXMLNS) ->
821 __NewTopXMLNS =
822 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
823 [], __TopXMLNS),
824 _els = encode_jingle_ft_size_cdata(Cdata, []),
825 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
826 __TopXMLNS),
827 {xmlel, <<"size">>, _attrs, _els}.
828
829 decode_jingle_ft_size_cdata(__TopXMLNS, <<>>) ->
830 erlang:error({xmpp_codec,
831 {missing_cdata, <<>>, <<"size">>, __TopXMLNS}});
832 decode_jingle_ft_size_cdata(__TopXMLNS, _val) ->
833 case catch dec_int(_val, 0, infinity) of
834 {'EXIT', _} ->
835 erlang:error({xmpp_codec,
836 {bad_cdata_value, <<>>, <<"size">>, __TopXMLNS}});
837 _res -> _res
838 end.
839
840 encode_jingle_ft_size_cdata(_val, _acc) ->
841 [{xmlcdata, enc_int(_val)} | _acc].
842
843 decode_jingle_ft_name(__TopXMLNS, __Opts,
844 {xmlel, <<"name">>, _attrs, _els}) ->
845 Cdata = decode_jingle_ft_name_els(__TopXMLNS, __Opts,
846 _els, <<>>),
847 Cdata.
848
849 decode_jingle_ft_name_els(__TopXMLNS, __Opts, [],
850 Cdata) ->
851 decode_jingle_ft_name_cdata(__TopXMLNS, Cdata);
852 decode_jingle_ft_name_els(__TopXMLNS, __Opts,
853 [{xmlcdata, _data} | _els], Cdata) ->
854 decode_jingle_ft_name_els(__TopXMLNS, __Opts, _els,
855 <<Cdata/binary, _data/binary>>);
856 decode_jingle_ft_name_els(__TopXMLNS, __Opts,
857 [_ | _els], Cdata) ->
858 decode_jingle_ft_name_els(__TopXMLNS, __Opts, _els,
859 Cdata).
860
861 encode_jingle_ft_name(Cdata, __TopXMLNS) ->
862 __NewTopXMLNS =
863 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
864 [], __TopXMLNS),
865 _els = encode_jingle_ft_name_cdata(Cdata, []),
866 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
867 __TopXMLNS),
868 {xmlel, <<"name">>, _attrs, _els}.
869
870 decode_jingle_ft_name_cdata(__TopXMLNS, <<>>) ->
871 erlang:error({xmpp_codec,
872 {missing_cdata, <<>>, <<"name">>, __TopXMLNS}});
873 decode_jingle_ft_name_cdata(__TopXMLNS, _val) -> _val.
874
875 encode_jingle_ft_name_cdata(_val, _acc) ->
876 [{xmlcdata, _val} | _acc].
877
878 decode_jingle_ft_media_type(__TopXMLNS, __Opts,
879 {xmlel, <<"media-type">>, _attrs, _els}) ->
880 Cdata = decode_jingle_ft_media_type_els(__TopXMLNS,
881 __Opts, _els, <<>>),
882 Cdata.
883
884 decode_jingle_ft_media_type_els(__TopXMLNS, __Opts, [],
885 Cdata) ->
886 decode_jingle_ft_media_type_cdata(__TopXMLNS, Cdata);
887 decode_jingle_ft_media_type_els(__TopXMLNS, __Opts,
888 [{xmlcdata, _data} | _els], Cdata) ->
889 decode_jingle_ft_media_type_els(__TopXMLNS, __Opts,
890 _els, <<Cdata/binary, _data/binary>>);
891 decode_jingle_ft_media_type_els(__TopXMLNS, __Opts,
892 [_ | _els], Cdata) ->
893 decode_jingle_ft_media_type_els(__TopXMLNS, __Opts,
894 _els, Cdata).
895
896 encode_jingle_ft_media_type(Cdata, __TopXMLNS) ->
897 __NewTopXMLNS =
898 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
899 [], __TopXMLNS),
900 _els = encode_jingle_ft_media_type_cdata(Cdata, []),
901 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
902 __TopXMLNS),
903 {xmlel, <<"media-type">>, _attrs, _els}.
904
905 decode_jingle_ft_media_type_cdata(__TopXMLNS, <<>>) ->
906 erlang:error({xmpp_codec,
907 {missing_cdata, <<>>, <<"media-type">>, __TopXMLNS}});
908 decode_jingle_ft_media_type_cdata(__TopXMLNS, _val) ->
909 _val.
910
911 encode_jingle_ft_media_type_cdata(_val, _acc) ->
912 [{xmlcdata, _val} | _acc].
913
914 decode_jingle_ft_desc(__TopXMLNS, __Opts,
915 {xmlel, <<"desc">>, _attrs, _els}) ->
916 Data = decode_jingle_ft_desc_els(__TopXMLNS, __Opts,
917 _els, <<>>),
918 Lang = decode_jingle_ft_desc_attrs(__TopXMLNS, _attrs,
919 undefined),
920 {text, Lang, Data}.
921
922 decode_jingle_ft_desc_els(__TopXMLNS, __Opts, [],
923 Data) ->
924 decode_jingle_ft_desc_cdata(__TopXMLNS, Data);
925 decode_jingle_ft_desc_els(__TopXMLNS, __Opts,
926 [{xmlcdata, _data} | _els], Data) ->
927 decode_jingle_ft_desc_els(__TopXMLNS, __Opts, _els,
928 <<Data/binary, _data/binary>>);
929 decode_jingle_ft_desc_els(__TopXMLNS, __Opts,
930 [_ | _els], Data) ->
931 decode_jingle_ft_desc_els(__TopXMLNS, __Opts, _els,
932 Data).
933
934 decode_jingle_ft_desc_attrs(__TopXMLNS,
935 [{<<"xml:lang">>, _val} | _attrs], _Lang) ->
936 decode_jingle_ft_desc_attrs(__TopXMLNS, _attrs, _val);
937 decode_jingle_ft_desc_attrs(__TopXMLNS, [_ | _attrs],
938 Lang) ->
939 decode_jingle_ft_desc_attrs(__TopXMLNS, _attrs, Lang);
940 decode_jingle_ft_desc_attrs(__TopXMLNS, [], Lang) ->
941 'decode_jingle_ft_desc_attr_xml:lang'(__TopXMLNS, Lang).
942
943 encode_jingle_ft_desc({text, Lang, Data}, __TopXMLNS) ->
944 __NewTopXMLNS =
945 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
946 [], __TopXMLNS),
947 _els = encode_jingle_ft_desc_cdata(Data, []),
948 _attrs = 'encode_jingle_ft_desc_attr_xml:lang'(Lang,
949 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
950 __TopXMLNS)),
951 {xmlel, <<"desc">>, _attrs, _els}.
952
953 'decode_jingle_ft_desc_attr_xml:lang'(__TopXMLNS,
954 undefined) ->
955 <<>>;
956 'decode_jingle_ft_desc_attr_xml:lang'(__TopXMLNS,
957 _val) ->
958 _val.
959
960 'encode_jingle_ft_desc_attr_xml:lang'(<<>>, _acc) ->
961 _acc;
962 'encode_jingle_ft_desc_attr_xml:lang'(_val, _acc) ->
963 [{<<"xml:lang">>, _val} | _acc].
964
965 decode_jingle_ft_desc_cdata(__TopXMLNS, <<>>) -> <<>>;
966 decode_jingle_ft_desc_cdata(__TopXMLNS, _val) -> _val.
967
968 encode_jingle_ft_desc_cdata(<<>>, _acc) -> _acc;
969 encode_jingle_ft_desc_cdata(_val, _acc) ->
970 [{xmlcdata, _val} | _acc].
971
972 decode_jingle_ft_date(__TopXMLNS, __Opts,
973 {xmlel, <<"date">>, _attrs, _els}) ->
974 Cdata = decode_jingle_ft_date_els(__TopXMLNS, __Opts,
975 _els, <<>>),
976 Cdata.
977
978 decode_jingle_ft_date_els(__TopXMLNS, __Opts, [],
979 Cdata) ->
980 decode_jingle_ft_date_cdata(__TopXMLNS, Cdata);
981 decode_jingle_ft_date_els(__TopXMLNS, __Opts,
982 [{xmlcdata, _data} | _els], Cdata) ->
983 decode_jingle_ft_date_els(__TopXMLNS, __Opts, _els,
984 <<Cdata/binary, _data/binary>>);
985 decode_jingle_ft_date_els(__TopXMLNS, __Opts,
986 [_ | _els], Cdata) ->
987 decode_jingle_ft_date_els(__TopXMLNS, __Opts, _els,
988 Cdata).
989
990 encode_jingle_ft_date(Cdata, __TopXMLNS) ->
991 __NewTopXMLNS =
992 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:apps:file-transfer:5">>,
993 [], __TopXMLNS),
994 _els = encode_jingle_ft_date_cdata(Cdata, []),
995 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
996 __TopXMLNS),
997 {xmlel, <<"date">>, _attrs, _els}.
998
999 decode_jingle_ft_date_cdata(__TopXMLNS, <<>>) ->
1000 erlang:error({xmpp_codec,
1001 {missing_cdata, <<>>, <<"date">>, __TopXMLNS}});
1002 decode_jingle_ft_date_cdata(__TopXMLNS, _val) ->
1003 case catch dec_utc(_val) of
1004 {'EXIT', _} ->
1005 erlang:error({xmpp_codec,
1006 {bad_cdata_value, <<>>, <<"date">>, __TopXMLNS}});
1007 _res -> _res
1008 end.
1009
1010 encode_jingle_ft_date_cdata(_val, _acc) ->
1011 [{xmlcdata, enc_utc(_val)} | _acc].
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0260).
4
5 -compile(export_all).
6
7 do_decode(<<"transport">>,
8 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
9 decode_jingle_s5b_transport(<<"urn:xmpp:jingle:transports:s5b:1">>,
10 Opts, El);
11 do_decode(<<"proxy-error">>,
12 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
13 decode_jingle_s5b_proxy_error(<<"urn:xmpp:jingle:transports:s5b:1">>,
14 Opts, El);
15 do_decode(<<"candidate-error">>,
16 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
17 decode_jingle_s5b_candidate_error(<<"urn:xmpp:jingle:transports:s5b:1">>,
18 Opts, El);
19 do_decode(<<"activated">>,
20 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
21 decode_jingle_s5b_activated(<<"urn:xmpp:jingle:transports:s5b:1">>,
22 Opts, El);
23 do_decode(<<"candidate">>,
24 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
25 decode_jingle_s5b_candidate(<<"urn:xmpp:jingle:transports:s5b:1">>,
26 Opts, El);
27 do_decode(<<"candidate-used">>,
28 <<"urn:xmpp:jingle:transports:s5b:1">>, El, Opts) ->
29 decode_jingle_s5b_candidate_used(<<"urn:xmpp:jingle:transports:s5b:1">>,
30 Opts, El);
31 do_decode(Name, <<>>, _, _) ->
32 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
33 do_decode(Name, XMLNS, _, _) ->
34 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
35
36 tags() ->
37 [{<<"transport">>,
38 <<"urn:xmpp:jingle:transports:s5b:1">>},
39 {<<"proxy-error">>,
40 <<"urn:xmpp:jingle:transports:s5b:1">>},
41 {<<"candidate-error">>,
42 <<"urn:xmpp:jingle:transports:s5b:1">>},
43 {<<"activated">>,
44 <<"urn:xmpp:jingle:transports:s5b:1">>},
45 {<<"candidate">>,
46 <<"urn:xmpp:jingle:transports:s5b:1">>},
47 {<<"candidate-used">>,
48 <<"urn:xmpp:jingle:transports:s5b:1">>}].
49
50 do_encode({jingle_s5b_candidate, _, _, _, _, _, _} =
51 Candidate,
52 TopXMLNS) ->
53 encode_jingle_s5b_candidate(Candidate, TopXMLNS);
54 do_encode({jingle_s5b_transport, _, _, _, _, _, _, _} =
55 Transport,
56 TopXMLNS) ->
57 encode_jingle_s5b_transport(Transport, TopXMLNS).
58
59 do_get_name({jingle_s5b_candidate, _, _, _, _, _, _}) ->
60 <<"candidate">>;
61 do_get_name({jingle_s5b_transport, _, _, _, _, _, _,
62 _}) ->
63 <<"transport">>.
64
65 do_get_ns({jingle_s5b_candidate, _, _, _, _, _, _}) ->
66 <<"urn:xmpp:jingle:transports:s5b:1">>;
67 do_get_ns({jingle_s5b_transport, _, _, _, _, _, _,
68 _}) ->
69 <<"urn:xmpp:jingle:transports:s5b:1">>.
70
71 pp(jingle_s5b_candidate, 6) ->
72 [cid, host, port, jid, type, priority];
73 pp(jingle_s5b_transport, 7) ->
74 [sid, dstaddr, mode, candidates, 'candidate-used',
75 activated, error];
76 pp(_, _) -> no.
77
78 records() ->
79 [{jingle_s5b_candidate, 6}, {jingle_s5b_transport, 7}].
80
81 dec_enum(Val, Enums) ->
82 AtomVal = erlang:binary_to_existing_atom(Val, utf8),
83 case lists:member(AtomVal, Enums) of
84 true -> AtomVal
85 end.
86
87 dec_int(Val, Min, Max) ->
88 case erlang:binary_to_integer(Val) of
89 Int when Int =< Max, Min == infinity -> Int;
90 Int when Int =< Max, Int >= Min -> Int
91 end.
92
93 dec_ip(S) ->
94 {ok, Addr} = inet_parse:address(binary_to_list(S)),
95 Addr.
96
97 enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
98
99 enc_int(Int) -> erlang:integer_to_binary(Int).
100
101 enc_ip({0, 0, 0, 0, 0, 65535, A, B}) ->
102 enc_ip({(A bsr 8) band 255, A band 255,
103 (B bsr 8) band 255, B band 255});
104 enc_ip(Addr) -> list_to_binary(inet_parse:ntoa(Addr)).
105
106 decode_jingle_s5b_transport(__TopXMLNS, __Opts,
107 {xmlel, <<"transport">>, _attrs, _els}) ->
108 {Error, Candidates, Activated, Candidate_used} =
109 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
110 _els, undefined, [], undefined,
111 undefined),
112 {Sid, Dstaddr, Mode} =
113 decode_jingle_s5b_transport_attrs(__TopXMLNS, _attrs,
114 undefined, undefined, undefined),
115 {jingle_s5b_transport, Sid, Dstaddr, Mode, Candidates,
116 Candidate_used, Activated, Error}.
117
118 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts, [],
119 Error, Candidates, Activated, Candidate_used) ->
120 {Error, lists:reverse(Candidates), Activated,
121 Candidate_used};
122 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
123 [{xmlel, <<"candidate">>, _attrs, _} = _el
124 | _els],
125 Error, Candidates, Activated, Candidate_used) ->
126 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
127 __TopXMLNS)
128 of
129 <<"urn:xmpp:jingle:transports:s5b:1">> ->
130 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
131 _els, Error,
132 [decode_jingle_s5b_candidate(<<"urn:xmpp:jingle:transports:s5b:1">>,
133 __Opts,
134 _el)
135 | Candidates],
136 Activated, Candidate_used);
137 _ ->
138 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
139 _els, Error, Candidates, Activated,
140 Candidate_used)
141 end;
142 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
143 [{xmlel, <<"candidate-used">>, _attrs, _} = _el
144 | _els],
145 Error, Candidates, Activated, Candidate_used) ->
146 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
147 __TopXMLNS)
148 of
149 <<"urn:xmpp:jingle:transports:s5b:1">> ->
150 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
151 _els, Error, Candidates, Activated,
152 decode_jingle_s5b_candidate_used(<<"urn:xmpp:jingle:transports:s5b:1">>,
153 __Opts,
154 _el));
155 _ ->
156 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
157 _els, Error, Candidates, Activated,
158 Candidate_used)
159 end;
160 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
161 [{xmlel, <<"activated">>, _attrs, _} = _el
162 | _els],
163 Error, Candidates, Activated, Candidate_used) ->
164 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
165 __TopXMLNS)
166 of
167 <<"urn:xmpp:jingle:transports:s5b:1">> ->
168 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
169 _els, Error, Candidates,
170 decode_jingle_s5b_activated(<<"urn:xmpp:jingle:transports:s5b:1">>,
171 __Opts,
172 _el),
173 Candidate_used);
174 _ ->
175 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
176 _els, Error, Candidates, Activated,
177 Candidate_used)
178 end;
179 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
180 [{xmlel, <<"candidate-error">>, _attrs, _} = _el
181 | _els],
182 Error, Candidates, Activated, Candidate_used) ->
183 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
184 __TopXMLNS)
185 of
186 <<"urn:xmpp:jingle:transports:s5b:1">> ->
187 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
188 _els,
189 decode_jingle_s5b_candidate_error(<<"urn:xmpp:jingle:transports:s5b:1">>,
190 __Opts,
191 _el),
192 Candidates, Activated,
193 Candidate_used);
194 _ ->
195 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
196 _els, Error, Candidates, Activated,
197 Candidate_used)
198 end;
199 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
200 [{xmlel, <<"proxy-error">>, _attrs, _} = _el
201 | _els],
202 Error, Candidates, Activated, Candidate_used) ->
203 case xmpp_codec:get_attr(<<"xmlns">>, _attrs,
204 __TopXMLNS)
205 of
206 <<"urn:xmpp:jingle:transports:s5b:1">> ->
207 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
208 _els,
209 decode_jingle_s5b_proxy_error(<<"urn:xmpp:jingle:transports:s5b:1">>,
210 __Opts,
211 _el),
212 Candidates, Activated,
213 Candidate_used);
214 _ ->
215 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
216 _els, Error, Candidates, Activated,
217 Candidate_used)
218 end;
219 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
220 [_ | _els], Error, Candidates, Activated,
221 Candidate_used) ->
222 decode_jingle_s5b_transport_els(__TopXMLNS, __Opts,
223 _els, Error, Candidates, Activated,
224 Candidate_used).
225
226 decode_jingle_s5b_transport_attrs(__TopXMLNS,
227 [{<<"sid">>, _val} | _attrs], _Sid, Dstaddr,
228 Mode) ->
229 decode_jingle_s5b_transport_attrs(__TopXMLNS, _attrs,
230 _val, Dstaddr, Mode);
231 decode_jingle_s5b_transport_attrs(__TopXMLNS,
232 [{<<"dstaddr">>, _val} | _attrs], Sid,
233 _Dstaddr, Mode) ->
234 decode_jingle_s5b_transport_attrs(__TopXMLNS, _attrs,
235 Sid, _val, Mode);
236 decode_jingle_s5b_transport_attrs(__TopXMLNS,
237 [{<<"mode">>, _val} | _attrs], Sid, Dstaddr,
238 _Mode) ->
239 decode_jingle_s5b_transport_attrs(__TopXMLNS, _attrs,
240 Sid, Dstaddr, _val);
241 decode_jingle_s5b_transport_attrs(__TopXMLNS,
242 [_ | _attrs], Sid, Dstaddr, Mode) ->
243 decode_jingle_s5b_transport_attrs(__TopXMLNS, _attrs,
244 Sid, Dstaddr, Mode);
245 decode_jingle_s5b_transport_attrs(__TopXMLNS, [], Sid,
246 Dstaddr, Mode) ->
247 {decode_jingle_s5b_transport_attr_sid(__TopXMLNS, Sid),
248 decode_jingle_s5b_transport_attr_dstaddr(__TopXMLNS,
249 Dstaddr),
250 decode_jingle_s5b_transport_attr_mode(__TopXMLNS,
251 Mode)}.
252
253 encode_jingle_s5b_transport({jingle_s5b_transport, Sid,
254 Dstaddr, Mode, Candidates, Candidate_used,
255 Activated, Error},
256 __TopXMLNS) ->
257 __NewTopXMLNS =
258 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
259 [], __TopXMLNS),
260 _els =
261 lists:reverse('encode_jingle_s5b_transport_$error'(Error,
262 __NewTopXMLNS,
263 'encode_jingle_s5b_transport_$candidates'(Candidates,
264 __NewTopXMLNS,
265 'encode_jingle_s5b_transport_$activated'(Activated,
266 __NewTopXMLNS,
267 'encode_jingle_s5b_transport_$candidate-used'(Candidate_used,
268 __NewTopXMLNS,
269 []))))),
270 _attrs = encode_jingle_s5b_transport_attr_mode(Mode,
271 encode_jingle_s5b_transport_attr_dstaddr(Dstaddr,
272 encode_jingle_s5b_transport_attr_sid(Sid,
273 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
274 __TopXMLNS)))),
275 {xmlel, <<"transport">>, _attrs, _els}.
276
277 'encode_jingle_s5b_transport_$error'(undefined,
278 __TopXMLNS, _acc) ->
279 _acc;
280 'encode_jingle_s5b_transport_$error'('candidate-error' =
281 Error,
282 __TopXMLNS, _acc) ->
283 [encode_jingle_s5b_candidate_error(Error, __TopXMLNS)
284 | _acc];
285 'encode_jingle_s5b_transport_$error'('proxy-error' =
286 Error,
287 __TopXMLNS, _acc) ->
288 [encode_jingle_s5b_proxy_error(Error, __TopXMLNS)
289 | _acc].
290
291 'encode_jingle_s5b_transport_$candidates'([],
292 __TopXMLNS, _acc) ->
293 _acc;
294 'encode_jingle_s5b_transport_$candidates'([Candidates
295 | _els],
296 __TopXMLNS, _acc) ->
297 'encode_jingle_s5b_transport_$candidates'(_els,
298 __TopXMLNS,
299 [encode_jingle_s5b_candidate(Candidates,
300 __TopXMLNS)
301 | _acc]).
302
303 'encode_jingle_s5b_transport_$activated'(undefined,
304 __TopXMLNS, _acc) ->
305 _acc;
306 'encode_jingle_s5b_transport_$activated'(Activated,
307 __TopXMLNS, _acc) ->
308 [encode_jingle_s5b_activated(Activated, __TopXMLNS)
309 | _acc].
310
311 'encode_jingle_s5b_transport_$candidate-used'(undefined,
312 __TopXMLNS, _acc) ->
313 _acc;
314 'encode_jingle_s5b_transport_$candidate-used'(Candidate_used,
315 __TopXMLNS, _acc) ->
316 [encode_jingle_s5b_candidate_used(Candidate_used,
317 __TopXMLNS)
318 | _acc].
319
320 decode_jingle_s5b_transport_attr_sid(__TopXMLNS,
321 undefined) ->
322 erlang:error({xmpp_codec,
323 {missing_attr, <<"sid">>, <<"transport">>,
324 __TopXMLNS}});
325 decode_jingle_s5b_transport_attr_sid(__TopXMLNS,
326 _val) ->
327 _val.
328
329 encode_jingle_s5b_transport_attr_sid(_val, _acc) ->
330 [{<<"sid">>, _val} | _acc].
331
332 decode_jingle_s5b_transport_attr_dstaddr(__TopXMLNS,
333 undefined) ->
334 <<>>;
335 decode_jingle_s5b_transport_attr_dstaddr(__TopXMLNS,
336 _val) ->
337 _val.
338
339 encode_jingle_s5b_transport_attr_dstaddr(<<>>, _acc) ->
340 _acc;
341 encode_jingle_s5b_transport_attr_dstaddr(_val, _acc) ->
342 [{<<"dstaddr">>, _val} | _acc].
343
344 decode_jingle_s5b_transport_attr_mode(__TopXMLNS,
345 undefined) ->
346 tcp;
347 decode_jingle_s5b_transport_attr_mode(__TopXMLNS,
348 _val) ->
349 case catch dec_enum(_val, [tcp, udp]) of
350 {'EXIT', _} ->
351 erlang:error({xmpp_codec,
352 {bad_attr_value, <<"mode">>, <<"transport">>,
353 __TopXMLNS}});
354 _res -> _res
355 end.
356
357 encode_jingle_s5b_transport_attr_mode(tcp, _acc) ->
358 _acc;
359 encode_jingle_s5b_transport_attr_mode(_val, _acc) ->
360 [{<<"mode">>, enc_enum(_val)} | _acc].
361
362 decode_jingle_s5b_proxy_error(__TopXMLNS, __Opts,
363 {xmlel, <<"proxy-error">>, _attrs, _els}) ->
364 'proxy-error'.
365
366 encode_jingle_s5b_proxy_error('proxy-error',
367 __TopXMLNS) ->
368 __NewTopXMLNS =
369 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
370 [], __TopXMLNS),
371 _els = [],
372 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
373 __TopXMLNS),
374 {xmlel, <<"proxy-error">>, _attrs, _els}.
375
376 decode_jingle_s5b_candidate_error(__TopXMLNS, __Opts,
377 {xmlel, <<"candidate-error">>, _attrs,
378 _els}) ->
379 'candidate-error'.
380
381 encode_jingle_s5b_candidate_error('candidate-error',
382 __TopXMLNS) ->
383 __NewTopXMLNS =
384 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
385 [], __TopXMLNS),
386 _els = [],
387 _attrs = xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
388 __TopXMLNS),
389 {xmlel, <<"candidate-error">>, _attrs, _els}.
390
391 decode_jingle_s5b_activated(__TopXMLNS, __Opts,
392 {xmlel, <<"activated">>, _attrs, _els}) ->
393 Cid = decode_jingle_s5b_activated_attrs(__TopXMLNS,
394 _attrs, undefined),
395 Cid.
396
397 decode_jingle_s5b_activated_attrs(__TopXMLNS,
398 [{<<"cid">>, _val} | _attrs], _Cid) ->
399 decode_jingle_s5b_activated_attrs(__TopXMLNS, _attrs,
400 _val);
401 decode_jingle_s5b_activated_attrs(__TopXMLNS,
402 [_ | _attrs], Cid) ->
403 decode_jingle_s5b_activated_attrs(__TopXMLNS, _attrs,
404 Cid);
405 decode_jingle_s5b_activated_attrs(__TopXMLNS, [],
406 Cid) ->
407 decode_jingle_s5b_activated_attr_cid(__TopXMLNS, Cid).
408
409 encode_jingle_s5b_activated(Cid, __TopXMLNS) ->
410 __NewTopXMLNS =
411 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
412 [], __TopXMLNS),
413 _els = [],
414 _attrs = encode_jingle_s5b_activated_attr_cid(Cid,
415 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
416 __TopXMLNS)),
417 {xmlel, <<"activated">>, _attrs, _els}.
418
419 decode_jingle_s5b_activated_attr_cid(__TopXMLNS,
420 undefined) ->
421 erlang:error({xmpp_codec,
422 {missing_attr, <<"cid">>, <<"activated">>,
423 __TopXMLNS}});
424 decode_jingle_s5b_activated_attr_cid(__TopXMLNS,
425 _val) ->
426 _val.
427
428 encode_jingle_s5b_activated_attr_cid(_val, _acc) ->
429 [{<<"cid">>, _val} | _acc].
430
431 decode_jingle_s5b_candidate(__TopXMLNS, __Opts,
432 {xmlel, <<"candidate">>, _attrs, _els}) ->
433 {Cid, Host, Jid, Port, Priority, Type} =
434 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
435 undefined, undefined, undefined,
436 undefined, undefined, undefined),
437 {jingle_s5b_candidate, Cid, Host, Port, Jid, Type,
438 Priority}.
439
440 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
441 [{<<"cid">>, _val} | _attrs], _Cid, Host, Jid,
442 Port, Priority, Type) ->
443 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
444 _val, Host, Jid, Port, Priority, Type);
445 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
446 [{<<"host">>, _val} | _attrs], Cid, _Host,
447 Jid, Port, Priority, Type) ->
448 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
449 Cid, _val, Jid, Port, Priority, Type);
450 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
451 [{<<"jid">>, _val} | _attrs], Cid, Host, _Jid,
452 Port, Priority, Type) ->
453 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
454 Cid, Host, _val, Port, Priority, Type);
455 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
456 [{<<"port">>, _val} | _attrs], Cid, Host, Jid,
457 _Port, Priority, Type) ->
458 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
459 Cid, Host, Jid, _val, Priority, Type);
460 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
461 [{<<"priority">>, _val} | _attrs], Cid, Host,
462 Jid, Port, _Priority, Type) ->
463 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
464 Cid, Host, Jid, Port, _val, Type);
465 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
466 [{<<"type">>, _val} | _attrs], Cid, Host, Jid,
467 Port, Priority, _Type) ->
468 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
469 Cid, Host, Jid, Port, Priority, _val);
470 decode_jingle_s5b_candidate_attrs(__TopXMLNS,
471 [_ | _attrs], Cid, Host, Jid, Port, Priority,
472 Type) ->
473 decode_jingle_s5b_candidate_attrs(__TopXMLNS, _attrs,
474 Cid, Host, Jid, Port, Priority, Type);
475 decode_jingle_s5b_candidate_attrs(__TopXMLNS, [], Cid,
476 Host, Jid, Port, Priority, Type) ->
477 {decode_jingle_s5b_candidate_attr_cid(__TopXMLNS, Cid),
478 decode_jingle_s5b_candidate_attr_host(__TopXMLNS, Host),
479 decode_jingle_s5b_candidate_attr_jid(__TopXMLNS, Jid),
480 decode_jingle_s5b_candidate_attr_port(__TopXMLNS, Port),
481 decode_jingle_s5b_candidate_attr_priority(__TopXMLNS,
482 Priority),
483 decode_jingle_s5b_candidate_attr_type(__TopXMLNS,
484 Type)}.
485
486 encode_jingle_s5b_candidate({jingle_s5b_candidate, Cid,
487 Host, Port, Jid, Type, Priority},
488 __TopXMLNS) ->
489 __NewTopXMLNS =
490 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
491 [], __TopXMLNS),
492 _els = [],
493 _attrs = encode_jingle_s5b_candidate_attr_type(Type,
494 encode_jingle_s5b_candidate_attr_priority(Priority,
495 encode_jingle_s5b_candidate_attr_port(Port,
496 encode_jingle_s5b_candidate_attr_jid(Jid,
497 encode_jingle_s5b_candidate_attr_host(Host,
498 encode_jingle_s5b_candidate_attr_cid(Cid,
499 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
500 __TopXMLNS))))))),
501 {xmlel, <<"candidate">>, _attrs, _els}.
502
503 decode_jingle_s5b_candidate_attr_cid(__TopXMLNS,
504 undefined) ->
505 erlang:error({xmpp_codec,
506 {missing_attr, <<"cid">>, <<"candidate">>,
507 __TopXMLNS}});
508 decode_jingle_s5b_candidate_attr_cid(__TopXMLNS,
509 _val) ->
510 _val.
511
512 encode_jingle_s5b_candidate_attr_cid(_val, _acc) ->
513 [{<<"cid">>, _val} | _acc].
514
515 decode_jingle_s5b_candidate_attr_host(__TopXMLNS,
516 undefined) ->
517 erlang:error({xmpp_codec,
518 {missing_attr, <<"host">>, <<"candidate">>,
519 __TopXMLNS}});
520 decode_jingle_s5b_candidate_attr_host(__TopXMLNS,
521 _val) ->
522 case catch dec_ip(_val) of
523 {'EXIT', _} ->
524 erlang:error({xmpp_codec,
525 {bad_attr_value, <<"host">>, <<"candidate">>,
526 __TopXMLNS}});
527 _res -> _res
528 end.
529
530 encode_jingle_s5b_candidate_attr_host(_val, _acc) ->
531 [{<<"host">>, enc_ip(_val)} | _acc].
532
533 decode_jingle_s5b_candidate_attr_jid(__TopXMLNS,
534 undefined) ->
535 erlang:error({xmpp_codec,
536 {missing_attr, <<"jid">>, <<"candidate">>,
537 __TopXMLNS}});
538 decode_jingle_s5b_candidate_attr_jid(__TopXMLNS,
539 _val) ->
540 case catch jid:decode(_val) of
541 {'EXIT', _} ->
542 erlang:error({xmpp_codec,
543 {bad_attr_value, <<"jid">>, <<"candidate">>,
544 __TopXMLNS}});
545 _res -> _res
546 end.
547
548 encode_jingle_s5b_candidate_attr_jid(_val, _acc) ->
549 [{<<"jid">>, jid:encode(_val)} | _acc].
550
551 decode_jingle_s5b_candidate_attr_port(__TopXMLNS,
552 undefined) ->
553 undefined;
554 decode_jingle_s5b_candidate_attr_port(__TopXMLNS,
555 _val) ->
556 case catch dec_int(_val, 0, 65535) of
557 {'EXIT', _} ->
558 erlang:error({xmpp_codec,
559 {bad_attr_value, <<"port">>, <<"candidate">>,
560 __TopXMLNS}});
561 _res -> _res
562 end.
563
564 encode_jingle_s5b_candidate_attr_port(undefined,
565 _acc) ->
566 _acc;
567 encode_jingle_s5b_candidate_attr_port(_val, _acc) ->
568 [{<<"port">>, enc_int(_val)} | _acc].
569
570 decode_jingle_s5b_candidate_attr_priority(__TopXMLNS,
571 undefined) ->
572 erlang:error({xmpp_codec,
573 {missing_attr, <<"priority">>, <<"candidate">>,
574 __TopXMLNS}});
575 decode_jingle_s5b_candidate_attr_priority(__TopXMLNS,
576 _val) ->
577 case catch dec_int(_val, 0, infinity) of
578 {'EXIT', _} ->
579 erlang:error({xmpp_codec,
580 {bad_attr_value, <<"priority">>, <<"candidate">>,
581 __TopXMLNS}});
582 _res -> _res
583 end.
584
585 encode_jingle_s5b_candidate_attr_priority(_val, _acc) ->
586 [{<<"priority">>, enc_int(_val)} | _acc].
587
588 decode_jingle_s5b_candidate_attr_type(__TopXMLNS,
589 undefined) ->
590 direct;
591 decode_jingle_s5b_candidate_attr_type(__TopXMLNS,
592 _val) ->
593 case catch dec_enum(_val,
594 [assisted, direct, proxy, tunnel])
595 of
596 {'EXIT', _} ->
597 erlang:error({xmpp_codec,
598 {bad_attr_value, <<"type">>, <<"candidate">>,
599 __TopXMLNS}});
600 _res -> _res
601 end.
602
603 encode_jingle_s5b_candidate_attr_type(direct, _acc) ->
604 _acc;
605 encode_jingle_s5b_candidate_attr_type(_val, _acc) ->
606 [{<<"type">>, enc_enum(_val)} | _acc].
607
608 decode_jingle_s5b_candidate_used(__TopXMLNS, __Opts,
609 {xmlel, <<"candidate-used">>, _attrs, _els}) ->
610 Cid = decode_jingle_s5b_candidate_used_attrs(__TopXMLNS,
611 _attrs, undefined),
612 Cid.
613
614 decode_jingle_s5b_candidate_used_attrs(__TopXMLNS,
615 [{<<"cid">>, _val} | _attrs], _Cid) ->
616 decode_jingle_s5b_candidate_used_attrs(__TopXMLNS,
617 _attrs, _val);
618 decode_jingle_s5b_candidate_used_attrs(__TopXMLNS,
619 [_ | _attrs], Cid) ->
620 decode_jingle_s5b_candidate_used_attrs(__TopXMLNS,
621 _attrs, Cid);
622 decode_jingle_s5b_candidate_used_attrs(__TopXMLNS, [],
623 Cid) ->
624 decode_jingle_s5b_candidate_used_attr_cid(__TopXMLNS,
625 Cid).
626
627 encode_jingle_s5b_candidate_used(Cid, __TopXMLNS) ->
628 __NewTopXMLNS =
629 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:s5b:1">>,
630 [], __TopXMLNS),
631 _els = [],
632 _attrs = encode_jingle_s5b_candidate_used_attr_cid(Cid,
633 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
634 __TopXMLNS)),
635 {xmlel, <<"candidate-used">>, _attrs, _els}.
636
637 decode_jingle_s5b_candidate_used_attr_cid(__TopXMLNS,
638 undefined) ->
639 erlang:error({xmpp_codec,
640 {missing_attr, <<"cid">>, <<"candidate-used">>,
641 __TopXMLNS}});
642 decode_jingle_s5b_candidate_used_attr_cid(__TopXMLNS,
643 _val) ->
644 _val.
645
646 encode_jingle_s5b_candidate_used_attr_cid(_val, _acc) ->
647 [{<<"cid">>, _val} | _acc].
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0261).
4
5 -compile(export_all).
6
7 do_decode(<<"transport">>,
8 <<"urn:xmpp:jingle:transports:ibb:1">>, El, Opts) ->
9 decode_jingle_ibb_transport(<<"urn:xmpp:jingle:transports:ibb:1">>,
10 Opts, El);
11 do_decode(Name, <<>>, _, _) ->
12 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
13 do_decode(Name, XMLNS, _, _) ->
14 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
15
16 tags() ->
17 [{<<"transport">>,
18 <<"urn:xmpp:jingle:transports:ibb:1">>}].
19
20 do_encode({jingle_ibb_transport, _, _, _} = Transport,
21 TopXMLNS) ->
22 encode_jingle_ibb_transport(Transport, TopXMLNS).
23
24 do_get_name({jingle_ibb_transport, _, _, _}) ->
25 <<"transport">>.
26
27 do_get_ns({jingle_ibb_transport, _, _, _}) ->
28 <<"urn:xmpp:jingle:transports:ibb:1">>.
29
30 pp(jingle_ibb_transport, 3) ->
31 [sid, 'block-size', stanza];
32 pp(_, _) -> no.
33
34 records() -> [{jingle_ibb_transport, 3}].
35
36 dec_enum(Val, Enums) ->
37 AtomVal = erlang:binary_to_existing_atom(Val, utf8),
38 case lists:member(AtomVal, Enums) of
39 true -> AtomVal
40 end.
41
42 dec_int(Val, Min, Max) ->
43 case erlang:binary_to_integer(Val) of
44 Int when Int =< Max, Min == infinity -> Int;
45 Int when Int =< Max, Int >= Min -> Int
46 end.
47
48 enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8).
49
50 enc_int(Int) -> erlang:integer_to_binary(Int).
51
52 decode_jingle_ibb_transport(__TopXMLNS, __Opts,
53 {xmlel, <<"transport">>, _attrs, _els}) ->
54 {Sid, Block_size, Stanza} =
55 decode_jingle_ibb_transport_attrs(__TopXMLNS, _attrs,
56 undefined, undefined, undefined),
57 {jingle_ibb_transport, Sid, Block_size, Stanza}.
58
59 decode_jingle_ibb_transport_attrs(__TopXMLNS,
60 [{<<"sid">>, _val} | _attrs], _Sid,
61 Block_size, Stanza) ->
62 decode_jingle_ibb_transport_attrs(__TopXMLNS, _attrs,
63 _val, Block_size, Stanza);
64 decode_jingle_ibb_transport_attrs(__TopXMLNS,
65 [{<<"block-size">>, _val} | _attrs], Sid,
66 _Block_size, Stanza) ->
67 decode_jingle_ibb_transport_attrs(__TopXMLNS, _attrs,
68 Sid, _val, Stanza);
69 decode_jingle_ibb_transport_attrs(__TopXMLNS,
70 [{<<"stanza">>, _val} | _attrs], Sid,
71 Block_size, _Stanza) ->
72 decode_jingle_ibb_transport_attrs(__TopXMLNS, _attrs,
73 Sid, Block_size, _val);
74 decode_jingle_ibb_transport_attrs(__TopXMLNS,
75 [_ | _attrs], Sid, Block_size, Stanza) ->
76 decode_jingle_ibb_transport_attrs(__TopXMLNS, _attrs,
77 Sid, Block_size, Stanza);
78 decode_jingle_ibb_transport_attrs(__TopXMLNS, [], Sid,
79 Block_size, Stanza) ->
80 {decode_jingle_ibb_transport_attr_sid(__TopXMLNS, Sid),
81 'decode_jingle_ibb_transport_attr_block-size'(__TopXMLNS,
82 Block_size),
83 decode_jingle_ibb_transport_attr_stanza(__TopXMLNS,
84 Stanza)}.
85
86 encode_jingle_ibb_transport({jingle_ibb_transport, Sid,
87 Block_size, Stanza},
88 __TopXMLNS) ->
89 __NewTopXMLNS =
90 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:jingle:transports:ibb:1">>,
91 [], __TopXMLNS),
92 _els = [],
93 _attrs = encode_jingle_ibb_transport_attr_stanza(Stanza,
94 'encode_jingle_ibb_transport_attr_block-size'(Block_size,
95 encode_jingle_ibb_transport_attr_sid(Sid,
96 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
97 __TopXMLNS)))),
98 {xmlel, <<"transport">>, _attrs, _els}.
99
100 decode_jingle_ibb_transport_attr_sid(__TopXMLNS,
101 undefined) ->
102 erlang:error({xmpp_codec,
103 {missing_attr, <<"sid">>, <<"transport">>,
104 __TopXMLNS}});
105 decode_jingle_ibb_transport_attr_sid(__TopXMLNS,
106 _val) ->
107 _val.
108
109 encode_jingle_ibb_transport_attr_sid(_val, _acc) ->
110 [{<<"sid">>, _val} | _acc].
111
112 'decode_jingle_ibb_transport_attr_block-size'(__TopXMLNS,
113 undefined) ->
114 erlang:error({xmpp_codec,
115 {missing_attr, <<"block-size">>, <<"transport">>,
116 __TopXMLNS}});
117 'decode_jingle_ibb_transport_attr_block-size'(__TopXMLNS,
118 _val) ->
119 case catch dec_int(_val, 0, infinity) of
120 {'EXIT', _} ->
121 erlang:error({xmpp_codec,
122 {bad_attr_value, <<"block-size">>, <<"transport">>,
123 __TopXMLNS}});
124 _res -> _res
125 end.
126
127 'encode_jingle_ibb_transport_attr_block-size'(_val,
128 _acc) ->
129 [{<<"block-size">>, enc_int(_val)} | _acc].
130
131 decode_jingle_ibb_transport_attr_stanza(__TopXMLNS,
132 undefined) ->
133 iq;
134 decode_jingle_ibb_transport_attr_stanza(__TopXMLNS,
135 _val) ->
136 case catch dec_enum(_val, [iq, message]) of
137 {'EXIT', _} ->
138 erlang:error({xmpp_codec,
139 {bad_attr_value, <<"stanza">>, <<"transport">>,
140 __TopXMLNS}});
141 _res -> _res
142 end.
143
144 encode_jingle_ibb_transport_attr_stanza(iq, _acc) ->
145 _acc;
146 encode_jingle_ibb_transport_attr_stanza(_val, _acc) ->
147 [{<<"stanza">>, enc_enum(_val)} | _acc].
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0300).
4
5 -compile(export_all).
6
7 do_decode(<<"hash-used">>, <<"urn:xmpp:hashes:2">>, El,
8 Opts) ->
9 decode_hash_used(<<"urn:xmpp:hashes:2">>, Opts, El);
10 do_decode(<<"hash">>, <<"urn:xmpp:hashes:2">>, El,
11 Opts) ->
12 decode_hash(<<"urn:xmpp:hashes:2">>, Opts, El);
13 do_decode(Name, <<>>, _, _) ->
14 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
15 do_decode(Name, XMLNS, _, _) ->
16 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
17
18 tags() ->
19 [{<<"hash-used">>, <<"urn:xmpp:hashes:2">>},
20 {<<"hash">>, <<"urn:xmpp:hashes:2">>}].
21
22 do_encode({hash, _, _} = Hash, TopXMLNS) ->
23 encode_hash(Hash, TopXMLNS);
24 do_encode({hash_used, _} = Hash_used, TopXMLNS) ->
25 encode_hash_used(Hash_used, TopXMLNS).
26
27 do_get_name({hash, _, _}) -> <<"hash">>;
28 do_get_name({hash_used, _}) -> <<"hash-used">>.
29
30 do_get_ns({hash, _, _}) -> <<"urn:xmpp:hashes:2">>;
31 do_get_ns({hash_used, _}) -> <<"urn:xmpp:hashes:2">>.
32
33 pp(hash, 2) -> [algo, data];
34 pp(hash_used, 1) -> [algo];
35 pp(_, _) -> no.
36
37 records() -> [{hash, 2}, {hash_used, 1}].
38
39 decode_hash_used(__TopXMLNS, __Opts,
40 {xmlel, <<"hash-used">>, _attrs, _els}) ->
41 Algo = decode_hash_used_attrs(__TopXMLNS, _attrs,
42 undefined),
43 {hash_used, Algo}.
44
45 decode_hash_used_attrs(__TopXMLNS,
46 [{<<"algo">>, _val} | _attrs], _Algo) ->
47 decode_hash_used_attrs(__TopXMLNS, _attrs, _val);
48 decode_hash_used_attrs(__TopXMLNS, [_ | _attrs],
49 Algo) ->
50 decode_hash_used_attrs(__TopXMLNS, _attrs, Algo);
51 decode_hash_used_attrs(__TopXMLNS, [], Algo) ->
52 decode_hash_used_attr_algo(__TopXMLNS, Algo).
53
54 encode_hash_used({hash_used, Algo}, __TopXMLNS) ->
55 __NewTopXMLNS =
56 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:hashes:2">>, [],
57 __TopXMLNS),
58 _els = [],
59 _attrs = encode_hash_used_attr_algo(Algo,
60 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
61 __TopXMLNS)),
62 {xmlel, <<"hash-used">>, _attrs, _els}.
63
64 decode_hash_used_attr_algo(__TopXMLNS, undefined) ->
65 erlang:error({xmpp_codec,
66 {missing_attr, <<"algo">>, <<"hash-used">>,
67 __TopXMLNS}});
68 decode_hash_used_attr_algo(__TopXMLNS, _val) -> _val.
69
70 encode_hash_used_attr_algo(_val, _acc) ->
71 [{<<"algo">>, _val} | _acc].
72
73 decode_hash(__TopXMLNS, __Opts,
74 {xmlel, <<"hash">>, _attrs, _els}) ->
75 Data = decode_hash_els(__TopXMLNS, __Opts, _els, <<>>),
76 Algo = decode_hash_attrs(__TopXMLNS, _attrs, undefined),
77 {hash, Algo, Data}.
78
79 decode_hash_els(__TopXMLNS, __Opts, [], Data) ->
80 decode_hash_cdata(__TopXMLNS, Data);
81 decode_hash_els(__TopXMLNS, __Opts,
82 [{xmlcdata, _data} | _els], Data) ->
83 decode_hash_els(__TopXMLNS, __Opts, _els,
84 <<Data/binary, _data/binary>>);
85 decode_hash_els(__TopXMLNS, __Opts, [_ | _els], Data) ->
86 decode_hash_els(__TopXMLNS, __Opts, _els, Data).
87
88 decode_hash_attrs(__TopXMLNS,
89 [{<<"algo">>, _val} | _attrs], _Algo) ->
90 decode_hash_attrs(__TopXMLNS, _attrs, _val);
91 decode_hash_attrs(__TopXMLNS, [_ | _attrs], Algo) ->
92 decode_hash_attrs(__TopXMLNS, _attrs, Algo);
93 decode_hash_attrs(__TopXMLNS, [], Algo) ->
94 decode_hash_attr_algo(__TopXMLNS, Algo).
95
96 encode_hash({hash, Algo, Data}, __TopXMLNS) ->
97 __NewTopXMLNS =
98 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:hashes:2">>, [],
99 __TopXMLNS),
100 _els = encode_hash_cdata(Data, []),
101 _attrs = encode_hash_attr_algo(Algo,
102 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
103 __TopXMLNS)),
104 {xmlel, <<"hash">>, _attrs, _els}.
105
106 decode_hash_attr_algo(__TopXMLNS, undefined) ->
107 erlang:error({xmpp_codec,
108 {missing_attr, <<"algo">>, <<"hash">>, __TopXMLNS}});
109 decode_hash_attr_algo(__TopXMLNS, _val) -> _val.
110
111 encode_hash_attr_algo(_val, _acc) ->
112 [{<<"algo">>, _val} | _acc].
113
114 decode_hash_cdata(__TopXMLNS, <<>>) -> <<>>;
115 decode_hash_cdata(__TopXMLNS, _val) ->
116 case catch base64:decode(_val) of
117 {'EXIT', _} ->
118 erlang:error({xmpp_codec,
119 {bad_cdata_value, <<>>, <<"hash">>, __TopXMLNS}});
120 _res -> _res
121 end.
122
123 encode_hash_cdata(<<>>, _acc) -> _acc;
124 encode_hash_cdata(_val, _acc) ->
125 [{xmlcdata, base64:encode(_val)} | _acc].
0 %% Created automatically by XML generator (fxml_gen.erl)
1 %% Source: xmpp_codec.spec
2
3 -module(xep0319).
4
5 -compile(export_all).
6
7 do_decode(<<"idle">>, <<"urn:xmpp:idle:1">>, El,
8 Opts) ->
9 decode_idle(<<"urn:xmpp:idle:1">>, Opts, El);
10 do_decode(Name, <<>>, _, _) ->
11 erlang:error({xmpp_codec, {missing_tag_xmlns, Name}});
12 do_decode(Name, XMLNS, _, _) ->
13 erlang:error({xmpp_codec, {unknown_tag, Name, XMLNS}}).
14
15 tags() -> [{<<"idle">>, <<"urn:xmpp:idle:1">>}].
16
17 do_encode({idle, _} = Idle, TopXMLNS) ->
18 encode_idle(Idle, TopXMLNS).
19
20 do_get_name({idle, _}) -> <<"idle">>.
21
22 do_get_ns({idle, _}) -> <<"urn:xmpp:idle:1">>.
23
24 pp(idle, 1) -> [since];
25 pp(_, _) -> no.
26
27 records() -> [{idle, 1}].
28
29 dec_utc(Val) -> xmpp_util:decode_timestamp(Val).
30
31 enc_utc(Val) -> xmpp_util:encode_timestamp(Val).
32
33 decode_idle(__TopXMLNS, __Opts,
34 {xmlel, <<"idle">>, _attrs, _els}) ->
35 Since = decode_idle_attrs(__TopXMLNS, _attrs,
36 undefined),
37 {idle, Since}.
38
39 decode_idle_attrs(__TopXMLNS,
40 [{<<"since">>, _val} | _attrs], _Since) ->
41 decode_idle_attrs(__TopXMLNS, _attrs, _val);
42 decode_idle_attrs(__TopXMLNS, [_ | _attrs], Since) ->
43 decode_idle_attrs(__TopXMLNS, _attrs, Since);
44 decode_idle_attrs(__TopXMLNS, [], Since) ->
45 decode_idle_attr_since(__TopXMLNS, Since).
46
47 encode_idle({idle, Since}, __TopXMLNS) ->
48 __NewTopXMLNS =
49 xmpp_codec:choose_top_xmlns(<<"urn:xmpp:idle:1">>, [],
50 __TopXMLNS),
51 _els = [],
52 _attrs = encode_idle_attr_since(Since,
53 xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS,
54 __TopXMLNS)),
55 {xmlel, <<"idle">>, _attrs, _els}.
56
57 decode_idle_attr_since(__TopXMLNS, undefined) ->
58 erlang:error({xmpp_codec,
59 {missing_attr, <<"since">>, <<"idle">>, __TopXMLNS}});
60 decode_idle_attr_since(__TopXMLNS, _val) ->
61 case catch dec_utc(_val) of
62 {'EXIT', _} ->
63 erlang:error({xmpp_codec,
64 {bad_attr_value, <<"since">>, <<"idle">>, __TopXMLNS}});
65 _res -> _res
66 end.
67
68 encode_idle_attr_since(_val, _acc) ->
69 [{<<"since">>, enc_utc(_val)} | _acc].
2323
2424 {application, xmpp,
2525 [{description, "Erlang/Elixir XMPP parsing and serialization library"},
26 {vsn, "1.2.2"},
26 {vsn, "1.2.5"},
2727 {modules, []},
2828 {registered, []},
2929 {applications, [kernel, stdlib]},
3131 {env, []},
3232
3333 %% hex.pm packaging:
34 {files, ["src/", "include/", "c_src/jid.c", "c_src/xmpp_uri.c", "c_src/xmpp_lang.c", "rebar.config", "rebar.config.script", "README.md", "LICENSE.txt"]},
34 {files, ["src/", "specs/", "asn1/", "include/", "c_src/jid.c", "c_src/xmpp_uri.c", "c_src/xmpp_lang.c", "rebar.config", "rebar.config.script", "README.md", "LICENSE.txt"]},
3535 {licenses, ["Apache 2.0"]},
3636 {maintainers, ["ProcessOne"]},
3737 {links, [{"Github", "https://github.com/processone/xmpp"}]}]}.
2323
2424 -module(xmpp).
2525 -behaviour(application).
26 -dialyzer({nowarn_function, get_els/1}).
2627
2728 %% API
2829 %% Application callbacks
4243 append_subtags/2, prep_lang/1, register_codec/1, unregister_codec/1,
4344 set_tr_callback/1, format_stanza_error/1, format_stanza_error/2,
4445 format_stream_error/1, format_stream_error/2, format_sasl_error/1,
45 format_sasl_error/2]).
46 format_sasl_error/2, set_config/1, get_config/0]).
4647
4748 %% XMPP errors
4849 -export([err_bad_request/0, err_bad_request/2,
101102 -type reason_text() :: binary() | {io:format(), list()}.
102103 -type lang() :: binary().
103104 -type decode_option() :: ignore_els.
105 -type config_option() :: {debug, boolean()} | {fqdn, [binary()]}.
106
107 -export_type([decode_option/0, config_option/0]).
104108
105109 %%%===================================================================
106110 %%% Application callbacks
109113 try
110114 {ok, _} = application:ensure_all_started(fast_xml),
111115 {ok, _} = application:ensure_all_started(stringprep),
116 {ok, _} = application:ensure_all_started(fast_tls),
117 {ok, _} = application:ensure_all_started(ezlib),
112118 ok = jid:start(),
113119 ok = xmpp_uri:start(),
114120 ok = xmpp_lang:start(),
121 p1_options:start_link(xmpp_config),
122 p1_options:insert(xmpp_config, debug, global, false),
123 p1_options:insert(xmpp_config, fqdn, global, []),
115124 xmpp_sup:start_link()
116125 catch _:{badmatch, Err} ->
117126 Err
570579 {module, xmpp_tr} = code:load_binary(xmpp_tr, "nofile", Code),
571580 ok.
572581
582 -spec set_config([config_option()]) -> ok.
583 set_config(Options) ->
584 lists:foreach(
585 fun({debug, Bool}) when is_boolean(Bool) ->
586 p1_options:insert(xmpp_config, debug, global, Bool);
587 ({fqdn, Domains}) ->
588 case lists:all(fun erlang:is_binary/1, Domains) of
589 true ->
590 p1_options:insert(xmpp_config, fqdn, global, Domains);
591 false ->
592 erlang:error(badarg)
593 end;
594 (_) ->
595 erlang:error(badarg)
596 end, Options),
597 p1_options:compile(xmpp_config).
598
599 -spec get_config() -> [config_option()].
600 get_config() ->
601 {ok, Debug} = xmpp_config:debug(global),
602 {ok, Domains} = xmpp_config:fqdn(global),
603 [{debug, Debug}, {fqdn, Domains}].
604
573605 %%%===================================================================
574606 %%% Functions to construct general XMPP errors
575607 %%%===================================================================
224224 get_mod(<<"x">>, <<"jabber:x:conference">>) -> xep0249;
225225 get_mod(<<"jid">>, <<"urn:xmpp:mam:1">>) -> xep0313;
226226 get_mod(<<"never">>, <<"urn:xmpp:mam:0">>) -> xep0313;
227 get_mod(<<"tie-break">>,
228 <<"urn:xmpp:jingle:errors:1">>) ->
229 xep0166;
227230 get_mod(<<"item">>,
228231 <<"http://jabber.org/protocol/pubsub">>) ->
229232 xep0060;
270273 get_mod(<<"item">>,
271274 <<"http://jabber.org/protocol/muc#admin">>) ->
272275 xep0045;
276 get_mod(<<"gone">>, <<"urn:xmpp:jingle:1">>) -> xep0166;
277 get_mod(<<"range">>,
278 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
279 xep0234;
273280 get_mod(<<"error">>, <<"jabber:component:accept">>) ->
274281 rfc6120;
275282 get_mod(<<"password">>, <<"jabber:iq:auth">>) ->
277284 get_mod(<<"TZ">>, <<"vcard-temp">>) -> xep0054;
278285 get_mod(<<"request">>, <<"urn:xmpp:http:upload">>) ->
279286 xep0363;
287 get_mod(<<"timeout">>, <<"urn:xmpp:jingle:1">>) ->
288 xep0166;
289 get_mod(<<"content">>, <<"urn:xmpp:jingle:1">>) ->
290 xep0166;
280291 get_mod(<<"nick">>, <<"storage:bookmarks">>) -> xep0048;
281292 get_mod(<<"iq">>, <<"jabber:component:accept">>) ->
282293 rfc6120;
288299 xep0045;
289300 get_mod(<<"thumbnail">>, <<"urn:xmpp:thumbs:1">>) ->
290301 xep0264;
302 get_mod(<<"security-required">>,
303 <<"urn:xmpp:jingle:errors:1">>) ->
304 xep0166;
291305 get_mod(<<"url">>, <<"jabber:iq:register">>) -> xep0077;
292306 get_mod(<<"GIVEN">>, <<"vcard-temp">>) -> xep0054;
293307 get_mod(<<"TYPE">>, <<"vcard-temp">>) -> xep0054;
301315 xep0045;
302316 get_mod(<<"media">>, <<"urn:xmpp:media-element">>) ->
303317 xep0221;
318 get_mod(<<"out-of-order">>,
319 <<"urn:xmpp:jingle:errors:1">>) ->
320 xep0166;
304321 get_mod(<<"KEYWORD">>, <<"vcard-temp">>) -> xep0054;
305322 get_mod(<<"result">>, <<"urn:xmpp:mam:tmp">>) ->
306323 xep0313;
307324 get_mod(<<"active">>, <<"urn:xmpp:csi:0">>) -> xep0352;
308325 get_mod(<<"slot">>, <<"urn:xmpp:http:upload">>) ->
309326 xep0363;
327 get_mod(<<"data">>,
328 <<"http://jabber.org/protocol/ibb">>) ->
329 xep0047;
310330 get_mod(<<"query">>, <<"jabber:iq:roster">>) -> rfc6121;
311331 get_mod(<<"digest">>, <<"jabber:iq:auth">>) -> xep0078;
312332 get_mod(<<"NUMBER">>, <<"vcard-temp">>) -> xep0054;
345365 get_mod(<<"received">>, <<"urn:xmpp:carbons:2">>) ->
346366 xep0280;
347367 get_mod(<<"x">>, <<"jabber:x:event">>) -> xep0022;
368 get_mod(<<"general-error">>, <<"urn:xmpp:jingle:1">>) ->
369 xep0166;
348370 get_mod(<<"nick">>, <<"jabber:iq:register">>) ->
349371 xep0077;
350372 get_mod(<<"internal-server-error">>,
388410 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
389411 rfc6120;
390412 get_mod(<<"r">>, <<"urn:xmpp:sm:3">>) -> xep0198;
413 get_mod(<<"unsupported-info">>,
414 <<"urn:xmpp:jingle:errors:1">>) ->
415 xep0166;
391416 get_mod(<<"query">>, <<"jabber:iq:version">>) ->
392417 xep0092;
393418 get_mod(<<"archived">>, <<"urn:xmpp:mam:tmp">>) ->
419444 get_mod(<<"instructions">>, <<"jabber:x:data">>) ->
420445 xep0004;
421446 get_mod(<<"join">>, <<"urn:xmpp:mix:0">>) -> xep0369;
447 get_mod(<<"idle">>, <<"urn:xmpp:idle:1">>) -> xep0319;
422448 get_mod(<<"retract">>,
423449 <<"http://jabber.org/protocol/pubsub#event">>) ->
424450 xep0060;
441467 xep0077;
442468 get_mod(<<"query">>, <<"urn:xmpp:delegation:1">>) ->
443469 xep0355;
470 get_mod(<<"hash">>, <<"urn:xmpp:hashes:2">>) -> xep0300;
444471 get_mod(<<"invalid-options">>,
445472 <<"http://jabber.org/protocol/pubsub#errors">>) ->
446473 xep0060;
477504 get_mod(<<"no-permanent-storage">>,
478505 <<"urn:xmpp:hints">>) ->
479506 xep0334;
507 get_mod(<<"unsupported-applications">>,
508 <<"urn:xmpp:jingle:1">>) ->
509 xep0166;
480510 get_mod(<<"text">>, <<"urn:xmpp:reporting:0">>) ->
481511 xep0377;
482512 get_mod(<<"credentials-expired">>,
491521 get_mod(<<"address">>,
492522 <<"http://jabber.org/protocol/address">>) ->
493523 xep0033;
524 get_mod(<<"unsupported-transports">>,
525 <<"urn:xmpp:jingle:1">>) ->
526 xep0166;
527 get_mod(<<"file-not-available">>,
528 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>) ->
529 xep0234;
494530 get_mod(<<"c">>,
495531 <<"http://jabber.org/protocol/caps">>) ->
496532 xep0115;
524560 get_mod(<<"stream:stream">>, <<"jabber:client">>) ->
525561 rfc6120;
526562 get_mod(<<"data">>, <<"urn:xmpp:bob">>) -> xep0231;
563 get_mod(<<"success">>, <<"urn:xmpp:jingle:1">>) ->
564 xep0166;
527565 get_mod(<<"method">>,
528566 <<"http://jabber.org/protocol/compress">>) ->
529567 xep0138;
530568 get_mod(<<"x">>, <<"jabber:x:data">>) -> xep0004;
569 get_mod(<<"transport">>,
570 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
571 xep0260;
531572 get_mod(<<"CONFIDENTIAL">>, <<"vcard-temp">>) ->
532573 xep0054;
533574 get_mod(<<"subscribe">>,
534575 <<"http://jabber.org/protocol/pubsub">>) ->
535576 xep0060;
577 get_mod(<<"media-type">>,
578 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
579 xep0234;
536580 get_mod(<<"stream:features">>, <<"jabber:client">>) ->
537581 rfc6120;
538582 get_mod(<<"fin">>, <<"urn:xmpp:mam:1">>) -> xep0313;
549593 get_mod(<<"errors">>,
550594 <<"urn:xmpp:features:dialback">>) ->
551595 xep0220;
596 get_mod(<<"media-error">>, <<"urn:xmpp:jingle:1">>) ->
597 xep0166;
598 get_mod(<<"name">>,
599 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
600 xep0234;
601 get_mod(<<"description">>,
602 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
603 xep0234;
604 get_mod(<<"checksum">>,
605 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
606 xep0234;
552607 get_mod(<<"host-gone">>,
553608 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
554609 rfc6120;
560615 get_mod(<<"not-subscribed">>,
561616 <<"http://jabber.org/protocol/pubsub#errors">>) ->
562617 xep0060;
618 get_mod(<<"size">>,
619 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
620 xep0234;
563621 get_mod(<<"query">>,
564622 <<"http://jabber.org/protocol/stats">>) ->
565623 xep0039;
599657 get_mod(<<"a">>, <<"urn:xmpp:sm:3">>) -> xep0198;
600658 get_mod(<<"info">>, <<"urn:xmpp:avatar:metadata">>) ->
601659 xep0084;
660 get_mod(<<"date">>,
661 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
662 xep0234;
602663 get_mod(<<"not-allowed">>,
603664 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
604665 rfc6120;
615676 get_mod(<<"file-too-large">>,
616677 <<"eu:siacs:conversations:http:upload">>) ->
617678 xep0363;
679 get_mod(<<"received">>,
680 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
681 xep0234;
618682 get_mod(<<"JABBERID">>, <<"vcard-temp">>) -> xep0054;
619683 get_mod(<<"subscription">>,
620684 <<"http://jabber.org/protocol/pubsub#event">>) ->
621685 xep0060;
686 get_mod(<<"decline">>, <<"urn:xmpp:jingle:1">>) ->
687 xep0166;
622688 get_mod(<<"key">>, <<"jabber:iq:register">>) -> xep0077;
623689 get_mod(<<"unsupported">>,
624690 <<"http://jabber.org/protocol/pubsub#errors">>) ->
646712 get_mod(<<"max-file-size">>,
647713 <<"urn:xmpp:http:upload">>) ->
648714 xep0363;
715 get_mod(<<"unknown-session">>,
716 <<"urn:xmpp:jingle:errors:1">>) ->
717 xep0166;
718 get_mod(<<"candidate-used">>,
719 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
720 xep0260;
649721 get_mod(<<"not-well-formed">>,
650722 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
651723 rfc6120;
654726 xep0363;
655727 get_mod(<<"perm">>, <<"urn:xmpp:privilege:1">>) ->
656728 xep0356;
729 get_mod(<<"close">>,
730 <<"http://jabber.org/protocol/ibb">>) ->
731 xep0047;
657732 get_mod(<<"internal-server-error">>,
658733 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
659734 rfc6120;
668743 get_mod(<<"metadata">>,
669744 <<"urn:xmpp:avatar:metadata">>) ->
670745 xep0084;
671 get_mod(<<"bad-format">>,
672 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
673 rfc6120;
674 get_mod(<<"STREET">>, <<"vcard-temp">>) -> xep0054;
675 get_mod(<<"status">>,
676 <<"http://jabber.org/protocol/muc#user">>) ->
677 xep0045;
678746 get_mod(<<"password">>,
679747 <<"http://jabber.org/protocol/muc#owner">>) ->
680748 xep0045;
681 get_mod(<<"error">>, <<"jabber:server">>) -> rfc6120;
682749 get_mod(<<"method">>,
683750 <<"http://jabber.org/features/compress">>) ->
684751 xep0138;
685 get_mod(<<"VIDEO">>, <<"vcard-temp">>) -> xep0054;
686 get_mod(<<"redirect">>,
687 <<"http://jabber.org/protocol/pubsub#event">>) ->
688 xep0060;
689752 get_mod(<<"invalid-jid">>,
690753 <<"http://jabber.org/protocol/pubsub#errors">>) ->
691754 xep0060;
692 get_mod(<<"destroy">>,
693 <<"http://jabber.org/protocol/muc#user">>) ->
694 xep0045;
695 get_mod(<<"id">>, <<"jabber:x:event">>) -> xep0022;
696755 get_mod(<<"iq">>, <<"jabber:client">>) -> rfc6120;
697 get_mod(<<"not-acceptable">>,
698 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
699 rfc6120;
700 get_mod(<<"SUFFIX">>, <<"vcard-temp">>) -> xep0054;
701756 get_mod(<<"LOCALITY">>, <<"vcard-temp">>) -> xep0054;
702 get_mod(<<"redirect">>,
703 <<"http://jabber.org/protocol/pubsub">>) ->
704 xep0060;
705757 get_mod(<<"inactive">>,
706758 <<"http://jabber.org/protocol/chatstates">>) ->
707759 xep0085;
708 get_mod(<<"subject">>, <<"jabber:server">>) -> rfc6120;
709 get_mod(<<"bad-namespace-prefix">>,
710 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
711 rfc6120;
712 get_mod(<<"EXTADD">>, <<"vcard-temp">>) -> xep0054;
713 get_mod(<<"affiliation">>,
714 <<"http://jabber.org/protocol/pubsub#owner">>) ->
715 xep0060;
716760 get_mod(<<"affiliations">>,
717761 <<"http://jabber.org/protocol/pubsub#owner">>) ->
718762 xep0060;
719763 get_mod(<<"first">>,
720764 <<"http://jabber.org/protocol/rsm">>) ->
721765 xep0059;
722 get_mod(<<"fin">>, <<"urn:xmpp:mam:0">>) -> xep0313;
723 get_mod(<<"enabled">>, <<"urn:xmpp:sm:2">>) -> xep0198;
724766 get_mod(<<"get">>,
725767 <<"eu:siacs:conversations:http:upload">>) ->
726768 xep0363;
769 get_mod(<<"failed-application">>,
770 <<"urn:xmpp:jingle:1">>) ->
771 xep0166;
727772 get_mod(<<"priority">>, <<"jabber:server">>) -> rfc6120;
728773 get_mod(<<"redirect">>,
729774 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
731776 get_mod(<<"encryption-required">>,
732777 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
733778 rfc6120;
734 get_mod(<<"TITLE">>, <<"vcard-temp">>) -> xep0054;
735 get_mod(<<"too-many-subscriptions">>,
736 <<"http://jabber.org/protocol/pubsub#errors">>) ->
737 xep0060;
738 get_mod(<<"item">>,
739 <<"http://jabber.org/protocol/muc#owner">>) ->
740 xep0045;
741 get_mod(<<"always">>, <<"urn:xmpp:mam:0">>) -> xep0313;
742779 get_mod(<<"stanza-id">>, <<"urn:xmpp:sid:0">>) ->
743780 xep0359;
744781 get_mod(<<"complete">>,
745782 <<"http://jabber.org/protocol/commands">>) ->
746783 xep0050;
747 get_mod(<<"file-too-large">>,
748 <<"urn:xmpp:http:upload:0">>) ->
749 xep0363;
750784 get_mod(<<"enable">>, <<"urn:xmpp:push:0">>) -> xep0357;
751785 get_mod(<<"time">>, <<"urn:xmpp:time">>) -> xep0202;
752 get_mod(<<"conflict">>,
753 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
754 rfc6120;
755786 get_mod(<<"invalid-namespace">>,
756787 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
757788 rfc6120;
758 get_mod(<<"X400">>, <<"vcard-temp">>) -> xep0054;
759789 get_mod(<<"PREFIX">>, <<"vcard-temp">>) -> xep0054;
760 get_mod(<<"affiliation">>,
761 <<"http://jabber.org/protocol/pubsub">>) ->
762 xep0060;
763790 get_mod(<<"affiliations">>,
764791 <<"http://jabber.org/protocol/pubsub">>) ->
765792 xep0060;
766 get_mod(<<"data">>, <<"urn:xmpp:avatar:data">>) ->
767 xep0084;
768 get_mod(<<"item">>,
769 <<"http://jabber.org/protocol/disco#items">>) ->
770 xep0030;
793 get_mod(<<"desc">>,
794 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
795 xep0234;
771796 get_mod(<<"aborted">>,
772797 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
773798 rfc6120;
774799 get_mod(<<"address">>, <<"jabber:iq:register">>) ->
775800 xep0077;
776 get_mod(<<"VOICE">>, <<"vcard-temp">>) -> xep0054;
777801 get_mod(<<"LOGO">>, <<"vcard-temp">>) -> xep0054;
778 get_mod(<<"vCard">>, <<"vcard-temp">>) -> xep0054;
779802 get_mod(<<"retract">>,
780803 <<"http://jabber.org/protocol/pubsub">>) ->
781804 xep0060;
782 get_mod(<<"gone">>,
783 <<"http://jabber.org/protocol/chatstates">>) ->
784 xep0085;
785805 get_mod(<<"destroy">>,
786806 <<"http://jabber.org/protocol/muc#owner">>) ->
787807 xep0045;
788 get_mod(<<"query">>,
789 <<"http://jabber.org/protocol/muc#owner">>) ->
790 xep0045;
791 get_mod(<<"os">>, <<"jabber:iq:version">>) -> xep0092;
792 get_mod(<<"feature">>,
793 <<"http://jabber.org/protocol/disco#info">>) ->
794 xep0030;
795808 get_mod(<<"compress">>,
796809 <<"http://jabber.org/protocol/compress">>) ->
797810 xep0138;
798 get_mod(<<"ORGNAME">>, <<"vcard-temp">>) -> xep0054;
799 get_mod(<<"NOTE">>, <<"vcard-temp">>) -> xep0054;
800 get_mod(<<"REV">>, <<"vcard-temp">>) -> xep0054;
801811 get_mod(<<"ADR">>, <<"vcard-temp">>) -> xep0054;
802812 get_mod(<<"continue">>,
803813 <<"http://jabber.org/protocol/muc#user">>) ->
804814 xep0045;
805815 get_mod(<<"subscription">>, <<"urn:xmpp:mucsub:0">>) ->
806816 p1_mucsub;
807 get_mod(<<"query">>, <<"urn:xmpp:mam:1">>) -> xep0313;
808817 get_mod(<<"item">>, <<"jabber:iq:search">>) -> xep0055;
809818 get_mod(<<"stream:stream">>, <<"jabber:server">>) ->
810819 rfc6120;
811 get_mod(<<"remote-server-not-found">>,
812 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
813 rfc6120;
814 get_mod(<<"invalid-mechanism">>,
815 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
816 rfc6120;
817 get_mod(<<"starttls">>,
818 <<"urn:ietf:params:xml:ns:xmpp-tls">>) ->
819 rfc6120;
820820 get_mod(<<"proceed">>,
821821 <<"urn:ietf:params:xml:ns:xmpp-tls">>) ->
822822 rfc6120;
825825 get_mod(<<"undefined-condition">>,
826826 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
827827 rfc6120;
828 get_mod(<<"unsupported-feature">>,
829 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
830 rfc6120;
831828 get_mod(<<"DESC">>, <<"vcard-temp">>) -> xep0054;
832 get_mod(<<"request">>, <<"urn:xmpp:receipts">>) ->
833 xep0184;
834 get_mod(<<"url">>, <<"storage:bookmarks">>) -> xep0048;
835 get_mod(<<"unexpected-request">>,
836 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
837 rfc6120;
838 get_mod(<<"zip">>, <<"jabber:iq:register">>) -> xep0077;
839 get_mod(<<"delete">>,
840 <<"http://jabber.org/protocol/pubsub#owner">>) ->
841 xep0060;
842829 get_mod(<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>) ->
843830 p1_mucsub;
844831 get_mod(<<"resumed">>, <<"urn:xmpp:sm:2">>) -> xep0198;
845 get_mod(<<"a">>, <<"urn:xmpp:sm:2">>) -> xep0198;
846 get_mod(<<"dialback">>,
847 <<"urn:xmpp:features:dialback">>) ->
848 xep0220;
849 get_mod(<<"put">>, <<"urn:xmpp:http:upload">>) ->
850 xep0363;
851832 get_mod(<<"slot">>, <<"urn:xmpp:http:upload:0">>) ->
852833 xep0363;
834 get_mod(<<"open">>,
835 <<"http://jabber.org/protocol/ibb">>) ->
836 xep0047;
837 get_mod(<<"connectivity-error">>,
838 <<"urn:xmpp:jingle:1">>) ->
839 xep0166;
853840 get_mod(<<"query">>, <<"jabber:iq:privacy">>) ->
854841 xep0016;
855842 get_mod(<<"forbidden">>,
866853 get_mod(<<"request">>,
867854 <<"eu:siacs:conversations:http:upload">>) ->
868855 xep0363;
856 get_mod(<<"file">>,
857 <<"urn:xmpp:jingle:apps:file-transfer:5">>) ->
858 xep0234;
869859 get_mod(<<"query">>, <<"jabber:iq:auth">>) -> xep0078;
870860 get_mod(<<"username">>, <<"jabber:iq:register">>) ->
871861 xep0077;
890880 <<"http://jabber.org/protocol/rsm">>) ->
891881 xep0059;
892882 get_mod(<<"ip">>, <<"urn:xmpp:sic:0">>) -> xep0279;
883 get_mod(<<"file-too-large">>,
884 <<"urn:xmpp:jingle:apps:file-transfer:errors:0">>) ->
885 xep0234;
893886 get_mod(<<"active">>, <<"jabber:iq:privacy">>) ->
894887 xep0016;
895888 get_mod(<<"text">>,
917910 get_mod(<<"max-file-size">>,
918911 <<"urn:xmpp:http:upload:0">>) ->
919912 xep0363;
913 get_mod(<<"cancel">>, <<"urn:xmpp:jingle:1">>) ->
914 xep0166;
920915 get_mod(<<"error">>,
921916 <<"http://jabber.org/protocol/stats">>) ->
922917 xep0039;
936931 xep0357;
937932 get_mod(<<"delegated">>, <<"urn:xmpp:delegation:1">>) ->
938933 xep0355;
934 get_mod(<<"hash-used">>, <<"urn:xmpp:hashes:2">>) ->
935 xep0300;
936 get_mod(<<"security-error">>,
937 <<"urn:xmpp:jingle:1">>) ->
938 xep0166;
939939 get_mod(<<"presence">>,
940940 <<"jabber:component:accept">>) ->
941941 rfc6120;
976976 get_mod(<<"fetch">>,
977977 <<"http://jabber.org/protocol/offline">>) ->
978978 xep0013;
979 get_mod(<<"failed-transport">>,
980 <<"urn:xmpp:jingle:1">>) ->
981 xep0166;
979982 get_mod(<<"abuse">>, <<"urn:xmpp:reporting:0">>) ->
980983 xep0377;
981984 get_mod(<<"state">>, <<"jabber:iq:register">>) ->
11811184 get_mod(<<"subject">>, <<"jabber:client">>) -> rfc6120;
11821185 get_mod(<<"date">>, <<"jabber:iq:register">>) ->
11831186 xep0077;
1187 get_mod(<<"transport">>,
1188 <<"urn:xmpp:jingle:transports:ibb:1">>) ->
1189 xep0261;
11841190 get_mod(<<"auth">>,
11851191 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
11861192 rfc6120;
12031209 get_mod(<<"reason">>,
12041210 <<"http://jabber.org/protocol/muc#owner">>) ->
12051211 xep0045;
1212 get_mod(<<"candidate-error">>,
1213 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
1214 xep0260;
12061215 get_mod(<<"password">>, <<"storage:bookmarks">>) ->
12071216 xep0048;
12081217 get_mod(<<"message">>, <<"jabber:component:accept">>) ->
12161225 <<"http://jabber.org/protocol/muc#user">>) ->
12171226 xep0045;
12181227 get_mod(<<"never">>, <<"urn:xmpp:mam:2">>) -> xep0313;
1228 get_mod(<<"proxy-error">>,
1229 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
1230 xep0260;
12191231 get_mod(<<"stat">>,
12201232 <<"http://jabber.org/protocol/stats">>) ->
12211233 xep0039;
12221234 get_mod(<<"always">>, <<"urn:xmpp:mam:tmp">>) ->
12231235 xep0313;
12241236 get_mod(<<"desc">>, <<"jabber:x:oob">>) -> xep0066;
1237 get_mod(<<"reason">>, <<"urn:xmpp:jingle:1">>) ->
1238 xep0166;
12251239 get_mod(<<"thread">>, <<"jabber:server">>) -> rfc6120;
12261240 get_mod(<<"host-unknown">>,
12271241 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
12721286 get_mod(<<"delegation">>,
12731287 <<"urn:xmpp:delegation:1">>) ->
12741288 xep0355;
1289 get_mod(<<"expired">>, <<"urn:xmpp:jingle:1">>) ->
1290 xep0166;
12751291 get_mod(<<"bad-request">>,
12761292 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
12771293 rfc6120;
12831299 xep0280;
12841300 get_mod(<<"db:result">>, <<"jabber:server">>) ->
12851301 xep0220;
1302 get_mod(<<"busy">>, <<"urn:xmpp:jingle:1">>) -> xep0166;
1303 get_mod(<<"text">>, <<"urn:xmpp:jingle:1">>) -> xep0166;
12861304 get_mod(<<"group">>, <<"jabber:iq:roster">>) -> rfc6121;
12871305 get_mod(<<"incorrect-encoding">>,
12881306 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
12981316 <<"http://jabber.org/protocol/pubsub#event">>) ->
12991317 xep0060;
13001318 get_mod(<<"resumed">>, <<"urn:xmpp:sm:3">>) -> xep0198;
1319 get_mod(<<"activated">>,
1320 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
1321 xep0260;
13011322 get_mod(<<"conference">>, <<"storage:bookmarks">>) ->
13021323 xep0048;
13031324 get_mod(<<"registered">>, <<"jabber:iq:register">>) ->
13571378 get_mod(<<"default">>,
13581379 <<"http://jabber.org/protocol/pubsub">>) ->
13591380 xep0060;
1381 get_mod(<<"alternative-session">>,
1382 <<"urn:xmpp:jingle:1">>) ->
1383 xep0166;
13601384 get_mod(<<"remote-server-timeout">>,
13611385 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
13621386 rfc6120;
13661390 get_mod(<<"items">>,
13671391 <<"http://jabber.org/protocol/pubsub">>) ->
13681392 xep0060;
1393 get_mod(<<"bad-format">>,
1394 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
1395 rfc6120;
1396 get_mod(<<"STREET">>, <<"vcard-temp">>) -> xep0054;
1397 get_mod(<<"status">>,
1398 <<"http://jabber.org/protocol/muc#user">>) ->
1399 xep0045;
1400 get_mod(<<"error">>, <<"jabber:server">>) -> rfc6120;
1401 get_mod(<<"VIDEO">>, <<"vcard-temp">>) -> xep0054;
1402 get_mod(<<"redirect">>,
1403 <<"http://jabber.org/protocol/pubsub#event">>) ->
1404 xep0060;
1405 get_mod(<<"destroy">>,
1406 <<"http://jabber.org/protocol/muc#user">>) ->
1407 xep0045;
1408 get_mod(<<"id">>, <<"jabber:x:event">>) -> xep0022;
1409 get_mod(<<"not-acceptable">>,
1410 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
1411 rfc6120;
1412 get_mod(<<"SUFFIX">>, <<"vcard-temp">>) -> xep0054;
1413 get_mod(<<"redirect">>,
1414 <<"http://jabber.org/protocol/pubsub">>) ->
1415 xep0060;
1416 get_mod(<<"incompatible-parameters">>,
1417 <<"urn:xmpp:jingle:1">>) ->
1418 xep0166;
1419 get_mod(<<"candidate">>,
1420 <<"urn:xmpp:jingle:transports:s5b:1">>) ->
1421 xep0260;
1422 get_mod(<<"subject">>, <<"jabber:server">>) -> rfc6120;
1423 get_mod(<<"bad-namespace-prefix">>,
1424 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
1425 rfc6120;
1426 get_mod(<<"EXTADD">>, <<"vcard-temp">>) -> xep0054;
1427 get_mod(<<"affiliation">>,
1428 <<"http://jabber.org/protocol/pubsub#owner">>) ->
1429 xep0060;
1430 get_mod(<<"fin">>, <<"urn:xmpp:mam:0">>) -> xep0313;
1431 get_mod(<<"enabled">>, <<"urn:xmpp:sm:2">>) -> xep0198;
1432 get_mod(<<"TITLE">>, <<"vcard-temp">>) -> xep0054;
1433 get_mod(<<"too-many-subscriptions">>,
1434 <<"http://jabber.org/protocol/pubsub#errors">>) ->
1435 xep0060;
1436 get_mod(<<"item">>,
1437 <<"http://jabber.org/protocol/muc#owner">>) ->
1438 xep0045;
1439 get_mod(<<"always">>, <<"urn:xmpp:mam:0">>) -> xep0313;
1440 get_mod(<<"file-too-large">>,
1441 <<"urn:xmpp:http:upload:0">>) ->
1442 xep0363;
1443 get_mod(<<"jingle">>, <<"urn:xmpp:jingle:1">>) ->
1444 xep0166;
1445 get_mod(<<"conflict">>,
1446 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
1447 rfc6120;
1448 get_mod(<<"X400">>, <<"vcard-temp">>) -> xep0054;
1449 get_mod(<<"affiliation">>,
1450 <<"http://jabber.org/protocol/pubsub">>) ->
1451 xep0060;
1452 get_mod(<<"data">>, <<"urn:xmpp:avatar:data">>) ->
1453 xep0084;
1454 get_mod(<<"item">>,
1455 <<"http://jabber.org/protocol/disco#items">>) ->
1456 xep0030;
1457 get_mod(<<"VOICE">>, <<"vcard-temp">>) -> xep0054;
1458 get_mod(<<"vCard">>, <<"vcard-temp">>) -> xep0054;
1459 get_mod(<<"gone">>,
1460 <<"http://jabber.org/protocol/chatstates">>) ->
1461 xep0085;
1462 get_mod(<<"query">>,
1463 <<"http://jabber.org/protocol/muc#owner">>) ->
1464 xep0045;
1465 get_mod(<<"os">>, <<"jabber:iq:version">>) -> xep0092;
1466 get_mod(<<"feature">>,
1467 <<"http://jabber.org/protocol/disco#info">>) ->
1468 xep0030;
1469 get_mod(<<"ORGNAME">>, <<"vcard-temp">>) -> xep0054;
1470 get_mod(<<"NOTE">>, <<"vcard-temp">>) -> xep0054;
1471 get_mod(<<"REV">>, <<"vcard-temp">>) -> xep0054;
1472 get_mod(<<"query">>, <<"urn:xmpp:mam:1">>) -> xep0313;
1473 get_mod(<<"remote-server-not-found">>,
1474 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
1475 rfc6120;
1476 get_mod(<<"invalid-mechanism">>,
1477 <<"urn:ietf:params:xml:ns:xmpp-sasl">>) ->
1478 rfc6120;
1479 get_mod(<<"starttls">>,
1480 <<"urn:ietf:params:xml:ns:xmpp-tls">>) ->
1481 rfc6120;
1482 get_mod(<<"unsupported-feature">>,
1483 <<"urn:ietf:params:xml:ns:xmpp-streams">>) ->
1484 rfc6120;
1485 get_mod(<<"request">>, <<"urn:xmpp:receipts">>) ->
1486 xep0184;
1487 get_mod(<<"url">>, <<"storage:bookmarks">>) -> xep0048;
1488 get_mod(<<"unexpected-request">>,
1489 <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) ->
1490 rfc6120;
1491 get_mod(<<"zip">>, <<"jabber:iq:register">>) -> xep0077;
1492 get_mod(<<"delete">>,
1493 <<"http://jabber.org/protocol/pubsub#owner">>) ->
1494 xep0060;
1495 get_mod(<<"a">>, <<"urn:xmpp:sm:2">>) -> xep0198;
1496 get_mod(<<"dialback">>,
1497 <<"urn:xmpp:features:dialback">>) ->
1498 xep0220;
1499 get_mod(<<"put">>, <<"urn:xmpp:http:upload">>) ->
1500 xep0363;
13691501 get_mod(Name, XMLNS) ->
13701502 xmpp_codec_external:lookup(Name, XMLNS).
13711503
13761508 get_mod({compression, _}) -> xep0138;
13771509 get_mod({mam_prefs, _, _, _, _}) -> xep0313;
13781510 get_mod({upload_file_too_large, _, _}) -> xep0363;
1511 get_mod({jingle_ft_description, _}) -> xep0234;
13791512 get_mod({muc_user, _, _, _, _, _, _}) -> xep0045;
13801513 get_mod({bytestreams, _, _, _, _, _, _}) -> xep0065;
1381 get_mod({muc_invite, _, _, _, _}) -> xep0045;
13821514 get_mod({sasl_auth, _, _}) -> rfc6120;
13831515 get_mod({vcard_logo, _, _, _}) -> xep0054;
13841516 get_mod({ps_item, _, _, _, _, _}) -> xep0060;
1385 get_mod({ps_event, _, _, _, _, _, _}) -> xep0060;
13861517 get_mod({mam_fin, _, _, _, _, _}) -> xep0313;
1518 get_mod({jingle_ft_error, _}) -> xep0234;
13871519 get_mod({legacy_auth, _, _, _, _}) -> xep0078;
1388 get_mod({sasl_mechanisms, _}) -> rfc6120;
1389 get_mod({starttls, _}) -> rfc6120;
13901520 get_mod({stream_features, _}) -> rfc6120;
13911521 get_mod({muc_item, _, _, _, _, _, _, _}) -> xep0045;
1392 get_mod({stream_start, _, _, _, _, _, _, _, _}) ->
1393 rfc6120;
13941522 get_mod({block_list, _}) -> xep0191;
13951523 get_mod({'see-other-host', _}) -> rfc6120;
13961524 get_mod({muc_destroy, _, _, _, _}) -> xep0045;
13981526 get_mod({carbons_sent, _}) -> xep0280;
13991527 get_mod({hint, _}) -> xep0334;
14001528 get_mod({origin_id, _}) -> xep0359;
1529 get_mod({hash_used, _}) -> xep0300;
14011530 get_mod({disco_items, _, _, _}) -> xep0030;
14021531 get_mod({private, _}) -> xep0049;
14031532 get_mod({sasl_challenge, _}) -> rfc6120;
14051534 get_mod({vcard_xupdate, _}) -> xep0153;
14061535 get_mod({db_feature, _}) -> xep0220;
14071536 get_mod({handshake, _}) -> xep0114;
1537 get_mod({ibb_close, _}) -> xep0047;
14081538 get_mod({block_item, _, _}) -> xep0191;
14091539 get_mod({message, _, _, _, _, _, _, _, _, _, _}) ->
14101540 rfc6120;
14201550 xep0054;
14211551 get_mod({xdata_option, _, _}) -> xep0004;
14221552 get_mod({ps_unsubscribe, _, _, _}) -> xep0060;
1553 get_mod({muc_subscription, _, _}) -> p1_mucsub;
14231554 get_mod({sm_resume, _, _, _}) -> xep0198;
14241555 get_mod({push_enable, _, _, _}) -> xep0357;
1556 get_mod({jingle_ft_range, _, _, _}) -> xep0234;
14251557 get_mod({vcard_geo, _, _}) -> xep0054;
14261558 get_mod({mam_query, _, _, _, _, _, _, _, _}) -> xep0313;
14271559 get_mod({xevent, _, _, _, _, _}) -> xep0022;
14281560 get_mod({db_result, _, _, _, _, _}) -> xep0220;
1561 get_mod({jingle_content, _, _, _, _, _}) -> xep0166;
14291562 get_mod({bookmark_conference, _, _, _, _, _}) ->
14301563 xep0048;
14311564 get_mod({feature_register}) -> xep0077;
14381571 get_mod({disco_info, _, _, _, _}) -> xep0030;
14391572 get_mod({sm_a, _, _}) -> xep0198;
14401573 get_mod({oob_x, _, _, _}) -> xep0066;
1574 get_mod({jingle_s5b_transport, _, _, _, _, _, _, _}) ->
1575 xep0260;
14411576 get_mod({privacy_query, _, _, _}) -> xep0016;
14421577 get_mod({block, _}) -> xep0191;
14431578 get_mod({vcard_label, _, _, _, _, _, _, _, _}) ->
14471582 get_mod({delay, _, _, _}) -> xep0203;
14481583 get_mod({muc_actor, _, _}) -> xep0045;
14491584 get_mod({nick, _}) -> xep0172;
1585 get_mod({jingle, _, _, _, _, _, _, _}) -> xep0166;
14501586 get_mod({stats, _, _}) -> xep0039;
14511587 get_mod({vcard_temp, _, _, _, _, _, _, _, _, _, _, _, _,
14521588 _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}) ->
14591595 get_mod({privacy_list, _, _}) -> xep0016;
14601596 get_mod({report, _, _}) -> xep0377;
14611597 get_mod({message_thread, _, _}) -> rfc6120;
1462 get_mod({text, _, _}) -> rfc6120;
14631598 get_mod({shim, _}) -> xep0131;
14641599 get_mod({mam_archived, _, _}) -> xep0313;
14651600 get_mod({forwarded, _, _}) -> xep0297;
14671602 get_mod({mix_participant, _, _}) -> xep0369;
14681603 get_mod({stanza_id, _, _}) -> xep0359;
14691604 get_mod({delegated, _, _}) -> xep0355;
1605 get_mod({text, _, _}) -> xep0234;
1606 get_mod({jingle_ft_received, _, _}) -> xep0234;
14701607 get_mod({bookmark_storage, _, _}) -> xep0048;
14711608 get_mod({sasl_failure, _, _}) -> rfc6120;
14721609 get_mod({xmpp_session, _}) -> rfc3921;
14861623 get_mod({mam_result, _, _, _, _}) -> xep0313;
14871624 get_mod({sm_failed, _, _, _, _}) -> xep0198;
14881625 get_mod({upload_request, _, _, _, _}) -> xep0363;
1626 get_mod({jingle_ft_checksum, _, _, _}) -> xep0234;
14891627 get_mod({version, _, _, _}) -> xep0092;
14901628 get_mod({vcard_org, _, _}) -> xep0054;
14911629 get_mod({ps_items, _, _, _, _, _, _}) -> xep0060;
14921630 get_mod({muc_subscribe, _, _, _, _}) -> p1_mucsub;
14931631 get_mod({feature_csi}) -> xep0352;
1632 get_mod({ibb_open, _, _, _}) -> xep0047;
14941633 get_mod({roster_item, _, _, _, _, _}) -> rfc6121;
14951634 get_mod({vcard_tel, _, _, _, _, _, _, _, _, _, _, _, _,
14961635 _, _}) ->
15001639 get_mod({search_item, _, _, _, _, _}) -> xep0055;
15011640 get_mod({xcaptcha, _}) -> xep0158;
15021641 get_mod({avatar_data, _}) -> xep0084;
1642 get_mod({hash, _, _}) -> xep0300;
15031643 get_mod({roster_query, _, _}) -> rfc6121;
15041644 get_mod({vcard_key, _, _}) -> xep0054;
15051645 get_mod({sm_r, _}) -> xep0198;
15061646 get_mod({media_uri, _, _}) -> xep0221;
15071647 get_mod({upload_slot_0, _, _, _}) -> xep0363;
15081648 get_mod({delegation_query, _, _}) -> xep0355;
1649 get_mod({jingle_error, _}) -> xep0166;
1650 get_mod({jingle_s5b_candidate, _, _, _, _, _, _}) ->
1651 xep0260;
15091652 get_mod({sasl_success, _}) -> rfc6120;
15101653 get_mod({compress, _}) -> xep0138;
15111654 get_mod({xdata_field, _, _, _, _, _, _, _, _}) ->
15151658 get_mod({adhoc_command, _, _, _, _, _, _, _, _}) ->
15161659 xep0050;
15171660 get_mod({media, _, _, _}) -> xep0221;
1661 get_mod({ibb_data, _, _, _}) -> xep0047;
15181662 get_mod({identity, _, _, _, _}) -> xep0030;
15191663 get_mod({redirect, _}) -> rfc6120;
15201664 get_mod({muc_history, _, _, _, _}) -> xep0045;
15211665 get_mod({muc_owner, _, _, _}) -> xep0045;
1666 get_mod({jingle_ft_file, _, _, _, _, _, _, _, _}) ->
1667 xep0234;
15221668 get_mod({bookmark_url, _, _}) -> xep0048;
15231669 get_mod({gone, _}) -> rfc6120;
15241670 get_mod({sasl_response, _}) -> rfc6120;
15261672 get_mod({muc_unique, _}) -> xep0045;
15271673 get_mod({receipt_response, _}) -> xep0184;
15281674 get_mod({sic, _, _, _}) -> xep0279;
1675 get_mod({idle, _}) -> xep0319;
15291676 get_mod({sm_resumed, _, _, _}) -> xep0198;
15301677 get_mod({offline_item, _, _}) -> xep0013;
15311678 get_mod({db_verify, _, _, _, _, _, _}) -> xep0220;
15321679 get_mod({upload_slot, _, _, _}) -> xep0363;
1680 get_mod({jingle_ibb_transport, _, _, _}) -> xep0261;
15331681 get_mod({muc_decline, _, _, _}) -> xep0045;
15341682 get_mod({sm_enable, _, _, _}) -> xep0198;
15351683 get_mod({offline, _, _, _}) -> xep0013;
15671715 get_mod({privilege, _, _}) -> xep0356;
15681716 get_mod({stanza_error, _, _, _, _, _, _}) -> rfc6120;
15691717 get_mod({avatar_pointer, _, _, _, _, _, _}) -> xep0084;
1718 get_mod({muc_invite, _, _, _, _}) -> xep0045;
1719 get_mod({jingle_reason, _, _, _}) -> xep0166;
1720 get_mod({ps_event, _, _, _, _, _, _}) -> xep0060;
1721 get_mod({sasl_mechanisms, _}) -> rfc6120;
1722 get_mod({starttls, _}) -> rfc6120;
1723 get_mod({stream_start, _, _, _, _, _, _, _, _}) ->
1724 rfc6120;
15701725 get_mod(Record) -> xmpp_codec_external:lookup(Record).
0 %%%-------------------------------------------------------------------
1 %%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
2 %%%
3 %%%
4 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
5 %%%
6 %%% Licensed under the Apache License, Version 2.0 (the "License");
7 %%% you may not use this file except in compliance with the License.
8 %%% You may obtain a copy of the License at
9 %%%
10 %%% http://www.apache.org/licenses/LICENSE-2.0
11 %%%
12 %%% Unless required by applicable law or agreed to in writing, software
13 %%% distributed under the License is distributed on an "AS IS" BASIS,
14 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 %%% See the License for the specific language governing permissions and
16 %%% limitations under the License.
17 %%%
18 %%%-------------------------------------------------------------------
19 -module(xmpp_config).
20
21 %% API
22 -export([debug/1, fqdn/1]).
23
24 %%%===================================================================
25 %%% API
26 %%%===================================================================
27 -spec fqdn(any()) -> {ok, [binary()]}.
28 fqdn(_) ->
29 {ok, []}.
30
31 -spec debug(any()) -> {ok, boolean()}.
32 debug(_) ->
33 {ok, false}.
34
35 %%%===================================================================
36 %%% Internal functions
37 %%%===================================================================
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_idna).
18 -author('alexey@process-one.net').
19
20 -export([domain_utf8_to_ascii/1,
21 domain_ucs2_to_ascii/1,
22 utf8_to_ucs2/1]).
23
24 -ifdef(TEST).
25 -include_lib("eunit/include/eunit.hrl").
26 -endif.
27
28 -spec domain_utf8_to_ascii(binary()) -> false | binary().
29
30 domain_utf8_to_ascii(Domain) ->
31 domain_ucs2_to_ascii(utf8_to_ucs2(Domain)).
32
33 utf8_to_ucs2(S) ->
34 utf8_to_ucs2(binary_to_list(S), "").
35
36 utf8_to_ucs2([], R) -> lists:reverse(R);
37 utf8_to_ucs2([C | S], R) when C < 128 ->
38 utf8_to_ucs2(S, [C | R]);
39 utf8_to_ucs2([C1, C2 | S], R) when C1 < 224 ->
40 utf8_to_ucs2(S, [C1 band 31 bsl 6 bor C2 band 63 | R]);
41 utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 ->
42 utf8_to_ucs2(S,
43 [C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63
44 | R]).
45
46 -spec domain_ucs2_to_ascii(list()) -> false | binary().
47
48 domain_ucs2_to_ascii(Domain) ->
49 case catch domain_ucs2_to_ascii1(Domain) of
50 {'EXIT', _Reason} -> false;
51 Res -> iolist_to_binary(Res)
52 end.
53
54 domain_ucs2_to_ascii1(Domain) ->
55 Parts = string:tokens(Domain,
56 [46, 12290, 65294, 65377]),
57 ASCIIParts = lists:map(fun (P) -> to_ascii(P) end,
58 Parts),
59 string:strip(lists:flatmap(fun (P) -> [$. | P] end,
60 ASCIIParts),
61 left, $.).
62
63 %% Assuming domain name is already nameprep'ed
64 to_ascii(Name) ->
65 false = lists:any(fun (C)
66 when (0 =< C) and (C =< 44) or
67 (46 =< C) and (C =< 47)
68 or (58 =< C) and (C =< 64)
69 or (91 =< C) and (C =< 96)
70 or (123 =< C) and (C =< 127) ->
71 true;
72 (_) -> false
73 end,
74 Name),
75 case Name of
76 [H | _] when H /= $- -> true = lists:last(Name) /= $-
77 end,
78 ASCIIName = case lists:any(fun (C) -> C > 127 end, Name)
79 of
80 true ->
81 true = case Name of
82 "xn--" ++ _ -> false;
83 _ -> true
84 end,
85 "xn--" ++ punycode_encode(Name);
86 false -> Name
87 end,
88 L = length(ASCIIName),
89 true = (1 =< L) and (L =< 63),
90 ASCIIName.
91
92 %%% PUNYCODE (RFC3492)
93
94 -define(BASE, 36).
95
96 -define(TMIN, 1).
97
98 -define(TMAX, 26).
99
100 -define(SKEW, 38).
101
102 -define(DAMP, 700).
103
104 -define(INITIAL_BIAS, 72).
105
106 -define(INITIAL_N, 128).
107
108 punycode_encode(Input) ->
109 N = (?INITIAL_N),
110 Delta = 0,
111 Bias = (?INITIAL_BIAS),
112 Basic = lists:filter(fun (C) -> C =< 127 end, Input),
113 NonBasic = lists:filter(fun (C) -> C > 127 end, Input),
114 L = length(Input),
115 B = length(Basic),
116 SNonBasic = lists:usort(NonBasic),
117 Output1 = if B > 0 -> Basic ++ "-";
118 true -> ""
119 end,
120 Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N,
121 Delta, Bias, ""),
122 Output1 ++ Output2.
123
124 punycode_encode1(Input, [M | SNonBasic], B, H, L, N,
125 Delta, Bias, Out)
126 when H < L ->
127 Delta1 = Delta + (M - N) * (H + 1),
128 % let n = m
129 {NewDelta, NewBias, NewH, NewOut} = lists:foldl(fun (C,
130 {ADelta, ABias, AH,
131 AOut}) ->
132 if C < M ->
133 {ADelta + 1,
134 ABias, AH,
135 AOut};
136 C == M ->
137 NewOut =
138 punycode_encode_delta(ADelta,
139 ABias,
140 AOut),
141 NewBias =
142 adapt(ADelta,
143 H +
144 1,
145 H
146 ==
147 B),
148 {0, NewBias,
149 AH + 1,
150 NewOut};
151 true ->
152 {ADelta,
153 ABias, AH,
154 AOut}
155 end
156 end,
157 {Delta1, Bias, H, Out},
158 Input),
159 punycode_encode1(Input, SNonBasic, B, NewH, L, M + 1,
160 NewDelta + 1, NewBias, NewOut);
161 punycode_encode1(_Input, _SNonBasic, _B, _H, _L, _N,
162 _Delta, _Bias, Out) ->
163 lists:reverse(Out).
164
165 punycode_encode_delta(Delta, Bias, Out) ->
166 punycode_encode_delta(Delta, Bias, Out, ?BASE).
167
168 punycode_encode_delta(Delta, Bias, Out, K) ->
169 T = if K =< Bias -> ?TMIN;
170 K >= Bias + (?TMAX) -> ?TMAX;
171 true -> K - Bias
172 end,
173 if Delta < T -> [codepoint(Delta) | Out];
174 true ->
175 C = T + (Delta - T) rem ((?BASE) - T),
176 punycode_encode_delta((Delta - T) div ((?BASE) - T),
177 Bias, [codepoint(C) | Out], K + (?BASE))
178 end.
179
180 adapt(Delta, NumPoints, FirstTime) ->
181 Delta1 = if FirstTime -> Delta div (?DAMP);
182 true -> Delta div 2
183 end,
184 Delta2 = Delta1 + Delta1 div NumPoints,
185 adapt1(Delta2, 0).
186
187 adapt1(Delta, K) ->
188 if Delta > ((?BASE) - (?TMIN)) * (?TMAX) div 2 ->
189 adapt1(Delta div ((?BASE) - (?TMIN)), K + (?BASE));
190 true ->
191 K +
192 ((?BASE) - (?TMIN) + 1) * Delta div (Delta + (?SKEW))
193 end.
194
195 codepoint(C) ->
196 if (0 =< C) and (C =< 25) -> C + 97;
197 (26 =< C) and (C =< 35) -> C + 22
198 end.
199
200 %%%===================================================================
201 %%% Unit tests
202 %%%===================================================================
203 -ifdef(TEST).
204
205 acsii_test() ->
206 ?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)).
207
208 utf8_test() ->
209 ?assertEqual(
210 <<"xn--d1acufc.xn--p1ai">>,
211 domain_utf8_to_ascii(
212 <<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)).
213
214 -endif.
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_sasl).
18 -author('alexey@process-one.net').
19
20 -export([server_new/4, server_start/3, server_step/2,
21 listmech/0, format_error/2]).
22
23 %% TODO: write correct types for these callbacks
24 -type get_password_fun() :: fun().
25 -type check_password_fun() :: fun().
26 -type check_password_digest_fun() :: fun().
27
28 -record(sasl_state, {server_host :: binary(),
29 mech_name = <<"">> :: mechanism(),
30 mech_state :: mech_state(),
31 get_password :: get_password_fun(),
32 check_password :: check_password_fun(),
33 check_password_digest :: check_password_digest_fun()}).
34
35 -type mechanism() :: binary().
36 -type mech_state() :: term().
37 -type sasl_module() :: xmpp_sasl_anonymous | xmpp_sasl_digest |
38 xmpp_sasl_oauth | xmpp_sasl_plain |
39 xmpp_sasl_scram.
40 -type sasl_state() :: #sasl_state{}.
41 -type sasl_property() :: {username, binary()} |
42 {authzid, binary()} |
43 {mechanism, binary()} |
44 {auth_module, atom()}.
45 -type sasl_return() :: {ok, [sasl_property()]} |
46 {ok, [sasl_property()], binary()} |
47 {continue, binary(), sasl_state()} |
48 {error, error_reason(), binary()}.
49 -type error_reason() :: xmpp_sasl_digest:error_reason() |
50 xmpp_sasl_oauth:error_reason() |
51 xmpp_sasl_plain:error_reason() |
52 xmpp_sasl_scram:error_reason() |
53 unsupported_mechanism | nodeprep_failed |
54 empty_username | aborted.
55
56 -export_type([mechanism/0, error_reason/0,
57 sasl_state/0, sasl_return/0, sasl_property/0]).
58
59 -callback mech_new(binary(),
60 get_password_fun(),
61 check_password_fun(),
62 check_password_digest_fun()) -> mech_state().
63 -callback mech_step(mech_state(), binary()) -> sasl_return().
64
65 %%%===================================================================
66 %%% API
67 %%%===================================================================
68 -spec format_error(mechanism(), error_reason()) -> {atom(), binary()}.
69 format_error(_, unsupported_mechanism) ->
70 {'invalid-mechanism', <<"Unsupported mechanism">>};
71 format_error(_, nodeprep_failed) ->
72 {'bad-protocol', <<"Nodeprep failed">>};
73 format_error(_, empty_username) ->
74 {'bad-protocol', <<"Empty username">>};
75 format_error(_, aborted) ->
76 {'aborted', <<"Aborted">>};
77 format_error(Mech, Reason) ->
78 case get_mod(Mech) of
79 undefined ->
80 {'invalid-mechanism', <<"Unsupported mechanism">>};
81 Mod ->
82 Mod:format_error(Reason)
83 end.
84
85 -spec listmech() -> [mechanism()].
86 listmech() ->
87 [<<"ANONYMOUS">>,<<"DIGEST-MD5">>,<<"PLAIN">>,
88 <<"SCRAM-SHA-1">>,<<"X-OAUTH2">>].
89
90 -spec server_new(binary(),
91 get_password_fun(),
92 check_password_fun(),
93 check_password_digest_fun()) -> sasl_state().
94 server_new(ServerHost, GetPassword, CheckPassword, CheckPasswordDigest) ->
95 #sasl_state{server_host = ServerHost,
96 get_password = GetPassword,
97 check_password = CheckPassword,
98 check_password_digest = CheckPasswordDigest}.
99
100 -spec server_start(sasl_state(), mechanism(), binary()) -> sasl_return().
101 server_start(State, Mech, ClientIn) ->
102 case get_mod(Mech) of
103 undefined ->
104 {error, unsupported_mechanism, <<"">>};
105 Module ->
106 MechState = Module:mech_new(State#sasl_state.server_host,
107 State#sasl_state.get_password,
108 State#sasl_state.check_password,
109 State#sasl_state.check_password_digest),
110 State1 = State#sasl_state{mech_name = Mech,
111 mech_state = MechState},
112 server_step(State1, ClientIn)
113 end.
114
115 -spec server_step(sasl_state(), binary()) -> sasl_return().
116 server_step(State, ClientIn) ->
117 Module = get_mod(State#sasl_state.mech_name),
118 MechState = State#sasl_state.mech_state,
119 case Module:mech_step(MechState, ClientIn) of
120 {ok, Props} ->
121 case check_credentials(Props) of
122 ok -> {ok, Props};
123 {error, Error} -> {error, Error, <<"">>}
124 end;
125 {ok, Props, ServerOut} ->
126 case check_credentials(Props) of
127 ok -> {ok, Props, ServerOut};
128 {error, Error} -> {error, Error, <<"">>}
129 end;
130 {continue, ServerOut, NewMechState} ->
131 {continue, ServerOut, State#sasl_state{mech_state = NewMechState}};
132 {error, Error, Username} ->
133 {error, Error, Username};
134 {error, Error} ->
135 {error, Error, <<"">>}
136 end.
137
138 %%%===================================================================
139 %%% Internal functions
140 %%%===================================================================
141 -spec check_credentials([sasl_property()]) -> ok | {error, error_reason()}.
142 check_credentials(Props) ->
143 User = proplists:get_value(authzid, Props, <<>>),
144 case jid:nodeprep(User) of
145 error -> {error, nodeprep_failed};
146 <<"">> -> {error, empty_username};
147 _LUser -> ok
148 end.
149
150 -spec get_mod(binary()) -> sasl_module() | undefined.
151 get_mod(<<"ANONYMOUS">>) -> xmpp_sasl_anonymous;
152 get_mod(<<"DIGEST-MD5">>) -> xmpp_sasl_digest;
153 get_mod(<<"X-OAUTH2">>) -> xmpp_sasl_oauth;
154 get_mod(<<"PLAIN">>) -> xmpp_sasl_plain;
155 get_mod(<<"SCRAM-SHA-1">>) -> xmpp_sasl_scram;
156 get_mod(_) -> undefined.
0 %%%-------------------------------------------------------------------
1 %%% @author Magnus Henoch <henoch@dtek.chalmers.se>
2 %%%
3 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
4 %%%
5 %%% Licensed under the Apache License, Version 2.0 (the "License");
6 %%% you may not use this file except in compliance with the License.
7 %%% You may obtain a copy of the License at
8 %%%
9 %%% http://www.apache.org/licenses/LICENSE-2.0
10 %%%
11 %%% Unless required by applicable law or agreed to in writing, software
12 %%% distributed under the License is distributed on an "AS IS" BASIS,
13 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %%% See the License for the specific language governing permissions and
15 %%% limitations under the License.
16 %%%
17 %%%-------------------------------------------------------------------
18 -module(xmpp_sasl_anonymous).
19 -behaviour(xmpp_sasl).
20 -protocol({xep, 175, '1.2'}).
21
22 -export([mech_new/4, mech_step/2]).
23
24 -record(state, {server = <<"">> :: binary()}).
25
26 mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
27 #state{server = Host}.
28
29 mech_step(#state{}, _ClientIn) ->
30 User = iolist_to_binary([p1_rand:get_string(),
31 integer_to_binary(p1_time_compat:unique_integer([positive]))]),
32 {ok, [{username, User},
33 {authzid, User},
34 {auth_module, anonymous}]}.
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_sasl_digest).
18 -behaviour(xmpp_sasl).
19 -author('alexey@sevcom.net').
20 -dialyzer({no_match, [get_local_fqdn/1]}).
21
22 -export([mech_new/4, mech_step/2, format_error/1]).
23 %% For tests
24 -export([parse/1]).
25
26 -type get_password_fun() :: fun((binary()) -> {false, any()} |
27 {binary(), atom()}).
28 -type check_password_fun() :: fun((binary(), binary(), binary(), binary(),
29 fun((binary()) -> binary())) ->
30 {boolean(), any()} |
31 false).
32 -type error_reason() :: parser_failed | invalid_digest_uri |
33 not_authorized | unexpected_response.
34 -export_type([error_reason/0]).
35
36 -record(state, {step = 1 :: 1 | 3 | 5,
37 nonce = <<"">> :: binary(),
38 username = <<"">> :: binary(),
39 authzid = <<"">> :: binary(),
40 get_password :: get_password_fun(),
41 check_password :: check_password_fun(),
42 auth_module :: atom(),
43 host = <<"">> :: binary(),
44 hostfqdn = [] :: [binary()]}).
45
46 -spec format_error(error_reason()) -> {atom(), binary()}.
47 format_error(parser_failed) ->
48 {'bad-protocol', <<"Response decoding failed">>};
49 format_error(invalid_digest_uri) ->
50 {'bad-protocol', <<"Invalid digest URI">>};
51 format_error(not_authorized) ->
52 {'not-authorized', <<"Invalid username or password">>};
53 format_error(unexpected_response) ->
54 {'bad-protocol', <<"Unexpected response">>}.
55
56 mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
57 #state{step = 1, nonce = p1_rand:get_string(),
58 host = Host, hostfqdn = get_local_fqdn(Host),
59 get_password = GetPassword,
60 check_password = CheckPasswordDigest}.
61
62 mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
63 {continue,
64 <<"nonce=\"", Nonce/binary,
65 "\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>,
66 State#state{step = 3}};
67 mech_step(#state{step = 3, nonce = Nonce} = State,
68 ClientIn) ->
69 case parse(ClientIn) of
70 bad -> {error, parser_failed};
71 KeyVals ->
72 DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
73 UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
74 case is_digesturi_valid(DigestURI, State#state.host,
75 State#state.hostfqdn)
76 of
77 false ->
78 {error, invalid_digest_uri, UserName};
79 true ->
80 AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
81 case (State#state.get_password)(UserName) of
82 {false, _} -> {error, not_authorized, UserName};
83 {Passwd, AuthModule} ->
84 case (State#state.check_password)(UserName, UserName, <<"">>,
85 proplists:get_value(<<"response">>, KeyVals, <<>>),
86 fun (PW) ->
87 response(KeyVals,
88 UserName,
89 PW,
90 Nonce,
91 AuthzId,
92 <<"AUTHENTICATE">>)
93 end)
94 of
95 {true, _} ->
96 RspAuth = response(KeyVals, UserName, Passwd, Nonce,
97 AuthzId, <<"">>),
98 {continue, <<"rspauth=", RspAuth/binary>>,
99 State#state{step = 5, auth_module = AuthModule,
100 username = UserName,
101 authzid = AuthzId}};
102 false -> {error, not_authorized, UserName};
103 {false, _} -> {error, not_authorized, UserName}
104 end
105 end
106 end
107 end;
108 mech_step(#state{step = 5, auth_module = AuthModule,
109 username = UserName, authzid = AuthzId},
110 <<"">>) ->
111 {ok,
112 [{username, UserName}, {authzid, case AuthzId of
113 <<"">> -> UserName;
114 _ -> AuthzId
115 end
116 },
117 {auth_module, AuthModule}]};
118 mech_step(_, _) ->
119 {error, unexpected_response}.
120
121 parse(S) -> parse1(binary_to_list(S), "", []).
122
123 parse1([$= | Cs], S, Ts) ->
124 parse2(Cs, lists:reverse(S), "", Ts);
125 parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts);
126 parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts);
127 parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts);
128 parse1([], [], T) -> lists:reverse(T);
129 parse1([], _S, _T) -> bad.
130
131 parse2([$" | Cs], Key, Val, Ts) ->
132 parse3(Cs, Key, Val, Ts);
133 parse2([C | Cs], Key, Val, Ts) ->
134 parse4(Cs, Key, [C | Val], Ts);
135 parse2([], _, _, _) -> bad.
136
137 parse3([$" | Cs], Key, Val, Ts) ->
138 parse4(Cs, Key, Val, Ts);
139 parse3([$\\, C | Cs], Key, Val, Ts) ->
140 parse3(Cs, Key, [C | Val], Ts);
141 parse3([C | Cs], Key, Val, Ts) ->
142 parse3(Cs, Key, [C | Val], Ts);
143 parse3([], _, _, _) -> bad.
144
145 parse4([$, | Cs], Key, Val, Ts) ->
146 parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]);
147 parse4([$\s | Cs], Key, Val, Ts) ->
148 parse4(Cs, Key, Val, Ts);
149 parse4([C | Cs], Key, Val, Ts) ->
150 parse4(Cs, Key, [C | Val], Ts);
151 parse4([], Key, Val, Ts) ->
152 %% @doc Check if the digest-uri is valid.
153 %% RFC-2831 allows to provide the IP address in Host,
154 %% however this is not supported
155 %% If the service (for example jabber.example.org)
156 %% is provided by several hosts (being one of them server3.example.org),
157 %% then acceptable digest-uris would be:
158 %% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and
159 %% xmpp/jabber.example.org
160 %% The last version is not actually allowed by the RFC, but implemented by popular clients
161 parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]).
162
163 is_digesturi_valid(DigestURICase, JabberDomain,
164 JabberFQDN) ->
165 DigestURI = stringprep:tolower(DigestURICase),
166 case binary:split(DigestURI, <<$/>>, [global, trim]) of
167 [<<"xmpp">>, Host] ->
168 IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
169 (Host == JabberDomain) or IsHostFqdn;
170 [<<"xmpp">>, Host, ServName] ->
171 IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
172 (ServName == JabberDomain) and IsHostFqdn;
173 _ ->
174 false
175 end.
176
177 is_host_fqdn(_Host, []) ->
178 false;
179 is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn ->
180 true;
181 is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn ->
182 is_host_fqdn(Host, FqdnTail).
183
184 get_local_fqdn(Host) ->
185 {ok, FQDNs} = xmpp_config:fqdn(Host),
186 case FQDNs of
187 [] -> [Host];
188 _ -> FQDNs
189 end.
190
191 hex(S) ->
192 xmpp_util:hex(S).
193
194 proplists_get_bin_value(Key, Pairs, Default) ->
195 case proplists:get_value(Key, Pairs, Default) of
196 L when is_list(L) ->
197 list_to_binary(L);
198 L2 ->
199 L2
200 end.
201
202 response(KeyVals, User, Passwd, Nonce, AuthzId,
203 A2Prefix) ->
204 Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>),
205 CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>),
206 DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>),
207 NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>),
208 QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>),
209 MD5Hash = erlang:md5(<<User/binary, ":", Realm/binary, ":",
210 Passwd/binary>>),
211 A1 = case AuthzId of
212 <<"">> ->
213 <<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary>>;
214 _ ->
215 <<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary, ":",
216 AuthzId/binary>>
217 end,
218 A2 = case QOP of
219 <<"auth">> ->
220 <<A2Prefix/binary, ":", DigestURI/binary>>;
221 _ ->
222 <<A2Prefix/binary, ":", DigestURI/binary,
223 ":00000000000000000000000000000000">>
224 end,
225 T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary,
226 ":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
227 ":", (hex((erlang:md5(A2))))/binary>>,
228 hex((erlang:md5(T))).
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_sasl_oauth).
18 -behaviour(xmpp_sasl).
19 -author('alexey@process-one.net').
20
21 -export([mech_new/4, mech_step/2, format_error/1]).
22 %% For tests
23 -export([parse/1]).
24
25 -record(state, {host, check_password}).
26
27 -type error_reason() :: parser_failed | not_authorized.
28 -export_type([error_reason/0]).
29
30 -spec format_error(error_reason()) -> {atom(), binary()}.
31 format_error(parser_failed) ->
32 {'bad-protocol', <<"Response decoding failed">>};
33 format_error(not_authorized) ->
34 {'not-authorized', <<"Invalid token">>}.
35
36 mech_new(Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
37 #state{host = Host, check_password = CheckPassword}.
38
39 mech_step(State, ClientIn) ->
40 case prepare(ClientIn) of
41 [AuthzId, User, Token] ->
42 case (State#state.check_password)(User, AuthzId, Token) of
43 {true, AuthModule} ->
44 {ok,
45 [{username, User}, {authzid, AuthzId},
46 {auth_module, AuthModule}]};
47 _ ->
48 {error, not_authorized, User}
49 end;
50 _ -> {error, parser_failed}
51 end.
52
53 prepare(ClientIn) ->
54 case parse(ClientIn) of
55 [<<"">>, UserMaybeDomain, Token] ->
56 case parse_domain(UserMaybeDomain) of
57 %% <NUL>login@domain<NUL>pwd
58 [User, _Domain] -> [User, User, Token];
59 %% <NUL>login<NUL>pwd
60 [User] -> [User, User, Token]
61 end;
62 %% login@domain<NUL>login<NUL>pwd
63 [AuthzId, User, Token] ->
64 case parse_domain(AuthzId) of
65 %% login@domain<NUL>login<NUL>pwd
66 [AuthzUser, _Domain] -> [AuthzUser, User, Token];
67 %% login<NUL>login<NUL>pwd
68 [AuthzUser] -> [AuthzUser, User, Token]
69 end;
70 _ -> error
71 end.
72
73 parse(S) -> parse1(binary_to_list(S), "", []).
74
75 parse1([0 | Cs], S, T) ->
76 parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
77 parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
78 %parse1([], [], T) ->
79 % lists:reverse(T);
80 parse1([], S, T) ->
81 lists:reverse([list_to_binary(lists:reverse(S)) | T]).
82
83 parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
84
85 parse_domain1([$@ | Cs], S, T) ->
86 parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
87 parse_domain1([C | Cs], S, T) ->
88 parse_domain1(Cs, [C | S], T);
89 parse_domain1([], S, T) ->
90 lists:reverse([list_to_binary(lists:reverse(S)) | T]).
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_sasl_plain).
18 -behaviour(xmpp_sasl).
19 -author('alexey@process-one.net').
20
21 -export([mech_new/4, mech_step/2, format_error/1]).
22 %% For tests
23 -export([parse/1]).
24
25 -record(state, {check_password}).
26 -type error_reason() :: parser_failed | not_authorized.
27 -export_type([error_reason/0]).
28
29 -spec format_error(error_reason()) -> {atom(), binary()}.
30 format_error(parser_failed) ->
31 {'bad-protocol', <<"Response decoding failed">>};
32 format_error(not_authorized) ->
33 {'not-authorized', <<"Invalid username or password">>}.
34
35 mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
36 #state{check_password = CheckPassword}.
37
38 mech_step(State, ClientIn) ->
39 case prepare(ClientIn) of
40 {AuthzId, User, Password} ->
41 case (State#state.check_password)(User, AuthzId, Password) of
42 {true, AuthModule} ->
43 {ok, [{username, User},
44 {authzid, AuthzId},
45 {auth_module, AuthModule}]};
46 _ ->
47 {error, not_authorized, User}
48 end;
49 error ->
50 {error, parser_failed}
51 end.
52
53 -spec prepare(binary()) -> {binary(), binary(), binary()} | error.
54 prepare(ClientIn) ->
55 case parse(ClientIn) of
56 [<<"">>, UserMaybeDomain, Password] ->
57 case parse_authzid(UserMaybeDomain) of
58 {ok, User} ->
59 {User, User, Password};
60 _ ->
61 error
62 end;
63 [AuthzId, User, Password] ->
64 case parse_authzid(AuthzId) of
65 {ok, AuthzUser} ->
66 {AuthzUser, User, Password};
67 _ ->
68 error
69 end;
70 _ ->
71 error
72 end.
73
74 -spec parse(binary()) -> [binary()].
75 parse(S) ->
76 binary:split(S, <<0>>, [global]).
77
78 -spec parse_authzid(binary()) -> {ok, binary()} | error.
79 parse_authzid(S) ->
80 case binary:split(S, <<$@>>) of
81 [User] -> {ok, User};
82 [User, _Domain] -> {ok, User};
83 _ -> error
84 end.
0 %%%-------------------------------------------------------------------
1 %%% @author Stephen Röttger <stephen.roettger@googlemail.com>
2 %%%
3 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
4 %%%
5 %%% Licensed under the Apache License, Version 2.0 (the "License");
6 %%% you may not use this file except in compliance with the License.
7 %%% You may obtain a copy of the License at
8 %%%
9 %%% http://www.apache.org/licenses/LICENSE-2.0
10 %%%
11 %%% Unless required by applicable law or agreed to in writing, software
12 %%% distributed under the License is distributed on an "AS IS" BASIS,
13 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %%% See the License for the specific language governing permissions and
15 %%% limitations under the License.
16 %%%
17 %%%-------------------------------------------------------------------
18 -module(xmpp_sasl_scram).
19 -behaviour(xmpp_sasl).
20 -author('stephen.roettger@googlemail.com').
21 -protocol({rfc, 5802}).
22
23 -export([mech_new/4, mech_step/2, format_error/1]).
24
25 -include("scram.hrl").
26
27 -type password() :: binary() | #scram{}.
28 -type get_password_fun() :: fun((binary()) -> {false | password(), module()}).
29
30 -record(state,
31 {step = 2 :: 2 | 4,
32 stored_key = <<"">> :: binary(),
33 server_key = <<"">> :: binary(),
34 username = <<"">> :: binary(),
35 auth_module :: module(),
36 get_password :: get_password_fun(),
37 auth_message = <<"">> :: binary(),
38 client_nonce = <<"">> :: binary(),
39 server_nonce = <<"">> :: binary()}).
40
41 -define(SALT_LENGTH, 16).
42 -define(NONCE_LENGTH, 16).
43
44 -type error_reason() :: unsupported_extension | bad_username |
45 not_authorized | saslprep_failed |
46 parser_failed | bad_attribute |
47 nonce_mismatch | bad_channel_binding.
48
49 -export_type([error_reason/0]).
50
51 -spec format_error(error_reason()) -> {atom(), binary()}.
52 format_error(unsupported_extension) ->
53 {'bad-protocol', <<"Unsupported extension">>};
54 format_error(bad_username) ->
55 {'invalid-authzid', <<"Malformed username">>};
56 format_error(not_authorized) ->
57 {'not-authorized', <<"Invalid username or password">>};
58 format_error(saslprep_failed) ->
59 {'not-authorized', <<"SASLprep failed">>};
60 format_error(parser_failed) ->
61 {'bad-protocol', <<"Response decoding failed">>};
62 format_error(bad_attribute) ->
63 {'bad-protocol', <<"Malformed or unexpected attribute">>};
64 format_error(nonce_mismatch) ->
65 {'bad-protocol', <<"Nonce mismatch">>};
66 format_error(bad_channel_binding) ->
67 {'bad-protocol', <<"Invalid channel binding">>}.
68
69 mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) ->
70 #state{step = 2, get_password = GetPassword}.
71
72 mech_step(#state{step = 2} = State, ClientIn) ->
73 case re:split(ClientIn, <<",">>, [{return, binary}]) of
74 [_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _]
75 when ExtensionAttribute /= <<"">> ->
76 {error, unsupported_extension};
77 [CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _]
78 when (CBind == <<"y">>) or (CBind == <<"n">>) ->
79 case parse_attribute(UserNameAttribute) of
80 {error, Reason} -> {error, Reason};
81 {_, EscapedUserName} ->
82 case unescape_username(EscapedUserName) of
83 error -> {error, bad_username};
84 UserName ->
85 case parse_attribute(ClientNonceAttribute) of
86 {$r, ClientNonce} ->
87 {Pass, AuthModule} = (State#state.get_password)(UserName),
88 LPass = if is_binary(Pass) -> jid:resourceprep(Pass);
89 true -> Pass
90 end,
91 if Pass == false ->
92 {error, not_authorized, UserName};
93 LPass == error ->
94 {error, saslprep_failed, UserName};
95 true ->
96 {StoredKey, ServerKey, Salt, IterationCount} =
97 if is_record(Pass, scram) ->
98 {base64:decode(Pass#scram.storedkey),
99 base64:decode(Pass#scram.serverkey),
100 base64:decode(Pass#scram.salt),
101 Pass#scram.iterationcount};
102 true ->
103 TempSalt =
104 p1_rand:bytes(?SALT_LENGTH),
105 SaltedPassword =
106 scram:salted_password(Pass,
107 TempSalt,
108 ?SCRAM_DEFAULT_ITERATION_COUNT),
109 {scram:stored_key(scram:client_key(SaltedPassword)),
110 scram:server_key(SaltedPassword),
111 TempSalt,
112 ?SCRAM_DEFAULT_ITERATION_COUNT}
113 end,
114 ClientFirstMessageBare =
115 substr(ClientIn,
116 str(ClientIn, <<"n=">>)),
117 ServerNonce =
118 base64:encode(p1_rand:bytes(?NONCE_LENGTH)),
119 ServerFirstMessage =
120 iolist_to_binary(
121 ["r=",
122 ClientNonce,
123 ServerNonce,
124 ",", "s=",
125 base64:encode(Salt),
126 ",", "i=",
127 integer_to_list(IterationCount)]),
128 {continue, ServerFirstMessage,
129 State#state{step = 4, stored_key = StoredKey,
130 server_key = ServerKey,
131 auth_module = AuthModule,
132 auth_message =
133 <<ClientFirstMessageBare/binary,
134 ",", ServerFirstMessage/binary>>,
135 client_nonce = ClientNonce,
136 server_nonce = ServerNonce,
137 username = UserName}}
138 end;
139 _ -> {error, bad_attribute}
140 end
141 end
142 end;
143 _Else -> {error, parser_failed}
144 end;
145 mech_step(#state{step = 4} = State, ClientIn) ->
146 case tokens(ClientIn, <<",">>) of
147 [GS2ChannelBindingAttribute, NonceAttribute,
148 ClientProofAttribute] ->
149 case parse_attribute(GS2ChannelBindingAttribute) of
150 {$c, CVal} ->
151 ChannelBindingSupport = try binary:first(base64:decode(CVal))
152 catch _:badarg -> 0
153 end,
154 if (ChannelBindingSupport == $n)
155 or (ChannelBindingSupport == $y) ->
156 Nonce = <<(State#state.client_nonce)/binary,
157 (State#state.server_nonce)/binary>>,
158 case parse_attribute(NonceAttribute) of
159 {$r, CompareNonce} when CompareNonce == Nonce ->
160 case parse_attribute(ClientProofAttribute) of
161 {$p, ClientProofB64} ->
162 ClientProof = try base64:decode(ClientProofB64)
163 catch _:badarg -> <<>>
164 end,
165 AuthMessage = iolist_to_binary(
166 [State#state.auth_message,
167 ",",
168 substr(ClientIn, 1,
169 str(ClientIn, <<",p=">>)
170 - 1)]),
171 ClientSignature =
172 scram:client_signature(State#state.stored_key,
173 AuthMessage),
174 ClientKey = scram:client_key(ClientProof,
175 ClientSignature),
176 CompareStoredKey = scram:stored_key(ClientKey),
177 if CompareStoredKey == State#state.stored_key ->
178 ServerSignature =
179 scram:server_signature(State#state.server_key,
180 AuthMessage),
181 {ok, [{username, State#state.username},
182 {auth_module, State#state.auth_module},
183 {authzid, State#state.username}],
184 <<"v=",
185 (base64:encode(ServerSignature))/binary>>};
186 true -> {error, not_authorized, State#state.username}
187 end;
188 _ -> {error, bad_attribute}
189 end;
190 {$r, _} -> {error, nonce_mismatch};
191 _ -> {error, bad_attribute}
192 end;
193 true -> {error, bad_channel_binding}
194 end;
195 _ -> {error, bad_attribute}
196 end;
197 _ -> {error, parser_failed}
198 end.
199
200 parse_attribute(<<Name, $=, Val/binary>>) when Val /= <<>> ->
201 case is_alpha(Name) of
202 true -> {Name, Val};
203 false -> {error, bad_attribute}
204 end;
205 parse_attribute(_) ->
206 {error, bad_attribute}.
207
208 unescape_username(<<"">>) -> <<"">>;
209 unescape_username(EscapedUsername) ->
210 Pos = str(EscapedUsername, <<"=">>),
211 if Pos == 0 -> EscapedUsername;
212 true ->
213 Start = substr(EscapedUsername, 1, Pos - 1),
214 End = substr(EscapedUsername, Pos),
215 EndLen = byte_size(End),
216 if EndLen < 3 -> error;
217 true ->
218 case substr(End, 1, 3) of
219 <<"=2C">> ->
220 <<Start/binary, ",",
221 (unescape_username(substr(End, 4)))/binary>>;
222 <<"=3D">> ->
223 <<Start/binary, "=",
224 (unescape_username(substr(End, 4)))/binary>>;
225 _Else -> error
226 end
227 end
228 end.
229
230 is_alpha(Char) when Char >= $a, Char =< $z -> true;
231 is_alpha(Char) when Char >= $A, Char =< $Z -> true;
232 is_alpha(_) -> false.
233
234 -spec str(binary(), binary()) -> non_neg_integer().
235 str(B1, B2) ->
236 case binary:match(B1, B2) of
237 {R, _Len} -> R+1;
238 _ -> 0
239 end.
240
241 -spec substr(binary(), pos_integer()) -> binary().
242 substr(B, N) ->
243 binary_part(B, N-1, byte_size(B)-N+1).
244
245 -spec substr(binary(), pos_integer(), non_neg_integer()) -> binary().
246 substr(B, S, E) ->
247 binary_part(B, S-1, E).
248
249 -spec tokens(binary(), binary()) -> [binary()].
250 tokens(B1, B2) ->
251 [iolist_to_binary(T) ||
252 T <- string:tokens(binary_to_list(B1), binary_to_list(B2))].
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_socket).
18 -author('alexey@process-one.net').
19 -dialyzer({no_match, [send/2, parse/2]}).
20
21 %% API
22 -export([new/3,
23 connect/3,
24 connect/4,
25 connect/5,
26 starttls/2,
27 compress/1,
28 compress/2,
29 reset_stream/1,
30 send_element/2,
31 send_header/2,
32 send_trailer/1,
33 send/2,
34 send_xml/2,
35 recv/2,
36 activate/1,
37 change_shaper/2,
38 monitor/1,
39 get_sockmod/1,
40 get_transport/1,
41 get_peer_certificate/2,
42 get_verify_result/1,
43 close/1,
44 pp/1,
45 sockname/1, peername/1]).
46
47 -include("xmpp.hrl").
48
49 -type sockmod() :: gen_tcp | fast_tls | ezlib | ext_mod().
50 -type socket() :: inet:socket() | fast_tls:tls_socket() |
51 ezlib:zlib_socket() | ext_socket().
52 -type ext_mod() :: module().
53 -type ext_socket() :: any().
54 -type endpoint() :: {inet:ip_address(), inet:port_number()}.
55 -type stream_element() :: {xmlstreamelement, fxml:xmlel()} |
56 {xmlstreamstart, binary(), [{binary(), binary()}]} |
57 {xmlstreamend, binary()} |
58 {xmlstreamraw, iodata()}.
59
60 -record(socket_state, {sockmod :: sockmod(),
61 socket :: socket(),
62 max_stanza_size :: timeout(),
63 xml_stream :: undefined | fxml_stream:xml_stream_state(),
64 shaper = none :: none | p1_shaper:state()}).
65
66 -type socket_state() :: #socket_state{}.
67
68 -export_type([socket/0, socket_state/0, sockmod/0]).
69
70 -callback send_xml(ext_socket(), stream_element()) -> ok | {error, inet:posix()}.
71 -callback get_owner(ext_socket()) -> pid().
72 -callback get_transport(ext_socket()) -> atom().
73 -callback change_shaper(ext_socket(), none | p1_shaper:state()) -> ok.
74 -callback controlling_process(ext_socket(), pid()) -> ok | {error, inet:posix()}.
75 -callback close(ext_socket()) -> ok | {error, inet:posix()}.
76 -callback sockname(ext_socket()) -> {ok, endpoint()} | {error, inet:posix()}.
77 -callback peername(ext_socket()) -> {ok, endpoint()} | {error, inet:posix()}.
78 -callback setopts(ext_socket(), [{active, once}]) -> ok | {error, inet:posix()}.
79
80 -define(dbg(Fmt, Args),
81 case xmpp_config:debug(global) of
82 {ok, true} -> error_logger:info_msg(Fmt, Args);
83 _ -> false
84 end).
85
86 %%====================================================================
87 %% API
88 %%====================================================================
89 -spec new(sockmod(), socket(), [proplists:property()]) -> socket_state().
90 new(SockMod, Socket, Opts) ->
91 MaxStanzaSize = proplists:get_value(max_stanza_size, Opts, infinity),
92 XMLStream = case get_owner(SockMod, Socket) of
93 Pid when Pid == self() ->
94 fxml_stream:new(self(), MaxStanzaSize);
95 _ ->
96 undefined
97 end,
98 #socket_state{sockmod = SockMod,
99 socket = Socket,
100 xml_stream = XMLStream,
101 max_stanza_size = MaxStanzaSize}.
102
103 connect(Addr, Port, Opts) ->
104 connect(Addr, Port, Opts, infinity, self()).
105
106 connect(Addr, Port, Opts, Timeout) ->
107 connect(Addr, Port, Opts, Timeout, self()).
108
109 connect(Addr, Port, Opts, Timeout, Owner) ->
110 case gen_tcp:connect(Addr, Port, Opts, Timeout) of
111 {ok, Socket} ->
112 SocketData = new(gen_tcp, Socket, []),
113 case controlling_process(SocketData, Owner) of
114 ok ->
115 activate_after(Socket, Owner, 0),
116 {ok, SocketData};
117 {error, _Reason} = Error ->
118 gen_tcp:close(Socket),
119 Error
120 end;
121 {error, _Reason} = Error ->
122 Error
123 end.
124
125 starttls(#socket_state{sockmod = gen_tcp,
126 socket = Socket} = SocketData, TLSOpts) ->
127 case fast_tls:tcp_to_tls(Socket, TLSOpts) of
128 {ok, TLSSocket} ->
129 SocketData1 = SocketData#socket_state{socket = TLSSocket,
130 sockmod = fast_tls},
131 SocketData2 = reset_stream(SocketData1),
132 case fast_tls:recv_data(TLSSocket, <<>>) of
133 {ok, TLSData} ->
134 parse(SocketData2, TLSData);
135 {error, _} = Err ->
136 Err
137 end;
138 {error, _} = Err ->
139 Err
140 end;
141 starttls(_, _) ->
142 erlang:error(badarg).
143
144 compress(SocketData) -> compress(SocketData, undefined).
145
146 compress(#socket_state{sockmod = SockMod,
147 socket = Socket} = SocketData, Data)
148 when SockMod == gen_tcp orelse SockMod == fast_tls ->
149 {ok, ZlibSocket} = ezlib:enable_zlib(SockMod, Socket),
150 case Data of
151 undefined -> ok;
152 _ -> send(SocketData, Data)
153 end,
154 SocketData1 = SocketData#socket_state{socket = ZlibSocket,
155 sockmod = ezlib},
156 SocketData2 = reset_stream(SocketData1),
157 case ezlib:recv_data(ZlibSocket, <<"">>) of
158 {ok, ZlibData} ->
159 parse(SocketData2, ZlibData);
160 {error, _} = Err ->
161 Err
162 end;
163 compress(_, _) ->
164 erlang:error(badarg).
165
166 reset_stream(#socket_state{xml_stream = XMLStream,
167 sockmod = SockMod, socket = Socket,
168 max_stanza_size = MaxStanzaSize} = SocketData) ->
169 if XMLStream /= undefined ->
170 XMLStream1 = try fxml_stream:reset(XMLStream)
171 catch error:_ ->
172 fxml_stream:close(XMLStream),
173 fxml_stream:new(self(), MaxStanzaSize)
174 end,
175 SocketData#socket_state{xml_stream = XMLStream1};
176 true ->
177 Socket1 = SockMod:reset_stream(Socket),
178 SocketData#socket_state{socket = Socket1}
179 end.
180
181 -spec send_element(socket_state(), fxml:xmlel()) -> ok | {error, inet:posix()}.
182 send_element(#socket_state{xml_stream = undefined} = SocketData, El) ->
183 send_xml(SocketData, {xmlstreamelement, El});
184 send_element(SocketData, El) ->
185 send(SocketData, fxml:element_to_binary(El)).
186
187 -spec send_header(socket_state(), fxml:xmlel()) -> ok | {error, inet:posix()}.
188 send_header(#socket_state{xml_stream = undefined} = SocketData, El) ->
189 send_xml(SocketData, {xmlstreamstart, El#xmlel.name, El#xmlel.attrs});
190 send_header(SocketData, El) ->
191 send(SocketData, fxml:element_to_header(El)).
192
193 -spec send_trailer(socket_state()) -> ok | {error, inet:posix()}.
194 send_trailer(#socket_state{xml_stream = undefined} = SocketData) ->
195 send_xml(SocketData, {xmlstreamend, <<"stream:stream">>});
196 send_trailer(SocketData) ->
197 send(SocketData, <<"</stream:stream>">>).
198
199 -spec send(socket_state(), iodata()) -> ok | {error, closed | inet:posix()}.
200 send(#socket_state{sockmod = SockMod, socket = Socket} = SocketData, Data) ->
201 ?dbg("(~s) Send XML on stream = ~p", [pp(SocketData), Data]),
202 try SockMod:send(Socket, Data) of
203 {error, einval} -> {error, closed};
204 Result -> Result
205 catch _:badarg ->
206 %% Some modules throw badarg exceptions on closed sockets
207 %% TODO: their code should be improved
208 {error, closed}
209 end.
210
211 -spec send_xml(socket_state(), stream_element()) -> ok | {error, any()}.
212 send_xml(#socket_state{sockmod = SockMod, socket = Socket}, El) ->
213 SockMod:send_xml(Socket, El).
214
215 recv(#socket_state{sockmod = SockMod, socket = Socket} = SocketData, Data) ->
216 case SockMod of
217 fast_tls ->
218 case fast_tls:recv_data(Socket, Data) of
219 {ok, TLSData} ->
220 parse(SocketData, TLSData);
221 {error, _} = Err ->
222 Err
223 end;
224 ezlib ->
225 case ezlib:recv_data(Socket, Data) of
226 {ok, ZlibData} ->
227 parse(SocketData, ZlibData);
228 {error, _} = Err ->
229 Err
230 end;
231 _ ->
232 parse(SocketData, Data)
233 end.
234
235 -spec change_shaper(socket_state(), none | p1_shaper:state()) -> socket_state().
236 change_shaper(#socket_state{xml_stream = XMLStream,
237 sockmod = SockMod,
238 socket = Socket} = SocketData, Shaper) ->
239 if XMLStream /= undefined ->
240 SocketData#socket_state{shaper = Shaper};
241 true ->
242 SockMod:change_shaper(Socket, Shaper),
243 SocketData
244 end.
245
246 monitor(#socket_state{xml_stream = undefined,
247 sockmod = SockMod, socket = Socket}) ->
248 erlang:monitor(process, SockMod:get_owner(Socket));
249 monitor(_) ->
250 make_ref().
251
252 controlling_process(#socket_state{sockmod = SockMod,
253 socket = Socket}, Pid) ->
254 SockMod:controlling_process(Socket, Pid).
255
256 get_sockmod(SocketData) ->
257 SocketData#socket_state.sockmod.
258
259 get_transport(#socket_state{sockmod = SockMod,
260 socket = Socket}) ->
261 case SockMod of
262 gen_tcp -> tcp;
263 fast_tls -> tls;
264 ezlib ->
265 case ezlib:get_sockmod(Socket) of
266 gen_tcp -> tcp_zlib;
267 fast_tls -> tls_zlib
268 end;
269 _ -> SockMod:get_transport(Socket)
270 end.
271
272 get_owner(SockMod, _) when SockMod == gen_tcp orelse
273 SockMod == fast_tls orelse
274 SockMod == ezlib ->
275 self();
276 get_owner(SockMod, Socket) ->
277 SockMod:get_owner(Socket).
278
279 get_peer_certificate(SocketData, Type) ->
280 fast_tls:get_peer_certificate(SocketData#socket_state.socket, Type).
281
282 get_verify_result(SocketData) ->
283 fast_tls:get_verify_result(SocketData#socket_state.socket).
284
285 close(#socket_state{sockmod = SockMod, socket = Socket}) ->
286 SockMod:close(Socket).
287
288 -spec sockname(socket_state()) -> {ok, endpoint()} | {error, inet:posix()}.
289 sockname(#socket_state{sockmod = SockMod,
290 socket = Socket}) ->
291 case SockMod of
292 gen_tcp -> inet:sockname(Socket);
293 _ -> SockMod:sockname(Socket)
294 end.
295
296 -spec peername(socket_state()) -> {ok, endpoint()} | {error, inet:posix()}.
297 peername(#socket_state{sockmod = SockMod,
298 socket = Socket}) ->
299 case SockMod of
300 gen_tcp -> inet:peername(Socket);
301 _ -> SockMod:peername(Socket)
302 end.
303
304 activate(#socket_state{sockmod = SockMod, socket = Socket}) ->
305 case SockMod of
306 gen_tcp -> inet:setopts(Socket, [{active, once}]);
307 _ -> SockMod:setopts(Socket, [{active, once}])
308 end.
309
310 activate_after(Socket, Pid, Pause) ->
311 if Pause > 0 ->
312 erlang:send_after(Pause, Pid, {tcp, Socket, <<>>});
313 true ->
314 Pid ! {tcp, Socket, <<>>}
315 end,
316 ok.
317
318 pp(#socket_state{sockmod = SockMod, socket = Socket} = State) ->
319 Transport = get_transport(State),
320 Receiver = get_owner(SockMod, Socket),
321 io_lib:format("~s|~w", [Transport, Receiver]).
322
323 parse(SocketData, Data) when Data == <<>>; Data == [] ->
324 case activate(SocketData) of
325 ok ->
326 {ok, SocketData};
327 {error, _} = Err ->
328 Err
329 end;
330 parse(SocketData, [El | Els]) when is_record(El, xmlel) ->
331 self() ! {'$gen_event', {xmlstreamelement, El}},
332 parse(SocketData, Els);
333 parse(SocketData, [El | Els]) when
334 element(1, El) == xmlstreamstart;
335 element(1, El) == xmlstreamelement;
336 element(1, El) == xmlstreamend;
337 element(1, El) == xmlstreamerror ->
338 self() ! {'$gen_event', El},
339 parse(SocketData, Els);
340 parse(#socket_state{xml_stream = XMLStream,
341 socket = Socket,
342 shaper = ShaperState} = SocketData, Data)
343 when is_binary(Data) ->
344 ?dbg("(~s) Received XML on stream = ~p", [pp(SocketData), Data]),
345 XMLStream1 = fxml_stream:parse(XMLStream, Data),
346 {ShaperState1, Pause} = shaper_update(ShaperState, byte_size(Data)),
347 Ret = if Pause > 0 ->
348 activate_after(Socket, self(), Pause);
349 true ->
350 activate(SocketData)
351 end,
352 case Ret of
353 ok ->
354 {ok, SocketData#socket_state{xml_stream = XMLStream1,
355 shaper = ShaperState1}};
356 {error, _} = Err ->
357 Err
358 end.
359
360 shaper_update(none, _) ->
361 {none, 0};
362 shaper_update(Shaper, Size) ->
363 p1_shaper:update(Shaper, Size).
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_stream).
18
19 %% API
20 -export([new_id/0]).
21
22 %%%===================================================================
23 %%% API
24 %%%===================================================================
25 -spec new_id() -> binary().
26 new_id() ->
27 p1_rand:get_string().
28
29 %%%===================================================================
30 %%% Internal functions
31 %%%===================================================================
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_stream_in).
18 -define(GEN_SERVER, p1_server).
19 -behaviour(?GEN_SERVER).
20
21 -protocol({rfc, 6120}).
22 -protocol({xep, 114, '1.6'}).
23
24 %% API
25 -export([start/3, start_link/3, call/3, cast/2, reply/2, stop/1,
26 accept/1, send/2, close/1, close/2, send_error/3, establish/1,
27 get_transport/1, change_shaper/2, set_timeout/2, format_error/1]).
28
29 %% gen_server callbacks
30 -export([init/1, handle_cast/2, handle_call/3, handle_info/2,
31 terminate/2, code_change/3]).
32
33 %%-define(DBGFSM, true).
34 -ifdef(DBGFSM).
35 -define(FSMOPTS, [{debug, [trace]}]).
36 -else.
37 -define(FSMOPTS, []).
38 -endif.
39
40 -include("xmpp.hrl").
41 -type state() :: map().
42 -type stop_reason() :: {stream, reset | {in | out, stream_error()}} |
43 {tls, inet:posix() | atom() | binary()} |
44 {socket, inet:posix() | atom()} |
45 internal_failure.
46 -export_type([state/0, stop_reason/0]).
47 -callback init(list()) -> {ok, state()} | {error, term()} | ignore.
48 -callback handle_cast(term(), state()) -> state().
49 -callback handle_call(term(), term(), state()) -> state().
50 -callback handle_info(term(), state()) -> state().
51 -callback terminate(term(), state()) -> any().
52 -callback code_change(term(), state(), term()) -> {ok, state()} | {error, term()}.
53 -callback handle_stream_start(stream_start(), state()) -> state().
54 -callback handle_stream_established(state()) -> state().
55 -callback handle_stream_end(stop_reason(), state()) -> state().
56 -callback handle_cdata(binary(), state()) -> state().
57 -callback handle_unauthenticated_packet(xmpp_element(), state()) -> state().
58 -callback handle_authenticated_packet(xmpp_element(), state()) -> state().
59 -callback handle_unbinded_packet(xmpp_element(), state()) -> state().
60 -callback handle_auth_success(binary(), binary(), module(), state()) -> state().
61 -callback handle_auth_failure(binary(), binary(), binary(), state()) -> state().
62 -callback handle_send(xmpp_element(), ok | {error, inet:posix()}, state()) -> state().
63 -callback handle_recv(fxml:xmlel(), xmpp_element() | {error, term()}, state()) -> state().
64 -callback handle_timeout(state()) -> state().
65 -callback get_password_fun(state()) -> fun().
66 -callback get_password_fun(xmpp_sasl:mechanism(), state()) -> fun().
67 -callback check_password_fun(xmpp_sasl:mechanism(), state()) -> fun().
68 -callback check_password_digest_fun(xmpp_sasl:mechanism(), state()) -> fun().
69 -callback bind(binary(), state()) -> {ok, state()} | {error, stanza_error(), state()}.
70 -callback compress_methods(state()) -> [binary()].
71 -callback tls_options(state()) -> [proplists:property()].
72 -callback tls_required(state()) -> boolean().
73 -callback tls_enabled(state()) -> boolean().
74 -callback sasl_mechanisms([xmpp_sasl:mechanism()], state()) -> [xmpp_sasl:mechanism()].
75 -callback unauthenticated_stream_features(state()) -> [xmpp_element()].
76 -callback authenticated_stream_features(state()) -> [xmpp_element()].
77
78 %% All callbacks are optional
79 -optional_callbacks([init/1,
80 handle_cast/2,
81 handle_call/3,
82 handle_info/2,
83 terminate/2,
84 code_change/3,
85 handle_stream_start/2,
86 handle_stream_established/1,
87 handle_stream_end/2,
88 handle_cdata/2,
89 handle_authenticated_packet/2,
90 handle_unauthenticated_packet/2,
91 handle_unbinded_packet/2,
92 handle_auth_success/4,
93 handle_auth_failure/4,
94 handle_send/3,
95 handle_recv/3,
96 handle_timeout/1,
97 get_password_fun/1,
98 get_password_fun/2,
99 check_password_fun/2,
100 check_password_digest_fun/2,
101 bind/2,
102 compress_methods/1,
103 tls_options/1,
104 tls_required/1,
105 tls_enabled/1,
106 sasl_mechanisms/2,
107 unauthenticated_stream_features/1,
108 authenticated_stream_features/1]).
109
110 %%%===================================================================
111 %%% API
112 %%%===================================================================
113 start(Mod, Args, Opts) ->
114 ?GEN_SERVER:start(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS).
115
116 start_link(Mod, Args, Opts) ->
117 ?GEN_SERVER:start_link(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS).
118
119 call(Ref, Msg, Timeout) ->
120 ?GEN_SERVER:call(Ref, Msg, Timeout).
121
122 cast(Ref, Msg) ->
123 ?GEN_SERVER:cast(Ref, Msg).
124
125 reply(Ref, Reply) ->
126 ?GEN_SERVER:reply(Ref, Reply).
127
128 -spec stop(pid()) -> ok;
129 (state()) -> no_return().
130 stop(Pid) when is_pid(Pid) ->
131 cast(Pid, stop);
132 stop(#{owner := Owner} = State) when Owner == self() ->
133 terminate(normal, State),
134 exit(normal);
135 stop(_) ->
136 erlang:error(badarg).
137
138 -spec accept(pid()) -> ok.
139 accept(Pid) ->
140 cast(Pid, accept).
141
142 -spec send(pid(), xmpp_element()) -> ok;
143 (state(), xmpp_element()) -> state().
144 send(Pid, Pkt) when is_pid(Pid) ->
145 cast(Pid, {send, Pkt});
146 send(#{owner := Owner} = State, Pkt) when Owner == self() ->
147 send_pkt(State, Pkt);
148 send(_, _) ->
149 erlang:error(badarg).
150
151 -spec close(pid()) -> ok;
152 (state()) -> state().
153 close(Pid) when is_pid(Pid) ->
154 close(Pid, closed);
155 close(#{owner := Owner} = State) when Owner == self() ->
156 close_socket(State);
157 close(_) ->
158 erlang:error(badarg).
159
160 -spec close(pid(), atom()) -> ok.
161 close(Pid, Reason) ->
162 cast(Pid, {close, Reason}).
163
164 -spec establish(state()) -> state().
165 establish(State) ->
166 process_stream_established(State).
167
168 -spec set_timeout(state(), non_neg_integer() | infinity) -> state().
169 set_timeout(#{owner := Owner} = State, Timeout) when Owner == self() ->
170 case Timeout of
171 infinity -> State#{stream_timeout => infinity};
172 _ ->
173 Time = p1_time_compat:monotonic_time(milli_seconds),
174 State#{stream_timeout => {Timeout, Time}}
175 end;
176 set_timeout(_, _) ->
177 erlang:error(badarg).
178
179 get_transport(#{socket := Socket, owner := Owner})
180 when Owner == self() ->
181 xmpp_socket:get_transport(Socket);
182 get_transport(_) ->
183 erlang:error(badarg).
184
185 -spec change_shaper(state(), none | p1_shaper:state()) -> state().
186 change_shaper(#{socket := Socket, owner := Owner} = State, Shaper)
187 when Owner == self() ->
188 Socket1 = xmpp_socket:change_shaper(Socket, Shaper),
189 State#{socket => Socket1};
190 change_shaper(_, _) ->
191 erlang:error(badarg).
192
193 -spec format_error(stop_reason()) -> binary().
194 format_error({socket, Reason}) ->
195 format("Connection failed: ~s", [format_inet_error(Reason)]);
196 format_error({stream, reset}) ->
197 <<"Stream reset by peer">>;
198 format_error({stream, {in, #stream_error{} = Err}}) ->
199 format("Stream closed by peer: ~s", [xmpp:format_stream_error(Err)]);
200 format_error({stream, {out, #stream_error{} = Err}}) ->
201 format("Stream closed by local host: ~s", [xmpp:format_stream_error(Err)]);
202 format_error({tls, Reason}) ->
203 format("TLS failed: ~s", [format_tls_error(Reason)]);
204 format_error(internal_failure) ->
205 <<"Internal server error">>;
206 format_error(Err) ->
207 format("Unrecognized error: ~w", [Err]).
208
209 %%%===================================================================
210 %%% gen_server callbacks
211 %%%===================================================================
212 init([Mod, {SockMod, Socket}, Opts]) ->
213 Time = p1_time_compat:monotonic_time(milli_seconds),
214 Timeout = timer:seconds(30),
215 State = #{owner => self(),
216 mod => Mod,
217 socket => Socket,
218 socket_mod => SockMod,
219 socket_opts => Opts,
220 stream_timeout => {Timeout, Time},
221 stream_state => accepting},
222 {ok, State, Timeout}.
223
224 handle_cast(accept, #{socket := Socket,
225 socket_mod := SockMod,
226 socket_opts := Opts} = State) ->
227 XMPPSocket = xmpp_socket:new(SockMod, Socket, Opts),
228 SocketMonitor = xmpp_socket:monitor(XMPPSocket),
229 case xmpp_socket:peername(XMPPSocket) of
230 {ok, IP} ->
231 State1 = maps:remove(socket_mod, State),
232 State2 = maps:remove(socket_opts, State1),
233 State3 = State2#{socket => XMPPSocket,
234 socket_monitor => SocketMonitor,
235 ip => IP},
236 State4 = init_state(State3, Opts),
237 case is_disconnected(State4) of
238 true -> noreply(State4);
239 false -> handle_info({tcp, Socket, <<>>}, State4)
240 end;
241 {error, _} ->
242 stop(State)
243 end;
244 handle_cast({send, Pkt}, State) ->
245 noreply(send_pkt(State, Pkt));
246 handle_cast(stop, State) ->
247 {stop, normal, State};
248 handle_cast({close, Reason}, State) ->
249 State1 = close_socket(State),
250 noreply(
251 case is_disconnected(State) of
252 true -> State1;
253 false -> process_stream_end({socket, Reason}, State)
254 end);
255 handle_cast(Cast, State) ->
256 noreply(try callback(handle_cast, Cast, State)
257 catch _:{?MODULE, undef} -> State
258 end).
259
260 handle_call(Call, From, State) ->
261 noreply(try callback(handle_call, Call, From, State)
262 catch _:{?MODULE, undef} -> State
263 end).
264
265 handle_info(_, #{stream_state := accepting} = State) ->
266 stop(State);
267 handle_info({'$gen_event', {xmlstreamstart, Name, Attrs}},
268 #{stream_state := wait_for_stream,
269 xmlns := XMLNS, lang := MyLang} = State) ->
270 El = #xmlel{name = Name, attrs = Attrs},
271 noreply(
272 try xmpp:decode(El, XMLNS, []) of
273 #stream_start{} = Pkt ->
274 State1 = send_header(State, Pkt),
275 case is_disconnected(State1) of
276 true -> State1;
277 false -> process_stream(Pkt, State1)
278 end;
279 _ ->
280 State1 = send_header(State),
281 case is_disconnected(State1) of
282 true -> State1;
283 false -> send_pkt(State1, xmpp:serr_invalid_xml())
284 end
285 catch _:{xmpp_codec, Why} ->
286 State1 = send_header(State),
287 case is_disconnected(State1) of
288 true -> State1;
289 false ->
290 Txt = xmpp:io_format_error(Why),
291 Lang = select_lang(MyLang, xmpp:get_lang(El)),
292 Err = xmpp:serr_invalid_xml(Txt, Lang),
293 send_pkt(State1, Err)
294 end
295 end);
296 handle_info({'$gen_event', {xmlstreamend, _}}, State) ->
297 noreply(process_stream_end({stream, reset}, State));
298 handle_info({'$gen_event', closed}, State) ->
299 noreply(process_stream_end({socket, closed}, State));
300 handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
301 State1 = send_header(State),
302 noreply(
303 case is_disconnected(State1) of
304 true -> State1;
305 false ->
306 Err = case Reason of
307 <<"XML stanza is too big">> ->
308 xmpp:serr_policy_violation(Reason, Lang);
309 {_, Txt} ->
310 xmpp:serr_not_well_formed(Txt, Lang)
311 end,
312 send_pkt(State1, Err)
313 end);
314 handle_info({'$gen_event', El}, #{stream_state := wait_for_stream} = State) ->
315 error_logger:warning_msg("unexpected event from XML driver: ~p; "
316 "xmlstreamstart was expected", [El]),
317 State1 = send_header(State),
318 noreply(
319 case is_disconnected(State1) of
320 true -> State1;
321 false -> send_pkt(State1, xmpp:serr_invalid_xml())
322 end);
323 handle_info({'$gen_event', {xmlstreamelement, El}},
324 #{xmlns := NS, codec_options := Opts} = State) ->
325 noreply(
326 try xmpp:decode(El, NS, Opts) of
327 Pkt ->
328 State1 = try callback(handle_recv, El, Pkt, State)
329 catch _:{?MODULE, undef} -> State
330 end,
331 case is_disconnected(State1) of
332 true -> State1;
333 false -> process_element(Pkt, State1)
334 end
335 catch _:{xmpp_codec, Why} ->
336 State1 = try callback(handle_recv, El, {error, Why}, State)
337 catch _:{?MODULE, undef} -> State
338 end,
339 case is_disconnected(State1) of
340 true -> State1;
341 false -> process_invalid_xml(State1, El, Why)
342 end
343 end);
344 handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}},
345 State) ->
346 noreply(try callback(handle_cdata, Data, State)
347 catch _:{?MODULE, undef} -> State
348 end);
349 handle_info(timeout, #{lang := Lang} = State) ->
350 Disconnected = is_disconnected(State),
351 noreply(try callback(handle_timeout, State)
352 catch _:{?MODULE, undef} when not Disconnected ->
353 Txt = <<"Idle connection">>,
354 send_pkt(State, xmpp:serr_connection_timeout(Txt, Lang));
355 _:{?MODULE, undef} ->
356 stop(State)
357 end);
358 handle_info({'DOWN', MRef, _Type, _Object, _Info},
359 #{socket_monitor := MRef} = State) ->
360 noreply(process_stream_end({socket, closed}, State));
361 handle_info({tcp, _, Data}, #{socket := Socket} = State) ->
362 noreply(
363 case xmpp_socket:recv(Socket, Data) of
364 {ok, NewSocket} ->
365 State#{socket => NewSocket};
366 {error, Reason} when is_atom(Reason) ->
367 process_stream_end({socket, Reason}, State);
368 {error, Reason} ->
369 %% TODO: make fast_tls return atoms
370 process_stream_end({tls, Reason}, State)
371 end);
372 handle_info({tcp_closed, _}, State) ->
373 handle_info({'$gen_event', closed}, State);
374 handle_info({tcp_error, _, Reason}, State) ->
375 noreply(process_stream_end({socket, Reason}, State));
376 handle_info(Info, State) ->
377 noreply(try callback(handle_info, Info, State)
378 catch _:{?MODULE, undef} -> State
379 end).
380
381 terminate(_, #{stream_state := accepting} = State) ->
382 State;
383 terminate(Reason, State) ->
384 case get(already_terminated) of
385 true ->
386 State;
387 _ ->
388 put(already_terminated, true),
389 try callback(terminate, Reason, State)
390 catch _:{?MODULE, undef} -> ok
391 end,
392 send_trailer(State)
393 end.
394
395 code_change(OldVsn, State, Extra) ->
396 callback(code_change, OldVsn, State, Extra).
397
398 %%%===================================================================
399 %%% Internal functions
400 %%%===================================================================
401 -spec init_state(state(), [proplists:property()]) -> state().
402 init_state(#{socket := Socket, mod := Mod} = State, Opts) ->
403 Encrypted = proplists:get_bool(tls, Opts),
404 State1 = State#{stream_direction => in,
405 stream_id => xmpp_stream:new_id(),
406 stream_state => wait_for_stream,
407 stream_header_sent => false,
408 stream_restarted => false,
409 stream_compressed => false,
410 stream_encrypted => Encrypted,
411 stream_version => {1,0},
412 stream_authenticated => false,
413 codec_options => [ignore_els],
414 xmlns => ?NS_CLIENT,
415 lang => <<"">>,
416 user => <<"">>,
417 server => <<"">>,
418 resource => <<"">>,
419 lserver => <<"">>},
420 case try Mod:init([State1, Opts])
421 catch _:undef -> {ok, State1}
422 end of
423 {ok, State2} when not Encrypted ->
424 State2;
425 {ok, State2} when Encrypted ->
426 TLSOpts = try callback(tls_options, State2)
427 catch _:{?MODULE, undef} -> []
428 end,
429 case xmpp_socket:starttls(Socket, TLSOpts) of
430 {ok, TLSSocket} ->
431 State2#{socket => TLSSocket};
432 {error, Reason} ->
433 process_stream_end({tls, Reason}, State2)
434 end;
435 {error, Reason} ->
436 process_stream_end(Reason, State1);
437 ignore ->
438 stop(State)
439 end.
440
441 -spec noreply(state()) -> {noreply, state(), non_neg_integer() | infinity}.
442 noreply(#{stream_timeout := infinity} = State) ->
443 {noreply, State, infinity};
444 noreply(#{stream_timeout := {MSecs, StartTime}} = State) ->
445 CurrentTime = p1_time_compat:monotonic_time(milli_seconds),
446 Timeout = max(0, MSecs - CurrentTime + StartTime),
447 {noreply, State, Timeout}.
448
449 -spec is_disconnected(state()) -> boolean().
450 is_disconnected(#{stream_state := StreamState}) ->
451 StreamState == disconnected.
452
453 -spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state().
454 process_invalid_xml(#{lang := MyLang} = State, El, Reason) ->
455 case xmpp:is_stanza(El) of
456 true ->
457 Txt = xmpp:io_format_error(Reason),
458 Lang = select_lang(MyLang, xmpp:get_lang(El)),
459 send_error(State, El, xmpp:err_bad_request(Txt, Lang));
460 false ->
461 case {xmpp:get_name(El), xmpp:get_ns(El)} of
462 {Tag, ?NS_SASL} when Tag == <<"auth">>;
463 Tag == <<"response">>;
464 Tag == <<"abort">> ->
465 Txt = xmpp:io_format_error(Reason),
466 Err = #sasl_failure{reason = 'malformed-request',
467 text = xmpp:mk_text(Txt, MyLang)},
468 send_pkt(State, Err);
469 {<<"starttls">>, ?NS_TLS} ->
470 send_pkt(State, #starttls_failure{});
471 {<<"compress">>, ?NS_COMPRESS} ->
472 Err = #compress_failure{reason = 'setup-failed'},
473 send_pkt(State, Err);
474 _ ->
475 %% Maybe add something more?
476 State
477 end
478 end.
479
480 -spec process_stream_end(stop_reason(), state()) -> state().
481 process_stream_end(_, #{stream_state := disconnected} = State) ->
482 State;
483 process_stream_end(Reason, State) ->
484 State1 = State#{stream_timeout => infinity,
485 stream_state => disconnected},
486 try callback(handle_stream_end, Reason, State1)
487 catch _:{?MODULE, undef} -> stop(State1)
488 end.
489
490 -spec process_stream(stream_start(), state()) -> state().
491 process_stream(#stream_start{xmlns = XML_NS,
492 stream_xmlns = STREAM_NS},
493 #{xmlns := NS} = State)
494 when XML_NS /= NS; STREAM_NS /= ?NS_STREAM ->
495 send_pkt(State, xmpp:serr_invalid_namespace());
496 process_stream(#stream_start{version = {N, _}}, State) when N > 1 ->
497 send_pkt(State, xmpp:serr_unsupported_version());
498 process_stream(#stream_start{lang = Lang},
499 #{xmlns := ?NS_CLIENT, lang := DefaultLang} = State)
500 when size(Lang) > 35 ->
501 %% As stated in BCP47, 4.4.1:
502 %% Protocols or specifications that specify limited buffer sizes for
503 %% language tags MUST allow for language tags of at least 35 characters.
504 %% Do not store long language tag to avoid possible DoS/flood attacks
505 Txt = <<"Too long value of 'xml:lang' attribute">>,
506 send_pkt(State, xmpp:serr_policy_violation(Txt, DefaultLang));
507 process_stream(#stream_start{to = undefined, version = Version} = StreamStart,
508 #{lang := Lang, server := Server, xmlns := NS} = State) ->
509 if Version < {1,0} andalso NS /= ?NS_COMPONENT ->
510 %% Work-around for gmail servers
511 To = jid:make(Server),
512 process_stream(StreamStart#stream_start{to = To}, State);
513 true ->
514 Txt = <<"Missing 'to' attribute">>,
515 send_pkt(State, xmpp:serr_improper_addressing(Txt, Lang))
516 end;
517 process_stream(#stream_start{to = #jid{luser = U, lresource = R}},
518 #{lang := Lang} = State) when U /= <<"">>; R /= <<"">> ->
519 Txt = <<"Improper 'to' attribute">>,
520 send_pkt(State, xmpp:serr_improper_addressing(Txt, Lang));
521 process_stream(#stream_start{to = #jid{lserver = RemoteServer}} = StreamStart,
522 #{xmlns := ?NS_COMPONENT} = State) ->
523 State1 = State#{remote_server => RemoteServer,
524 stream_state => wait_for_handshake},
525 try callback(handle_stream_start, StreamStart, State1)
526 catch _:{?MODULE, undef} -> State1
527 end;
528 process_stream(#stream_start{to = #jid{server = Server, lserver = LServer},
529 from = From} = StreamStart,
530 #{stream_authenticated := Authenticated,
531 stream_restarted := StreamWasRestarted,
532 xmlns := NS, resource := Resource,
533 stream_encrypted := Encrypted} = State) ->
534 State1 = if not StreamWasRestarted ->
535 State#{server => Server, lserver => LServer};
536 true ->
537 State
538 end,
539 State2 = case From of
540 #jid{lserver = RemoteServer} when NS == ?NS_SERVER ->
541 State1#{remote_server => RemoteServer};
542 _ ->
543 State1
544 end,
545 State3 = try callback(handle_stream_start, StreamStart, State2)
546 catch _:{?MODULE, undef} -> State2
547 end,
548 case is_disconnected(State3) of
549 true -> State3;
550 false ->
551 State4 = send_features(State3),
552 case is_disconnected(State4) of
553 true -> State4;
554 false ->
555 TLSRequired = is_starttls_required(State4),
556 if not Authenticated and (TLSRequired and not Encrypted) ->
557 State4#{stream_state => wait_for_starttls};
558 not Authenticated ->
559 State4#{stream_state => wait_for_sasl_request};
560 (NS == ?NS_CLIENT) and (Resource == <<"">>) ->
561 State4#{stream_state => wait_for_bind};
562 true ->
563 process_stream_established(State4)
564 end
565 end
566 end.
567
568 -spec process_element(xmpp_element(), state()) -> state().
569 process_element(Pkt, #{stream_state := StateName, lang := Lang} = State) ->
570 case Pkt of
571 #starttls{} when StateName == wait_for_starttls;
572 StateName == wait_for_sasl_request ->
573 process_starttls(State);
574 #starttls{} ->
575 process_starttls_failure(unexpected_starttls_request, State);
576 #sasl_auth{} when StateName == wait_for_starttls ->
577 send_pkt(State, #sasl_failure{reason = 'encryption-required'});
578 #sasl_auth{} when StateName == wait_for_sasl_request ->
579 process_sasl_request(Pkt, State);
580 #sasl_auth{} when StateName == wait_for_sasl_response ->
581 process_sasl_request(Pkt, maps:remove(sasl_state, State));
582 #sasl_auth{} ->
583 Txt = <<"SASL negotiation is not allowed in this state">>,
584 send_pkt(State, #sasl_failure{reason = 'not-authorized',
585 text = xmpp:mk_text(Txt, Lang)});
586 #sasl_response{} when StateName == wait_for_starttls ->
587 send_pkt(State, #sasl_failure{reason = 'encryption-required'});
588 #sasl_response{} when StateName == wait_for_sasl_response ->
589 process_sasl_response(Pkt, State);
590 #sasl_response{} ->
591 Txt = <<"SASL negotiation is not allowed in this state">>,
592 send_pkt(State, #sasl_failure{reason = 'not-authorized',
593 text = xmpp:mk_text(Txt, Lang)});
594 #sasl_abort{} when StateName == wait_for_sasl_response ->
595 process_sasl_abort(State);
596 #sasl_abort{} ->
597 send_pkt(State, #sasl_failure{reason = 'aborted'});
598 #sasl_success{} ->
599 State;
600 #compress{} ->
601 process_compress(Pkt, State);
602 #handshake{} when StateName == wait_for_handshake ->
603 process_handshake(Pkt, State);
604 #handshake{} ->
605 State;
606 #stream_error{} ->
607 process_stream_end({stream, {in, Pkt}}, State);
608 _ when StateName == wait_for_sasl_request;
609 StateName == wait_for_handshake;
610 StateName == wait_for_sasl_response ->
611 process_unauthenticated_packet(Pkt, State);
612 _ when StateName == wait_for_starttls ->
613 Txt = <<"Use of STARTTLS required">>,
614 Err = xmpp:serr_policy_violation(Txt, Lang),
615 send_pkt(State, Err);
616 _ when StateName == wait_for_bind ->
617 process_bind(Pkt, State);
618 _ when StateName == established ->
619 process_authenticated_packet(Pkt, State)
620 end.
621
622 -spec process_unauthenticated_packet(xmpp_element(), state()) -> state().
623 process_unauthenticated_packet(Pkt, State) ->
624 NewPkt = set_lang(Pkt, State),
625 try callback(handle_unauthenticated_packet, NewPkt, State)
626 catch _:{?MODULE, undef} ->
627 Err = xmpp:serr_not_authorized(),
628 send(State, Err)
629 end.
630
631 -spec process_authenticated_packet(xmpp_element(), state()) -> state().
632 process_authenticated_packet(Pkt, State) ->
633 Pkt1 = set_lang(Pkt, State),
634 case set_from_to(Pkt1, State) of
635 {ok, Pkt2} ->
636 try callback(handle_authenticated_packet, Pkt2, State)
637 catch _:{?MODULE, undef} ->
638 Err = xmpp:err_service_unavailable(),
639 send_error(State, Pkt, Err)
640 end;
641 {error, Err} ->
642 send_pkt(State, Err)
643 end.
644
645 -spec process_bind(xmpp_element(), state()) -> state().
646 process_bind(#iq{type = set, sub_els = [_]} = Pkt,
647 #{xmlns := ?NS_CLIENT, lang := MyLang} = State) ->
648 try xmpp:try_subtag(Pkt, #bind{}) of
649 #bind{resource = R} ->
650 case callback(bind, R, State) of
651 {ok, #{user := U, server := S, resource := NewR} = State1}
652 when NewR /= <<"">> ->
653 Reply = #bind{jid = jid:make(U, S, NewR)},
654 State2 = send_pkt(State1, xmpp:make_iq_result(Pkt, Reply)),
655 process_stream_established(State2);
656 {error, #stanza_error{} = Err, State1} ->
657 send_error(State1, Pkt, Err)
658 end;
659 _ ->
660 try callback(handle_unbinded_packet, Pkt, State)
661 catch _:{?MODULE, undef} ->
662 Err = xmpp:err_not_authorized(),
663 send_error(State, Pkt, Err)
664 end
665 catch _:{xmpp_codec, Why} ->
666 Txt = xmpp:io_format_error(Why),
667 Lang = select_lang(MyLang, xmpp:get_lang(Pkt)),
668 Err = xmpp:err_bad_request(Txt, Lang),
669 send_error(State, Pkt, Err)
670 end;
671 process_bind(Pkt, State) ->
672 try callback(handle_unbinded_packet, Pkt, State)
673 catch _:{?MODULE, undef} ->
674 Err = xmpp:err_not_authorized(),
675 send_error(State, Pkt, Err)
676 end.
677
678 -spec process_handshake(handshake(), state()) -> state().
679 process_handshake(#handshake{data = Digest},
680 #{stream_id := StreamID,
681 remote_server := RemoteServer} = State) ->
682 GetPW = get_password_fun(State),
683 AuthRes = case GetPW(<<"">>) of
684 {false, _} ->
685 false;
686 {Password, _} ->
687 sha(<<StreamID/binary, Password/binary>>) == Digest
688 end,
689 case AuthRes of
690 true ->
691 State1 = try callback(handle_auth_success,
692 RemoteServer, <<"handshake">>, undefined, State)
693 catch _:{?MODULE, undef} -> State
694 end,
695 case is_disconnected(State1) of
696 true -> State1;
697 false ->
698 State2 = send_pkt(State1, #handshake{}),
699 process_stream_established(State2)
700 end;
701 false ->
702 State1 = try callback(handle_auth_failure,
703 RemoteServer, <<"handshake">>, <<"not authorized">>, State)
704 catch _:{?MODULE, undef} -> State
705 end,
706 case is_disconnected(State1) of
707 true -> State1;
708 false -> send_pkt(State1, xmpp:serr_not_authorized())
709 end
710 end.
711
712 -spec process_stream_established(state()) -> state().
713 process_stream_established(#{stream_state := StateName} = State)
714 when StateName == disconnected; StateName == established ->
715 State;
716 process_stream_established(State) ->
717 State1 = State#{stream_authenticated => true,
718 stream_state => established,
719 stream_timeout => infinity},
720 try callback(handle_stream_established, State1)
721 catch _:{?MODULE, undef} -> State1
722 end.
723
724 -spec process_compress(compress(), state()) -> state().
725 process_compress(#compress{},
726 #{stream_compressed := Compressed,
727 stream_authenticated := Authenticated} = State)
728 when Compressed or not Authenticated ->
729 send_pkt(State, #compress_failure{reason = 'setup-failed'});
730 process_compress(#compress{methods = HisMethods},
731 #{socket := Socket} = State) ->
732 MyMethods = try callback(compress_methods, State)
733 catch _:{?MODULE, undef} -> []
734 end,
735 CommonMethods = lists_intersection(MyMethods, HisMethods),
736 case lists:member(<<"zlib">>, CommonMethods) of
737 true ->
738 case xmpp_socket:compress(Socket) of
739 {ok, ZlibSocket} ->
740 State1 = send_pkt(State, #compressed{}),
741 case is_disconnected(State1) of
742 true -> State1;
743 false ->
744 State1#{socket => ZlibSocket,
745 stream_id => xmpp_stream:new_id(),
746 stream_header_sent => false,
747 stream_restarted => true,
748 stream_state => wait_for_stream,
749 stream_compressed => true}
750 end;
751 {error, _} ->
752 Err = #compress_failure{reason = 'setup-failed'},
753 send_pkt(State, Err)
754 end;
755 false ->
756 send_pkt(State, #compress_failure{reason = 'unsupported-method'})
757 end.
758
759 -spec process_starttls(state()) -> state().
760 process_starttls(#{stream_encrypted := true} = State) ->
761 process_starttls_failure(already_encrypted, State);
762 process_starttls(#{socket := Socket} = State) ->
763 case is_starttls_available(State) of
764 true ->
765 TLSOpts = try callback(tls_options, State)
766 catch _:{?MODULE, undef} -> []
767 end,
768 case xmpp_socket:starttls(Socket, TLSOpts) of
769 {ok, TLSSocket} ->
770 State1 = send_pkt(State, #starttls_proceed{}),
771 case is_disconnected(State1) of
772 true -> State1;
773 false ->
774 State1#{socket => TLSSocket,
775 stream_id => xmpp_stream:new_id(),
776 stream_header_sent => false,
777 stream_restarted => true,
778 stream_state => wait_for_stream,
779 stream_encrypted => true}
780 end;
781 {error, Reason} ->
782 process_starttls_failure(Reason, State)
783 end;
784 false ->
785 process_starttls_failure(starttls_unsupported, State)
786 end.
787
788 -spec process_starttls_failure(term(), state()) -> state().
789 process_starttls_failure(Why, State) ->
790 State1 = send_pkt(State, #starttls_failure{}),
791 case is_disconnected(State1) of
792 true -> State1;
793 false -> process_stream_end({tls, Why}, State1)
794 end.
795
796 -spec process_sasl_request(sasl_auth(), state()) -> state().
797 process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn},
798 #{lserver := LServer} = State) ->
799 State1 = State#{sasl_mech => Mech},
800 Mechs = get_sasl_mechanisms(State1),
801 case lists:member(Mech, Mechs) of
802 true when Mech == <<"EXTERNAL">> ->
803 Res = case xmpp_stream_pkix:authenticate(State1, ClientIn) of
804 {ok, Peer} ->
805 {ok, [{auth_module, pkix}, {username, Peer}]};
806 {error, Reason, Peer} ->
807 {error, Reason, Peer}
808 end,
809 process_sasl_result(Res, State1);
810 true ->
811 GetPW = get_password_fun(Mech, State1),
812 CheckPW = check_password_fun(Mech, State1),
813 CheckPWDigest = check_password_digest_fun(Mech, State1),
814 SASLState = xmpp_sasl:server_new(LServer, GetPW, CheckPW, CheckPWDigest),
815 Res = xmpp_sasl:server_start(SASLState, Mech, ClientIn),
816 process_sasl_result(Res, State1#{sasl_state => SASLState});
817 false ->
818 process_sasl_result({error, unsupported_mechanism, <<"">>}, State1)
819 end.
820
821 -spec process_sasl_response(sasl_response(), state()) -> state().
822 process_sasl_response(#sasl_response{text = ClientIn},
823 #{sasl_state := SASLState} = State) ->
824 SASLResult = xmpp_sasl:server_step(SASLState, ClientIn),
825 process_sasl_result(SASLResult, State).
826
827 -spec process_sasl_result(xmpp_sasl:sasl_return(), state()) -> state().
828 process_sasl_result({ok, Props}, State) ->
829 process_sasl_success(Props, <<"">>, State);
830 process_sasl_result({ok, Props, ServerOut}, State) ->
831 process_sasl_success(Props, ServerOut, State);
832 process_sasl_result({continue, ServerOut, NewSASLState}, State) ->
833 process_sasl_continue(ServerOut, NewSASLState, State);
834 process_sasl_result({error, Reason, User}, State) ->
835 process_sasl_failure(Reason, User, State).
836
837 -spec process_sasl_success([xmpp_sasl:sasl_property()], binary(), state()) -> state().
838 process_sasl_success(Props, ServerOut,
839 #{socket := Socket,
840 sasl_mech := Mech} = State) ->
841 User = identity(Props),
842 AuthModule = proplists:get_value(auth_module, Props),
843 Socket1 = xmpp_socket:reset_stream(Socket),
844 State0 = State#{socket => Socket1},
845 State1 = try callback(handle_auth_success, User, Mech, AuthModule, State0)
846 catch _:{?MODULE, undef} -> State
847 end,
848 case is_disconnected(State1) of
849 true -> State1;
850 false ->
851 State2 = send_pkt(State1, #sasl_success{text = ServerOut}),
852 case is_disconnected(State2) of
853 true -> State2;
854 false ->
855 State3 = maps:remove(sasl_state,
856 maps:remove(sasl_mech, State2)),
857 State3#{stream_id => xmpp_stream:new_id(),
858 stream_authenticated => true,
859 stream_header_sent => false,
860 stream_restarted => true,
861 stream_state => wait_for_stream,
862 user => User}
863 end
864 end.
865
866 -spec process_sasl_continue(binary(), xmpp_sasl:sasl_state(), state()) -> state().
867 process_sasl_continue(ServerOut, NewSASLState, State) ->
868 State1 = State#{sasl_state => NewSASLState,
869 stream_state => wait_for_sasl_response},
870 send_pkt(State1, #sasl_challenge{text = ServerOut}).
871
872 -spec process_sasl_failure(atom(), binary(), state()) -> state().
873 process_sasl_failure(Err, User,
874 #{sasl_mech := Mech, lang := Lang} = State) ->
875 {Reason, Text} = format_sasl_error(Mech, Err),
876 State1 = try callback(handle_auth_failure, User, Mech, Text, State)
877 catch _:{?MODULE, undef} -> State
878 end,
879 case is_disconnected(State1) of
880 true -> State1;
881 false ->
882 State2 = send_pkt(State1,
883 #sasl_failure{reason = Reason,
884 text = xmpp:mk_text(Text, Lang)}),
885 case is_disconnected(State2) of
886 true -> State2;
887 false ->
888 State3 = maps:remove(sasl_state,
889 maps:remove(sasl_mech, State2)),
890 State3#{stream_state => wait_for_sasl_request}
891 end
892 end.
893
894 -spec process_sasl_abort(state()) -> state().
895 process_sasl_abort(State) ->
896 process_sasl_failure(aborted, <<"">>, State).
897
898 -spec send_features(state()) -> state().
899 send_features(#{stream_version := {1,0},
900 stream_encrypted := Encrypted} = State) ->
901 TLSRequired = is_starttls_required(State),
902 Features = if TLSRequired and not Encrypted ->
903 get_tls_feature(State);
904 true ->
905 get_sasl_feature(State) ++ get_compress_feature(State)
906 ++ get_tls_feature(State) ++ get_bind_feature(State)
907 ++ get_session_feature(State) ++ get_other_features(State)
908 end,
909 send_pkt(State, #stream_features{sub_els = Features});
910 send_features(State) ->
911 %% clients and servers from stone age
912 State.
913
914 -spec get_password_fun(state()) -> fun().
915 get_password_fun(State) ->
916 try callback(get_password_fun, State)
917 catch _:{?MODULE, undef} -> fun(_) -> {false, undefined} end
918 end.
919
920 -spec get_password_fun(xmpp_sasl:mechanism(), state()) -> fun().
921 get_password_fun(Mech, State) ->
922 try callback(get_password_fun, Mech, State)
923 catch _:{?MODULE, undef} -> fun(_) -> {false, undefined} end
924 end.
925
926 -spec check_password_fun(xmpp_sasl:mechanism(), state()) -> fun().
927 check_password_fun(Mech, State) ->
928 try callback(check_password_fun, Mech, State)
929 catch _:{?MODULE, undef} -> fun(_, _, _) -> {false, undefined} end
930 end.
931
932 -spec check_password_digest_fun(xmpp_sasl:mechanism(), state()) -> fun().
933 check_password_digest_fun(Mech, State) ->
934 try callback(check_password_digest_fun, Mech, State)
935 catch _:{?MODULE, undef} -> fun(_, _, _, _, _) -> {false, undefined} end
936 end.
937
938 -spec get_sasl_mechanisms(state()) -> [xmpp_sasl:mechanism()].
939 get_sasl_mechanisms(#{stream_encrypted := Encrypted,
940 xmlns := NS} = State) ->
941 Mechs = if NS == ?NS_CLIENT -> xmpp_sasl:listmech();
942 true -> []
943 end,
944 Mechs1 = if Encrypted -> [<<"EXTERNAL">>|Mechs];
945 true -> Mechs
946 end,
947 try callback(sasl_mechanisms, Mechs1, State)
948 catch _:{?MODULE, undef} -> Mechs1
949 end.
950
951 -spec get_sasl_feature(state()) -> [sasl_mechanisms()].
952 get_sasl_feature(#{stream_authenticated := false,
953 stream_encrypted := Encrypted} = State) ->
954 TLSRequired = is_starttls_required(State),
955 if Encrypted or not TLSRequired ->
956 Mechs = get_sasl_mechanisms(State),
957 [#sasl_mechanisms{list = Mechs}];
958 true ->
959 []
960 end;
961 get_sasl_feature(_) ->
962 [].
963
964 -spec get_compress_feature(state()) -> [compression()].
965 get_compress_feature(#{stream_compressed := false,
966 stream_authenticated := true} = State) ->
967 try callback(compress_methods, State) of
968 [] -> [];
969 Ms -> [#compression{methods = Ms}]
970 catch _:{?MODULE, undef} ->
971 []
972 end;
973 get_compress_feature(_) ->
974 [].
975
976 -spec get_tls_feature(state()) -> [starttls()].
977 get_tls_feature(#{stream_authenticated := false,
978 stream_encrypted := false} = State) ->
979 case is_starttls_available(State) of
980 true ->
981 TLSRequired = is_starttls_required(State),
982 [#starttls{required = TLSRequired}];
983 false ->
984 []
985 end;
986 get_tls_feature(_) ->
987 [].
988
989 -spec get_bind_feature(state()) -> [bind()].
990 get_bind_feature(#{xmlns := ?NS_CLIENT,
991 stream_authenticated := true,
992 resource := <<"">>}) ->
993 [#bind{}];
994 get_bind_feature(_) ->
995 [].
996
997 -spec get_session_feature(state()) -> [xmpp_session()].
998 get_session_feature(#{xmlns := ?NS_CLIENT,
999 stream_authenticated := true,
1000 resource := <<"">>}) ->
1001 [#xmpp_session{optional = true}];
1002 get_session_feature(_) ->
1003 [].
1004
1005 -spec get_other_features(state()) -> [xmpp_element()].
1006 get_other_features(#{stream_authenticated := Auth} = State) ->
1007 try
1008 if Auth -> callback(authenticated_stream_features, State);
1009 true -> callback(unauthenticated_stream_features, State)
1010 end
1011 catch _:{?MODULE, undef} ->
1012 []
1013 end.
1014
1015 -spec is_starttls_available(state()) -> boolean().
1016 is_starttls_available(State) ->
1017 try callback(tls_enabled, State)
1018 catch _:{?MODULE, undef} -> true
1019 end.
1020
1021 -spec is_starttls_required(state()) -> boolean().
1022 is_starttls_required(State) ->
1023 try callback(tls_required, State)
1024 catch _:{?MODULE, undef} -> false
1025 end.
1026
1027 -spec set_from_to(xmpp_element(), state()) -> {ok, xmpp_element()} |
1028 {error, stream_error()}.
1029 set_from_to(Pkt, _State) when not ?is_stanza(Pkt) ->
1030 {ok, Pkt};
1031 set_from_to(Pkt, #{user := U, server := S, resource := R,
1032 lang := Lang, xmlns := ?NS_CLIENT}) ->
1033 JID = jid:make(U, S, R),
1034 From = case xmpp:get_from(Pkt) of
1035 undefined -> JID;
1036 F -> F
1037 end,
1038 if JID#jid.luser == From#jid.luser andalso
1039 JID#jid.lserver == From#jid.lserver andalso
1040 (JID#jid.lresource == From#jid.lresource
1041 orelse From#jid.lresource == <<"">>) ->
1042 To = case xmpp:get_to(Pkt) of
1043 undefined -> jid:make(U, S);
1044 T -> T
1045 end,
1046 {ok, xmpp:set_from_to(Pkt, JID, To)};
1047 true ->
1048 Txt = <<"Improper 'from' attribute">>,
1049 {error, xmpp:serr_invalid_from(Txt, Lang)}
1050 end;
1051 set_from_to(Pkt, #{lang := Lang}) ->
1052 From = xmpp:get_from(Pkt),
1053 To = xmpp:get_to(Pkt),
1054 if From == undefined ->
1055 Txt = <<"Missing 'from' attribute">>,
1056 {error, xmpp:serr_improper_addressing(Txt, Lang)};
1057 To == undefined ->
1058 Txt = <<"Missing 'to' attribute">>,
1059 {error, xmpp:serr_improper_addressing(Txt, Lang)};
1060 true ->
1061 {ok, Pkt}
1062 end.
1063
1064 -spec send_header(state()) -> state().
1065 send_header(#{stream_version := Version} = State) ->
1066 send_header(State, #stream_start{version = Version}).
1067
1068 -spec send_header(state(), stream_start()) -> state().
1069 send_header(#{stream_id := StreamID,
1070 stream_version := MyVersion,
1071 stream_header_sent := false,
1072 lang := MyLang,
1073 xmlns := NS} = State,
1074 #stream_start{to = HisTo, from = HisFrom,
1075 lang = HisLang, version = HisVersion}) ->
1076 Lang = select_lang(MyLang, HisLang),
1077 NS_DB = if NS == ?NS_SERVER -> ?NS_SERVER_DIALBACK;
1078 true -> <<"">>
1079 end,
1080 Version = case HisVersion of
1081 undefined -> undefined;
1082 {0,_} -> HisVersion;
1083 _ -> MyVersion
1084 end,
1085 StreamStart = #stream_start{version = Version,
1086 lang = Lang,
1087 xmlns = NS,
1088 stream_xmlns = ?NS_STREAM,
1089 db_xmlns = NS_DB,
1090 id = StreamID,
1091 to = HisFrom,
1092 from = HisTo},
1093 State1 = State#{lang => Lang,
1094 stream_version => Version,
1095 stream_header_sent => true},
1096 case socket_send(State1, StreamStart) of
1097 ok -> State1;
1098 {error, Why} -> process_stream_end({socket, Why}, State1)
1099 end;
1100 send_header(State, _) ->
1101 State.
1102
1103 -spec send_pkt(state(), xmpp_element() | xmlel()) -> state().
1104 send_pkt(State, Pkt) ->
1105 Result = socket_send(State, Pkt),
1106 State1 = try callback(handle_send, Pkt, Result, State)
1107 catch _:{?MODULE, undef} -> State
1108 end,
1109 case Result of
1110 _ when is_record(Pkt, stream_error) ->
1111 process_stream_end({stream, {out, Pkt}}, State1);
1112 ok ->
1113 State1;
1114 {error, _Why} ->
1115 % Queue process_stream_end instead of calling it directly,
1116 % so we have opurtunity to process incoming queued messages before
1117 % terminating session.
1118 self() ! {'$gen_event', closed},
1119 State1
1120 end.
1121
1122 -spec send_error(state(), xmpp_element() | xmlel(), stanza_error()) -> state().
1123 send_error(State, Pkt, Err) ->
1124 case xmpp:is_stanza(Pkt) of
1125 true ->
1126 case xmpp:get_type(Pkt) of
1127 result -> State;
1128 error -> State;
1129 <<"result">> -> State;
1130 <<"error">> -> State;
1131 _ ->
1132 ErrPkt = xmpp:make_error(Pkt, Err),
1133 send_pkt(State, ErrPkt)
1134 end;
1135 false ->
1136 State
1137 end.
1138
1139 -spec send_trailer(state()) -> state().
1140 send_trailer(State) ->
1141 socket_send(State, trailer),
1142 close_socket(State).
1143
1144 -spec socket_send(state(), xmpp_element() | xmlel() | trailer) -> ok | {error, inet:posix()}.
1145 socket_send(#{socket := Sock,
1146 stream_state := StateName,
1147 xmlns := NS,
1148 stream_header_sent := true}, Pkt) ->
1149 case Pkt of
1150 trailer ->
1151 xmpp_socket:send_trailer(Sock);
1152 #stream_start{} when StateName /= disconnected ->
1153 xmpp_socket:send_header(Sock, xmpp:encode(Pkt));
1154 _ when StateName /= disconnected ->
1155 xmpp_socket:send_element(Sock, xmpp:encode(Pkt, NS));
1156 _ ->
1157 {error, closed}
1158 end;
1159 socket_send(_, _) ->
1160 {error, closed}.
1161
1162 -spec close_socket(state()) -> state().
1163 close_socket(#{socket := Socket} = State) ->
1164 xmpp_socket:close(Socket),
1165 State#{stream_timeout => infinity,
1166 stream_state => disconnected}.
1167
1168 -spec select_lang(binary(), binary()) -> binary().
1169 select_lang(Lang, <<"">>) -> Lang;
1170 select_lang(_, Lang) -> Lang.
1171
1172 -spec set_lang(xmpp_element(), state()) -> xmpp_element().
1173 set_lang(Pkt, #{lang := MyLang, xmlns := ?NS_CLIENT}) when ?is_stanza(Pkt) ->
1174 HisLang = xmpp:get_lang(Pkt),
1175 Lang = select_lang(MyLang, HisLang),
1176 xmpp:set_lang(Pkt, Lang);
1177 set_lang(Pkt, _) ->
1178 Pkt.
1179
1180 -spec format_inet_error(atom()) -> string().
1181 format_inet_error(closed) ->
1182 "connection closed";
1183 format_inet_error(Reason) ->
1184 case inet:format_error(Reason) of
1185 "unknown POSIX error" -> atom_to_list(Reason);
1186 Txt -> Txt
1187 end.
1188
1189 -spec format_sasl_error(xmpp_sasl:mechanism(), atom()) -> {atom(), binary()}.
1190 format_sasl_error(<<"EXTERNAL">>, Err) ->
1191 xmpp_stream_pkix:format_error(Err);
1192 format_sasl_error(Mech, Err) ->
1193 xmpp_sasl:format_error(Mech, Err).
1194
1195 -spec format_tls_error(atom() | binary()) -> list().
1196 format_tls_error(Reason) when is_atom(Reason) ->
1197 format_inet_error(Reason);
1198 format_tls_error(Reason) ->
1199 Reason.
1200
1201 -spec format(io:format(), list()) -> binary().
1202 format(Fmt, Args) ->
1203 iolist_to_binary(io_lib:format(Fmt, Args)).
1204
1205 -spec lists_intersection(list(), list()) -> list().
1206 lists_intersection(L1, L2) ->
1207 lists:filter(
1208 fun(E) ->
1209 lists:member(E, L2)
1210 end, L1).
1211
1212 -spec identity([xmpp_sasl:sasl_property()]) -> binary().
1213 identity(Props) ->
1214 case proplists:get_value(authzid, Props, <<>>) of
1215 <<>> -> proplists:get_value(username, Props, <<>>);
1216 AuthzId -> AuthzId
1217 end.
1218
1219 -spec sha(binary()) -> binary().
1220 sha(Data) ->
1221 xmpp_util:hex(crypto:hash(sha, Data)).
1222
1223 %%%===================================================================
1224 %%% Callbacks
1225 %%%===================================================================
1226 callback(F, #{mod := Mod} = State) ->
1227 case erlang:function_exported(Mod, F, 1) of
1228 true -> Mod:F(State);
1229 false -> erlang:error({?MODULE, undef})
1230 end.
1231
1232 callback(F, Arg1, #{mod := Mod} = State) ->
1233 case erlang:function_exported(Mod, F, 2) of
1234 true -> Mod:F(Arg1, State);
1235 false -> erlang:error({?MODULE, undef})
1236 end.
1237
1238 callback(code_change, OldVsn, #{mod := Mod} = State, Extra) ->
1239 %% code_change/3 callback is a special snowflake
1240 case erlang:function_exported(Mod, code_change, 3) of
1241 true -> Mod:code_change(OldVsn, State, Extra);
1242 false -> {ok, State}
1243 end;
1244 callback(F, Arg1, Arg2, #{mod := Mod} = State) ->
1245 case erlang:function_exported(Mod, F, 3) of
1246 true -> Mod:F(Arg1, Arg2, State);
1247 false -> erlang:error({?MODULE, undef})
1248 end.
1249
1250 callback(F, Arg1, Arg2, Arg3, #{mod := Mod} = State) ->
1251 case erlang:function_exported(Mod, F, 4) of
1252 true -> Mod:F(Arg1, Arg2, Arg3, State);
1253 false -> erlang:error({?MODULE, undef})
1254 end.
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_stream_out).
18 -define(GEN_SERVER, p1_server).
19 -behaviour(?GEN_SERVER).
20 -dialyzer({no_contracts, h_addr_list_to_host_ports/1}).
21
22 -protocol({rfc, 6120}).
23 -protocol({xep, 114, '1.6'}).
24 -protocol({xep, 368, '1.0.0'}).
25
26 %% API
27 -export([start/3, start_link/3, call/3, cast/2, reply/2, connect/1,
28 stop/1, send/2, close/1, close/2, bind/2, establish/1, format_error/1,
29 set_timeout/2, get_transport/1, change_shaper/2]).
30 %% gen_server callbacks
31 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
32 terminate/2, code_change/3]).
33
34 %%-define(DBGFSM, true).
35 -ifdef(DBGFSM).
36 -define(FSMOPTS, [{debug, [trace]}]).
37 -else.
38 -define(FSMOPTS, []).
39 -endif.
40
41 -define(TCP_SEND_TIMEOUT, 15000).
42 -define(NEGOTIATION_TIMEOUT, 30000).
43 -define(DNS_TIMEOUT, 10000).
44 -define(CONNECT_TIMEOUT, 10000).
45
46 -include("xmpp.hrl").
47 -include_lib("kernel/include/inet.hrl").
48
49 -type state() :: map().
50 -type noreply() :: {noreply, state(), timeout()}.
51 -type host_port() :: {inet:hostname(), inet:port_number(), boolean()} | ip_port().
52 -type ip_port() :: {inet:ip_address(), inet:port_number(), boolean()}.
53 -type h_addr_list() :: {{integer(), integer(), inet:port_number(), string()}, boolean()}.
54 -type network_error() :: {error, inet:posix() | inet_res:res_error()}.
55 -type tls_error_reason() :: inet:posix() | atom() | binary().
56 -type socket_error_reason() :: inet:posix() | atom().
57 -type stop_reason() :: {idna, bad_string} |
58 {dns, inet:posix() | inet_res:res_error()} |
59 {stream, reset | {in | out, stream_error()}} |
60 {tls, tls_error_reason()} |
61 {pkix, binary()} |
62 {auth, atom() | binary() | string()} |
63 {bind, stanza_error() | undefined} |
64 {socket, socket_error_reason()} |
65 internal_failure.
66 -export_type([state/0, stop_reason/0]).
67 -callback init(list()) -> {ok, state()} | {error, term()} | ignore.
68 -callback handle_cast(term(), state()) -> state().
69 -callback handle_call(term(), term(), state()) -> state().
70 -callback handle_info(term(), state()) -> state().
71 -callback terminate(term(), state()) -> any().
72 -callback code_change(term(), state(), term()) -> {ok, state()} | {error, term()}.
73 -callback handle_stream_start(stream_start(), state()) -> state().
74 -callback handle_stream_established(state()) -> state().
75 -callback handle_stream_downgraded(stream_start(), state()) -> state().
76 -callback handle_stream_end(stop_reason(), state()) -> state().
77 -callback handle_cdata(binary(), state()) -> state().
78 -callback handle_send(xmpp_element(), ok | {error, inet:posix()}, state()) -> state().
79 -callback handle_recv(fxml:xmlel(), xmpp_element() | {error, term()}, state()) -> state().
80 -callback handle_timeout(state()) -> state().
81 -callback handle_authenticated_features(stream_features(), state()) -> state().
82 -callback handle_unauthenticated_features(stream_features(), state()) -> state().
83 -callback handle_auth_success(xmpp_sasl:mechanism(), state()) -> state().
84 -callback handle_auth_failure(xmpp_sasl:mechanism(), binary(), state()) -> state().
85 -callback handle_bind_success(state()) -> state().
86 -callback handle_bind_failure(stanza_error() | undefined, state()) -> state().
87 -callback handle_packet(xmpp_element(), state()) -> state().
88 -callback tls_options(state()) -> [proplists:property()].
89 -callback tls_required(state()) -> boolean().
90 -callback tls_verify(state()) -> boolean().
91 -callback tls_enabled(state()) -> boolean().
92 -callback resolve(string(), state()) -> [host_port()].
93 -callback sasl_mechanisms(state()) -> [binary()].
94 -callback dns_timeout(state()) -> timeout().
95 -callback dns_retries(state()) -> non_neg_integer().
96 -callback default_port(state()) -> inet:port_number().
97 -callback connect_options(inet:ip_address(), list(), state()) -> list().
98 -callback address_families(state()) -> [inet:address_family()].
99 -callback connect_timeout(state()) -> timeout().
100
101 -optional_callbacks([init/1,
102 handle_cast/2,
103 handle_call/3,
104 handle_info/2,
105 terminate/2,
106 code_change/3,
107 handle_stream_start/2,
108 handle_stream_established/1,
109 handle_stream_downgraded/2,
110 handle_stream_end/2,
111 handle_cdata/2,
112 handle_send/3,
113 handle_recv/3,
114 handle_timeout/1,
115 handle_authenticated_features/2,
116 handle_unauthenticated_features/2,
117 handle_auth_success/2,
118 handle_auth_failure/3,
119 handle_bind_success/1,
120 handle_bind_failure/2,
121 handle_packet/2,
122 tls_options/1,
123 tls_required/1,
124 tls_verify/1,
125 tls_enabled/1,
126 resolve/2,
127 sasl_mechanisms/1,
128 dns_timeout/1,
129 dns_retries/1,
130 default_port/1,
131 connect_options/3,
132 address_families/1,
133 connect_timeout/1]).
134
135 %%%===================================================================
136 %%% API
137 %%%===================================================================
138 start({local, Mod}, Args, Opts) ->
139 ?GEN_SERVER:start({local, Mod}, ?MODULE, [Mod|Args], Opts ++ ?FSMOPTS);
140 start(Mod, Args, Opts) ->
141 ?GEN_SERVER:start(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS).
142
143 start_link({local, Mod}, Args, Opts) ->
144 ?GEN_SERVER:start_link({local, Mod}, ?MODULE, [Mod|Args], Opts ++ ?FSMOPTS);
145 start_link(Mod, Args, Opts) ->
146 ?GEN_SERVER:start_link(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS).
147
148 call(Ref, Msg, Timeout) ->
149 ?GEN_SERVER:call(Ref, Msg, Timeout).
150
151 cast(Ref, Msg) ->
152 ?GEN_SERVER:cast(Ref, Msg).
153
154 reply(Ref, Reply) ->
155 ?GEN_SERVER:reply(Ref, Reply).
156
157 -spec connect(pid()) -> ok.
158 connect(Ref) ->
159 cast(Ref, connect).
160
161 -spec stop(pid()) -> ok;
162 (state()) -> no_return().
163 stop(Pid) when is_pid(Pid) ->
164 cast(Pid, stop);
165 stop(#{owner := Owner} = State) when Owner == self() ->
166 terminate(normal, State),
167 exit(normal);
168 stop(_) ->
169 erlang:error(badarg).
170
171 -spec send(pid(), xmpp_element()) -> ok;
172 (state(), xmpp_element()) -> state().
173 send(Pid, Pkt) when is_pid(Pid) ->
174 cast(Pid, {send, Pkt});
175 send(#{owner := Owner} = State, Pkt) when Owner == self() ->
176 send_pkt(State, Pkt);
177 send(_, _) ->
178 erlang:error(badarg).
179
180 -spec close(pid()) -> ok;
181 (state()) -> state().
182 close(Pid) when is_pid(Pid) ->
183 close(Pid, closed);
184 close(#{owner := Owner} = State) when Owner == self() ->
185 close_socket(State);
186 close(_) ->
187 erlang:error(badarg).
188
189 -spec close(pid(), atom()) -> ok.
190 close(Pid, Reason) ->
191 cast(Pid, {close, Reason}).
192
193 -spec bind(state(), stream_features()) -> state().
194 bind(#{stream_authenticated := true} = State, StreamFeatures) ->
195 process_bind(StreamFeatures, State).
196
197 -spec establish(state()) -> state().
198 establish(State) ->
199 process_stream_established(State).
200
201 -spec set_timeout(state(), timeout()) -> state().
202 set_timeout(#{owner := Owner} = State, Timeout) when Owner == self() ->
203 case Timeout of
204 infinity -> State#{stream_timeout => infinity};
205 _ ->
206 State#{stream_timeout => current_time() + Timeout}
207 end;
208 set_timeout(_, _) ->
209 erlang:error(badarg).
210
211 get_transport(#{socket := Socket, owner := Owner})
212 when Owner == self() ->
213 xmpp_socket:get_transport(Socket);
214 get_transport(_) ->
215 erlang:error(badarg).
216
217 -spec change_shaper(state(), p1_shaper:state()) -> state().
218 change_shaper(#{socket := Socket, owner := Owner} = State, Shaper)
219 when Owner == self() ->
220 Socket1 = xmpp_socket:change_shaper(Socket, Shaper),
221 State#{socket => Socket1};
222 change_shaper(_, _) ->
223 erlang:error(badarg).
224
225 -spec format_error(stop_reason()) -> binary().
226 format_error({idna, _}) ->
227 <<"Remote domain is not an IDN hostname">>;
228 format_error({dns, Reason}) ->
229 format("DNS lookup failed: ~s", [format_inet_error(Reason)]);
230 format_error({socket, Reason}) ->
231 format("Connection failed: ~s", [format_inet_error(Reason)]);
232 format_error({pkix, Reason}) ->
233 {_, ErrTxt} = xmpp_stream_pkix:format_error(Reason),
234 format("Peer certificate rejected: ~s", [ErrTxt]);
235 format_error({stream, reset}) ->
236 <<"Stream reset by peer">>;
237 format_error({stream, {in, #stream_error{} = Err}}) ->
238 format("Stream closed by peer: ~s", [xmpp:format_stream_error(Err)]);
239 format_error({stream, {out, #stream_error{} = Err}}) ->
240 format("Stream closed by local host: ~s", [xmpp:format_stream_error(Err)]);
241 format_error({bind, undefined}) ->
242 <<"Resource binding failure: peer reported no reason">>;
243 format_error({bind, #stanza_error{} = Err}) ->
244 format("Resource binding failure: ~s", [xmpp:format_stanza_error(Err)]);
245 format_error({tls, Reason}) ->
246 format("TLS failed: ~s", [format_tls_error(Reason)]);
247 format_error({auth, Reason}) ->
248 format("Authentication failed: ~s", [Reason]);
249 format_error(internal_failure) ->
250 <<"Internal server error">>;
251 format_error(Err) ->
252 format("Unrecognized error: ~w", [Err]).
253
254 %%%===================================================================
255 %%% gen_server callbacks
256 %%%===================================================================
257 -spec init(list()) -> {ok, state(), timeout()} | {stop, term()} | ignore.
258 init([Mod, From, To, Opts]) ->
259 State = #{owner => self(),
260 mod => Mod,
261 server => From,
262 user => <<"">>,
263 resource => <<"">>,
264 password => <<"">>,
265 lang => <<"">>,
266 remote_server => To,
267 xmlns => ?NS_SERVER,
268 codec_options => [ignore_els],
269 stream_direction => out,
270 stream_timeout => current_time() + ?NEGOTIATION_TIMEOUT,
271 stream_id => xmpp_stream:new_id(),
272 stream_encrypted => false,
273 stream_verified => false,
274 stream_authenticated => false,
275 stream_restarted => false,
276 stream_state => connecting},
277 case try Mod:init([State, Opts])
278 catch _:undef -> {ok, State}
279 end of
280 {ok, State1} ->
281 {_, State2, Timeout} = noreply(State1),
282 {ok, State2, Timeout};
283 {error, Reason} ->
284 {stop, Reason};
285 ignore ->
286 ignore
287 end.
288
289 -spec handle_call(term(), term(), state()) -> noreply().
290 handle_call(Call, From, State) ->
291 noreply(try callback(handle_call, Call, From, State)
292 catch _:{?MODULE, undef} -> State
293 end).
294
295 -spec handle_cast(term(), state()) -> noreply().
296 handle_cast(connect, #{remote_server := RemoteServer,
297 stream_state := connecting} = State) ->
298 noreply(
299 case idna_to_ascii(RemoteServer) of
300 false ->
301 process_stream_end({idna, bad_string}, State);
302 ASCIIName ->
303 case resolve(binary_to_list(ASCIIName), State) of
304 {ok, AddrPorts} ->
305 case connect(AddrPorts, State) of
306 {ok, Socket, {Addr, Port, Encrypted}} ->
307 SocketMonitor = xmpp_socket:monitor(Socket),
308 State1 = State#{ip => {Addr, Port},
309 socket => Socket,
310 stream_encrypted => Encrypted,
311 socket_monitor => SocketMonitor},
312 State2 = State1#{stream_state => wait_for_stream},
313 send_header(State2);
314 {error, {Class, Why}} ->
315 process_stream_end({Class, Why}, State)
316 end;
317 {error, Why} ->
318 process_stream_end({dns, Why}, State)
319 end
320 end);
321 handle_cast(connect, #{stream_state := disconnected} = State) ->
322 State1 = reset_state(State),
323 handle_cast(connect, State1);
324 handle_cast(connect, State) ->
325 %% Ignoring connection attempts in other states
326 noreply(State);
327 handle_cast({send, Pkt}, State) ->
328 noreply(send_pkt(State, Pkt));
329 handle_cast(stop, State) ->
330 {stop, normal, State};
331 handle_cast({close, Reason}, State) ->
332 State1 = close_socket(State),
333 noreply(
334 case is_disconnected(State) of
335 true -> State1;
336 false -> process_stream_end({socket, Reason}, State)
337 end);
338 handle_cast(Cast, State) ->
339 noreply(try callback(handle_cast, Cast, State)
340 catch _:{?MODULE, undef} -> State
341 end).
342
343 -spec handle_info(term(), state()) -> noreply().
344 handle_info({'$gen_event', {xmlstreamstart, Name, Attrs}},
345 #{stream_state := wait_for_stream,
346 xmlns := XMLNS, lang := MyLang} = State) ->
347 El = #xmlel{name = Name, attrs = Attrs},
348 noreply(
349 try xmpp:decode(El, XMLNS, []) of
350 #stream_start{} = Pkt ->
351 process_stream(Pkt, State);
352 _ ->
353 send_pkt(State, xmpp:serr_invalid_xml())
354 catch _:{xmpp_codec, Why} ->
355 Txt = xmpp:io_format_error(Why),
356 Lang = select_lang(MyLang, xmpp:get_lang(El)),
357 Err = xmpp:serr_invalid_xml(Txt, Lang),
358 send_pkt(State, Err)
359 end);
360 handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
361 State1 = send_header(State),
362 noreply(
363 case is_disconnected(State1) of
364 true -> State1;
365 false ->
366 Err = case Reason of
367 <<"XML stanza is too big">> ->
368 xmpp:serr_policy_violation(Reason, Lang);
369 {_, Txt} ->
370 xmpp:serr_not_well_formed(Txt, Lang)
371 end,
372 send_pkt(State1, Err)
373 end);
374 handle_info({'$gen_event', {xmlstreamelement, El}},
375 #{xmlns := NS, codec_options := Opts} = State) ->
376 noreply(
377 try xmpp:decode(El, NS, Opts) of
378 Pkt ->
379 State1 = try callback(handle_recv, El, Pkt, State)
380 catch _:{?MODULE, undef} -> State
381 end,
382 case is_disconnected(State1) of
383 true -> State1;
384 false -> process_element(Pkt, State1)
385 end
386 catch _:{xmpp_codec, Why} ->
387 State1 = try callback(handle_recv, El, {error, Why}, State)
388 catch _:{?MODULE, undef} -> State
389 end,
390 case is_disconnected(State1) of
391 true -> State1;
392 false -> process_invalid_xml(State1, El, Why)
393 end
394 end);
395 handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}}, State) ->
396 noreply(try callback(handle_cdata, Data, State)
397 catch _:{?MODULE, undef} -> State
398 end);
399 handle_info({'$gen_event', {xmlstreamend, _}}, State) ->
400 noreply(process_stream_end({stream, reset}, State));
401 handle_info({'$gen_event', closed}, State) ->
402 noreply(process_stream_end({socket, closed}, State));
403 handle_info(timeout, #{lang := Lang} = State) ->
404 Disconnected = is_disconnected(State),
405 noreply(try callback(handle_timeout, State)
406 catch _:{?MODULE, undef} when not Disconnected ->
407 Txt = <<"Idle connection">>,
408 send_pkt(State, xmpp:serr_connection_timeout(Txt, Lang));
409 _:{?MODULE, undef} ->
410 stop(State)
411 end);
412 handle_info({'DOWN', MRef, _Type, _Object, _Info},
413 #{socket_monitor := MRef} = State) ->
414 noreply(process_stream_end({socket, closed}, State));
415 handle_info({tcp, _, Data}, #{socket := Socket} = State) ->
416 noreply(
417 case xmpp_socket:recv(Socket, Data) of
418 {ok, NewSocket} ->
419 State#{socket => NewSocket};
420 {error, Reason} when is_atom(Reason) ->
421 process_stream_end({socket, Reason}, State);
422 {error, Reason} ->
423 %% TODO: make fast_tls return atoms
424 process_stream_end({tls, Reason}, State)
425 end);
426 handle_info({tcp_closed, _}, State) ->
427 handle_info({'$gen_event', closed}, State);
428 handle_info({tcp_error, _, Reason}, State) ->
429 noreply(process_stream_end({socket, Reason}, State));
430 handle_info({'EXIT', _, Reason}, State) ->
431 {stop, Reason, State};
432 handle_info(Info, State) ->
433 noreply(try callback(handle_info, Info, State)
434 catch _:{?MODULE, undef} -> State
435 end).
436
437 -spec terminate(term(), state()) -> any().
438 terminate(Reason, State) ->
439 case get(already_terminated) of
440 true ->
441 State;
442 _ ->
443 put(already_terminated, true),
444 try callback(terminate, Reason, State)
445 catch _:{?MODULE, undef} -> ok
446 end,
447 send_trailer(State)
448 end.
449
450 code_change(OldVsn, State, Extra) ->
451 callback(code_change, OldVsn, State, Extra).
452
453 %%%===================================================================
454 %%% Internal functions
455 %%%===================================================================
456 -spec noreply(state()) -> noreply().
457 noreply(State) ->
458 {noreply, State, get_timeout(State)}.
459
460 -spec is_disconnected(state()) -> boolean().
461 is_disconnected(#{stream_state := StreamState}) ->
462 StreamState == disconnected.
463
464 -spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state().
465 process_invalid_xml(#{lang := MyLang} = State, El, Reason) ->
466 case xmpp:is_stanza(El) of
467 true ->
468 Txt = xmpp:io_format_error(Reason),
469 Lang = select_lang(MyLang, xmpp:get_lang(El)),
470 send_error(State, El, xmpp:err_bad_request(Txt, Lang));
471 false ->
472 State
473 end.
474
475 -spec process_stream_end(stop_reason(), state()) -> state().
476 process_stream_end(_, #{stream_state := disconnected} = State) ->
477 State;
478 process_stream_end(Reason, State) ->
479 State1 = send_trailer(State),
480 try callback(handle_stream_end, Reason, State1)
481 catch _:{?MODULE, undef} -> stop(State1)
482 end.
483
484 -spec process_stream(stream_start(), state()) -> state().
485 process_stream(#stream_start{xmlns = XML_NS,
486 stream_xmlns = STREAM_NS},
487 #{xmlns := NS} = State)
488 when XML_NS /= NS; STREAM_NS /= ?NS_STREAM ->
489 send_pkt(State, xmpp:serr_invalid_namespace());
490 process_stream(#stream_start{version = {N, _}}, State) when N > 1 ->
491 send_pkt(State, xmpp:serr_unsupported_version());
492 process_stream(#stream_start{lang = Lang, id = ID,
493 version = Version} = StreamStart,
494 State) ->
495 State1 = State#{stream_remote_id => ID, lang => Lang},
496 State2 = try callback(handle_stream_start, StreamStart, State1)
497 catch _:{?MODULE, undef} -> State1
498 end,
499 case is_disconnected(State2) of
500 true -> State2;
501 false ->
502 case Version of
503 {1, _} ->
504 State2#{stream_state => wait_for_features};
505 _ ->
506 process_stream_downgrade(StreamStart, State2)
507 end
508 end.
509
510 -spec process_element(xmpp_element(), state()) -> state().
511 process_element(Pkt, #{stream_state := StateName} = State) ->
512 case Pkt of
513 #stream_features{} when StateName == wait_for_features ->
514 process_features(Pkt, State);
515 #starttls_proceed{} when StateName == wait_for_starttls_response ->
516 process_starttls(State);
517 #sasl_success{} when StateName == wait_for_sasl_response ->
518 process_sasl_success(State);
519 #sasl_failure{} when StateName == wait_for_sasl_response ->
520 process_sasl_failure(Pkt, State);
521 #stream_error{} ->
522 process_stream_end({stream, {in, Pkt}}, State);
523 _ when is_record(Pkt, stream_features);
524 is_record(Pkt, starttls_proceed);
525 is_record(Pkt, starttls);
526 is_record(Pkt, sasl_auth);
527 is_record(Pkt, sasl_success);
528 is_record(Pkt, sasl_failure);
529 is_record(Pkt, sasl_response);
530 is_record(Pkt, sasl_abort);
531 is_record(Pkt, compress);
532 is_record(Pkt, handshake) ->
533 %% Do not pass this crap upstream
534 State;
535 _ when StateName == wait_for_bind_response ->
536 process_bind_response(Pkt, State);
537 _ when StateName == wait_for_session_response ->
538 process_session_response(Pkt, State);
539 _ ->
540 process_packet(Pkt, State)
541 end.
542
543 -spec process_features(stream_features(), state()) -> state().
544 process_features(StreamFeatures,
545 #{stream_authenticated := true} = State) ->
546 try callback(handle_authenticated_features, StreamFeatures, State)
547 catch _:{?MODULE, undef} -> process_bind(StreamFeatures, State)
548 end;
549 process_features(StreamFeatures,
550 #{stream_encrypted := Encrypted, lang := Lang} = State) ->
551 State1 = try callback(handle_unauthenticated_features, StreamFeatures, State)
552 catch _:{?MODULE, undef} -> State
553 end,
554 case is_disconnected(State1) of
555 true -> State1;
556 false ->
557 TLSRequired = is_starttls_required(State1),
558 TLSAvailable = is_starttls_available(State1),
559 try xmpp:try_subtag(StreamFeatures, #starttls{}) of
560 false when TLSRequired and not Encrypted ->
561 Txt = <<"Use of STARTTLS required">>,
562 send_pkt(State1, xmpp:serr_policy_violation(Txt, Lang));
563 #starttls{required = true} when not TLSAvailable and not Encrypted ->
564 Txt = <<"STARTTLS is disabled in local configuration">>,
565 send_pkt(State1, xmpp:serr_unsupported_feature(Txt, Lang));
566 #starttls{} when TLSAvailable and not Encrypted ->
567 State2 = State1#{stream_state => wait_for_starttls_response},
568 send_pkt(State2, #starttls{});
569 _ ->
570 State2 = process_cert_verification(State1),
571 case is_disconnected(State2) of
572 true -> State2;
573 false -> process_sasl_mechanisms(StreamFeatures, State2)
574 end
575 catch _:{xmpp_codec, Why} ->
576 Txt = xmpp:io_format_error(Why),
577 send_pkt(State1, xmpp:serr_invalid_xml(Txt, Lang))
578 end
579 end.
580
581 -spec process_stream_established(state()) -> state().
582 process_stream_established(#{stream_state := StateName} = State)
583 when StateName == disconnected; StateName == established ->
584 State;
585 process_stream_established(State) ->
586 State1 = State#{stream_authenticated := true,
587 stream_state => established,
588 stream_timeout => infinity},
589 try callback(handle_stream_established, State1)
590 catch _:{?MODULE, undef} -> State1
591 end.
592
593 -spec process_sasl_mechanisms(stream_features(), state()) -> state().
594 process_sasl_mechanisms(StreamFeatures, State) ->
595 AvailMechs = sasl_mechanisms(State),
596 State1 = State#{sasl_mechs_available => AvailMechs},
597 try xmpp:try_subtag(StreamFeatures, #sasl_mechanisms{}) of
598 #sasl_mechanisms{list = ProvidedMechs} ->
599 process_sasl_auth(State1#{sasl_mechs_provided => ProvidedMechs});
600 false ->
601 process_sasl_auth(State1#{sasl_mechs_provided => []})
602 catch _:{xmpp_codec, Why} ->
603 Txt = xmpp:io_format_error(Why),
604 Lang = maps:get(lang, State),
605 send_pkt(State, xmpp:serr_invalid_xml(Txt, Lang))
606 end.
607
608 process_sasl_auth(#{stream_encrypted := false, xmlns := ?NS_SERVER} = State) ->
609 State1 = State#{sasl_mechs_available => []},
610 Txt = case is_starttls_available(State) of
611 true -> <<"Peer doesn't support STARTTLS">>;
612 false -> <<"STARTTLS is disabled in local configuration">>
613 end,
614 process_sasl_failure(Txt, State1);
615 process_sasl_auth(#{sasl_mechs_provided := [],
616 stream_encrypted := Encrypted} = State) ->
617 State1 = State#{sasl_mechs_available => []},
618 Hint = case Encrypted of
619 true -> <<"; most likely it doesn't accept our certificate">>;
620 false -> <<"">>
621 end,
622 Txt = <<"Peer provided no SASL mechanisms", Hint/binary>>,
623 process_sasl_failure(Txt, State1);
624 process_sasl_auth(#{sasl_mechs_available := []} = State) ->
625 Err = maps:get(sasl_error, State,
626 <<"No mutually supported SASL mechanisms found">>),
627 process_sasl_failure(Err, State);
628 process_sasl_auth(#{sasl_mechs_available := [Mech|AvailMechs],
629 sasl_mechs_provided := ProvidedMechs} = State) ->
630 State1 = State#{sasl_mechs_available => AvailMechs},
631 if Mech == <<"EXTERNAL">> orelse Mech == <<"PLAIN">> ->
632 case lists:member(Mech, ProvidedMechs) of
633 true ->
634 Text = make_sasl_authzid(Mech, State1),
635 State2 = State1#{sasl_mech => Mech,
636 stream_state => wait_for_sasl_response},
637 send(State2, #sasl_auth{mechanism = Mech, text = Text});
638 false ->
639 process_sasl_auth(State1)
640 end;
641 true ->
642 process_sasl_auth(State1)
643 end.
644
645 -spec process_starttls(state()) -> state().
646 process_starttls(#{socket := Socket} = State) ->
647 case starttls(Socket, State) of
648 {ok, TLSSocket} ->
649 State1 = State#{socket => TLSSocket,
650 stream_id => xmpp_stream:new_id(),
651 stream_restarted => true,
652 stream_state => wait_for_stream,
653 stream_encrypted => true},
654 send_header(State1);
655 {error, Why} ->
656 process_stream_end({tls, Why}, State)
657 end.
658
659 -spec process_stream_downgrade(stream_start(), state()) -> state().
660 process_stream_downgrade(StreamStart,
661 #{lang := Lang,
662 stream_encrypted := Encrypted} = State) ->
663 TLSRequired = is_starttls_required(State),
664 if not Encrypted and TLSRequired ->
665 Txt = <<"Use of STARTTLS required">>,
666 send_pkt(State, xmpp:serr_policy_violation(Txt, Lang));
667 true ->
668 State1 = State#{stream_state => downgraded},
669 try callback(handle_stream_downgraded, StreamStart, State1)
670 catch _:{?MODULE, undef} ->
671 send_pkt(State1, xmpp:serr_unsupported_version())
672 end
673 end.
674
675 -spec process_cert_verification(state()) -> state().
676 process_cert_verification(#{stream_encrypted := true,
677 stream_verified := false} = State) ->
678 case try callback(tls_verify, State)
679 catch _:{?MODULE, undef} -> true
680 end of
681 true ->
682 case xmpp_stream_pkix:authenticate(State) of
683 {ok, _} ->
684 State#{stream_verified => true};
685 {error, Why, _Peer} ->
686 process_stream_end({pkix, Why}, State)
687 end;
688 false ->
689 State#{stream_verified => true}
690 end;
691 process_cert_verification(State) ->
692 State.
693
694 -spec process_sasl_success(state()) -> state().
695 process_sasl_success(#{socket := Socket, sasl_mech := Mech} = State) ->
696 Socket1 = xmpp_socket:reset_stream(Socket),
697 State1 = State#{socket => Socket1},
698 State2 = State1#{stream_id => xmpp_stream:new_id(),
699 stream_restarted => true,
700 stream_state => wait_for_stream,
701 stream_authenticated => true},
702 State3 = reset_sasl_state(State2),
703 State4 = send_header(State3),
704 case is_disconnected(State4) of
705 true -> State4;
706 false ->
707 try callback(handle_auth_success, Mech, State4)
708 catch _:{?MODULE, undef} -> State4
709 end
710 end.
711
712 -spec process_sasl_failure(sasl_failure() | binary(), state()) -> state().
713 process_sasl_failure(Failure, #{sasl_mechs_available := [_|_]} = State) ->
714 process_sasl_auth(State#{sasl_failure => Failure});
715 process_sasl_failure(#sasl_failure{} = Failure, State) ->
716 Reason = format("Peer responded with error: ~s",
717 [xmpp:format_sasl_error(Failure)]),
718 process_sasl_failure(Reason, State);
719 process_sasl_failure(Reason, State) ->
720 Mech = case maps:get(sasl_mech, State, undefined) of
721 undefined ->
722 case sasl_mechanisms(State) of
723 [] -> <<"EXTERNAL">>;
724 [M|_] -> M
725 end;
726 M -> M
727 end,
728 State1 = reset_sasl_state(State),
729 try callback(handle_auth_failure, Mech, {auth, Reason}, State1)
730 catch _:{?MODULE, undef} -> process_stream_end({auth, Reason}, State1)
731 end.
732
733 -spec process_bind(stream_features(), state()) -> state().
734 process_bind(StreamFeatures, #{lang := Lang, xmlns := ?NS_CLIENT,
735 resource := R,
736 stream_state := StateName} = State)
737 when StateName /= established, StateName /= disconnected ->
738 case xmpp:has_subtag(StreamFeatures, #bind{}) of
739 true ->
740 ID = xmpp_stream:new_id(),
741 Pkt = #iq{id = ID, type = set,
742 sub_els = [#bind{resource = R}]},
743 State1 = State#{stream_state => wait_for_bind_response,
744 bind_id => ID},
745 try xmpp:try_subtag(StreamFeatures, #xmpp_session{}) of
746 #xmpp_session{optional = false} ->
747 SID = xmpp_stream:new_id(),
748 State2 = State1#{session_response_id => SID},
749 send_pkt(State2, Pkt);
750 _ ->
751 send_pkt(State1, Pkt)
752 catch _:{xmpp_codec, Why} ->
753 Txt = xmpp:io_format_error(Why),
754 send_pkt(State, xmpp:serr_invalid_xml(Txt, Lang))
755 end;
756 false ->
757 Txt = <<"Missing resource binding feature">>,
758 send_pkt(State, xmpp:serr_invalid_xml(Txt, Lang))
759 end;
760 process_bind(_, State) ->
761 process_stream_established(State).
762
763 -spec process_bind_response(xmpp_element(), state()) -> state().
764 process_bind_response(#iq{type = result, id = ID} = IQ,
765 #{lang := Lang, bind_id := ID} = State) ->
766 State1 = reset_bind_state(State),
767 try xmpp:try_subtag(IQ, #bind{}) of
768 #bind{jid = #jid{user = U, server = S, resource = R}} ->
769 State2 = State1#{user => U, server => S, resource => R},
770 case maps:get(session_response_id, State2, undefined) of
771 undefined ->
772 State3 = try callback(handle_bind_success, State2)
773 catch _:{?MODULE, undef} -> State2
774 end,
775 process_stream_established(State3);
776 SID ->
777 Pkt = #iq{id = SID, type = set, to = jid:make(S),
778 sub_els = [#xmpp_session{}]},
779 State3 = State2#{stream_state => wait_for_session_response},
780 send_pkt(State3, Pkt)
781 end;
782 #bind{} ->
783 Txt = <<"Missing <jid/> element in resource binding response">>,
784 send_pkt(State1, xmpp:serr_invalid_xml(Txt, Lang));
785 false ->
786 Txt = <<"Missing <bind/> element in resource binding response">>,
787 send_pkt(State1, xmpp:serr_invalid_xml(Txt, Lang))
788 catch _:{xmpp_codec, Why} ->
789 Txt = xmpp:io_format_error(Why),
790 send_pkt(State1, xmpp:serr_invalid_xml(Txt, Lang))
791 end;
792 process_bind_response(#iq{type = error, id = ID} = IQ,
793 #{bind_id := ID} = State) ->
794 Err = xmpp:get_error(IQ),
795 State1 = reset_bind_state(State),
796 State2 = reset_session_state(State1),
797 try callback(handle_bind_failure, Err, State2)
798 catch _:{?MODULE, undef} -> process_stream_end({bind, Err}, State2)
799 end;
800 process_bind_response(Pkt, State) ->
801 process_packet(Pkt, State).
802
803 -spec process_session_response(xmpp_element(), state()) -> state().
804 process_session_response(#iq{type = result, id = ID},
805 #{session_response_id := ID} = State) ->
806 State1 = reset_session_state(State),
807 State2 = try callback(handle_bind_success, State1)
808 catch _:{?MODULE, undef} -> State1
809 end,
810 process_stream_established(State2);
811 process_session_response(#iq{type = error, id = ID} = IQ,
812 #{session_response_id := ID} = State) ->
813 State1 = reset_session_state(State),
814 Err = xmpp:get_error(IQ),
815 try callback(handle_bind_failure, Err, State1)
816 catch _:{?MODULE, undef} -> process_stream_end({bind, Err}, State1)
817 end;
818 process_session_response(Pkt, State) ->
819 process_packet(Pkt, State).
820
821 -spec process_packet(xmpp_element(), state()) -> state().
822 process_packet(Pkt, State) ->
823 Pkt1 = fix_from(Pkt, State),
824 try callback(handle_packet, Pkt1, State)
825 catch _:{?MODULE, undef} -> State
826 end.
827
828 -spec is_starttls_required(state()) -> boolean().
829 is_starttls_required(State) ->
830 try callback(tls_required, State)
831 catch _:{?MODULE, undef} -> false
832 end.
833
834 -spec is_starttls_available(state()) -> boolean().
835 is_starttls_available(State) ->
836 try callback(tls_enabled, State)
837 catch _:{?MODULE, undef} -> true
838 end.
839
840 -spec sasl_mechanisms(state()) -> [binary()].
841 sasl_mechanisms(#{stream_encrypted := Encrypted} = State) ->
842 try callback(sasl_mechanisms, State) of
843 Ms when Encrypted -> Ms;
844 Ms -> lists:delete(<<"EXTERNAL">>, Ms)
845 catch _:{?MODULE, undef} ->
846 if Encrypted -> [<<"EXTERNAL">>];
847 true -> []
848 end
849 end.
850
851 -spec send_header(state()) -> state().
852 send_header(#{remote_server := RemoteServer,
853 stream_encrypted := Encrypted,
854 lang := Lang,
855 xmlns := NS,
856 user := User,
857 resource := Resource,
858 server := Server} = State) ->
859 NS_DB = if NS == ?NS_SERVER -> ?NS_SERVER_DIALBACK;
860 true -> <<"">>
861 end,
862 From = if Encrypted ->
863 jid:make(User, Server, Resource);
864 NS == ?NS_SERVER ->
865 jid:make(Server);
866 true ->
867 undefined
868 end,
869 StreamStart = #stream_start{xmlns = NS,
870 lang = Lang,
871 stream_xmlns = ?NS_STREAM,
872 db_xmlns = NS_DB,
873 from = From,
874 to = jid:make(RemoteServer),
875 version = {1,0}},
876 case socket_send(State, StreamStart) of
877 ok -> State;
878 {error, Why} -> process_stream_end({socket, Why}, State)
879 end.
880
881 -spec send_pkt(state(), xmpp_element() | xmlel()) -> state().
882 send_pkt(State, Pkt) ->
883 Result = socket_send(State, Pkt),
884 State1 = try callback(handle_send, Pkt, Result, State)
885 catch _:{?MODULE, undef} -> State
886 end,
887 case Result of
888 _ when is_record(Pkt, stream_error) ->
889 process_stream_end({stream, {out, Pkt}}, State1);
890 ok ->
891 State1;
892 {error, _Why} ->
893 % Queue process_stream_end instead of calling it directly,
894 % so we have opurtunity to process incoming queued messages before
895 % terminating session.
896 self() ! {'$gen_event', closed},
897 State1
898 end.
899
900 -spec send_error(state(), xmpp_element() | xmlel(), stanza_error()) -> state().
901 send_error(State, Pkt, Err) ->
902 case xmpp:is_stanza(Pkt) of
903 true ->
904 case xmpp:get_type(Pkt) of
905 result -> State;
906 error -> State;
907 <<"result">> -> State;
908 <<"error">> -> State;
909 _ ->
910 ErrPkt = xmpp:make_error(Pkt, Err),
911 send_pkt(State, ErrPkt)
912 end;
913 false ->
914 State
915 end.
916
917 -spec socket_send(state(), xmpp_element() | xmlel() | trailer) -> ok | {error, inet:posix()}.
918 socket_send(#{socket := Socket, xmlns := NS,
919 stream_state := StateName}, Pkt) ->
920 case Pkt of
921 trailer ->
922 xmpp_socket:send_trailer(Socket);
923 #stream_start{} when StateName /= disconnected ->
924 xmpp_socket:send_header(Socket, xmpp:encode(Pkt));
925 _ when StateName /= disconnected ->
926 xmpp_socket:send_element(Socket, xmpp:encode(Pkt, NS));
927 _ ->
928 {error, closed}
929 end;
930 socket_send(_, _) ->
931 {error, closed}.
932
933 -spec send_trailer(state()) -> state().
934 send_trailer(State) ->
935 socket_send(State, trailer),
936 close_socket(State).
937
938 -spec close_socket(state()) -> state().
939 close_socket(State) ->
940 case State of
941 #{socket := Socket} ->
942 xmpp_socket:close(Socket);
943 _ ->
944 ok
945 end,
946 State#{stream_timeout => infinity,
947 stream_state => disconnected}.
948
949 -spec starttls(term(), state()) -> {ok, term()} | {error, tls_error_reason()}.
950 starttls(Socket, #{xmlns := NS,
951 remote_server := RemoteServer} = State) ->
952 TLSOpts = try callback(tls_options, State)
953 catch _:{?MODULE, undef} -> []
954 end,
955 SNI = idna_to_ascii(RemoteServer),
956 ALPN = case NS of
957 ?NS_SERVER -> <<"xmpp-server">>;
958 ?NS_CLIENT -> <<"xmpp-client">>
959 end,
960 xmpp_socket:starttls(Socket, [connect, {sni, SNI}, {alpn, [ALPN]}|TLSOpts]).
961
962 -spec select_lang(binary(), binary()) -> binary().
963 select_lang(Lang, <<"">>) -> Lang;
964 select_lang(_, Lang) -> Lang.
965
966 -spec format_inet_error(atom()) -> string().
967 format_inet_error(closed) ->
968 "connection closed";
969 format_inet_error(Reason) ->
970 case inet:format_error(Reason) of
971 "unknown POSIX error" -> atom_to_list(Reason);
972 Txt -> Txt
973 end.
974
975 -spec format_tls_error(atom() | binary()) -> list().
976 format_tls_error(Reason) when is_atom(Reason) ->
977 format_inet_error(Reason);
978 format_tls_error(Reason) ->
979 binary_to_list(Reason).
980
981 -spec format(io:format(), list()) -> binary().
982 format(Fmt, Args) ->
983 iolist_to_binary(io_lib:format(Fmt, Args)).
984
985 -spec make_sasl_authzid(binary(), state()) -> binary().
986 make_sasl_authzid(Mech, #{user := User, server := Server,
987 password := Password}) ->
988 case Mech of
989 <<"EXTERNAL">> ->
990 jid:encode(jid:make(User, Server));
991 <<"PLAIN">> ->
992 JID = jid:encode(jid:make(User, Server)),
993 <<JID/binary, 0, User/binary, 0, Password/binary>>
994 end.
995 -spec fix_from(xmpp_element(), state()) -> xmpp_element().
996 fix_from(Pkt, #{xmlns := ?NS_CLIENT} = State) ->
997 case xmpp:is_stanza(Pkt) of
998 true ->
999 case xmpp:get_from(Pkt) of
1000 undefined ->
1001 #{user := U, server := S, resource := R} = State,
1002 From = jid:make(U, S, R),
1003 xmpp:set_from(Pkt, From);
1004 _ ->
1005 Pkt
1006 end;
1007 false ->
1008 Pkt
1009 end;
1010 fix_from(Pkt, _State) ->
1011 Pkt.
1012
1013 -spec current_time() -> integer().
1014 current_time() ->
1015 p1_time_compat:monotonic_time(milli_seconds).
1016
1017 -spec get_timeout(state()) -> timeout().
1018 get_timeout(#{stream_timeout := ExpireTime}) ->
1019 case ExpireTime of
1020 infinity -> infinity;
1021 _ -> max(0, ExpireTime - current_time())
1022 end.
1023
1024 %%%===================================================================
1025 %%% State resets
1026 %%%===================================================================
1027 -spec reset_sasl_state(state()) -> state().
1028 reset_sasl_state(State) ->
1029 State1 = maps:remove(sasl_mech, State),
1030 State2 = maps:remove(sasl_failure, State1),
1031 State3 = maps:remove(sasl_mechs_provided, State2),
1032 maps:remove(sasl_mechs_available, State3).
1033
1034 -spec reset_connection_state(state()) -> state().
1035 reset_connection_state(State) ->
1036 State1 = maps:remove(ip, State),
1037 State2 = maps:remove(socket, State1),
1038 maps:remove(socket_monitor, State2).
1039
1040 -spec reset_stream_state(state()) -> state().
1041 reset_stream_state(State) ->
1042 State1 = State#{stream_id => xmpp_stream:new_id(),
1043 stream_encrypted => false,
1044 stream_verified => false,
1045 stream_authenticated => false,
1046 stream_restarted => false,
1047 stream_state => connecting},
1048 maps:remove(stream_remote_id, State1).
1049
1050 -spec reset_bind_state(state()) -> state().
1051 reset_bind_state(State) ->
1052 maps:remove(bind_id, State).
1053
1054 -spec reset_session_state(state()) -> state().
1055 reset_session_state(State) ->
1056 maps:remove(session_response_id, State).
1057
1058 -spec reset_state(state()) -> state().
1059 reset_state(State) ->
1060 State1 = reset_bind_state(State),
1061 State2 = reset_sasl_state(State1),
1062 State3 = reset_connection_state(State2),
1063 State4 = reset_session_state(State3),
1064 reset_stream_state(State4).
1065
1066 %%%===================================================================
1067 %%% Connection stuff
1068 %%%===================================================================
1069 -spec idna_to_ascii(binary()) -> binary() | false.
1070 idna_to_ascii(<<$[, _/binary>> = Host) ->
1071 %% This is an IPv6 address in 'IP-literal' format (as per RFC7622)
1072 %% We remove brackets here
1073 case binary:last(Host) of
1074 $] ->
1075 IPv6 = binary:part(Host, {1, size(Host)-2}),
1076 case inet:parse_ipv6strict_address(binary_to_list(IPv6)) of
1077 {ok, _} -> IPv6;
1078 {error, _} -> false
1079 end;
1080 _ ->
1081 false
1082 end;
1083 idna_to_ascii(Host) ->
1084 case inet:parse_address(binary_to_list(Host)) of
1085 {ok, _} -> Host;
1086 {error, _} -> xmpp_idna:domain_utf8_to_ascii(Host)
1087 end.
1088
1089 -spec resolve(string(), state()) -> {ok, [ip_port()]} | network_error().
1090 resolve(Host, State) ->
1091 try callback(resolve, Host, State) of
1092 [] ->
1093 do_resolve(Host, State);
1094 HostPorts ->
1095 a_lookup(HostPorts, State)
1096 catch _:{?MODULE, undef} ->
1097 do_resolve(Host, State)
1098 end.
1099
1100 -spec do_resolve(string(), state()) -> {ok, [ip_port()]} | network_error().
1101 do_resolve(Host, State) ->
1102 case srv_lookup(Host, State) of
1103 {error, _Reason} ->
1104 DefaultPort = get_default_port(State),
1105 a_lookup([{Host, DefaultPort, false}], State);
1106 {ok, HostPorts} ->
1107 a_lookup(HostPorts, State)
1108 end.
1109
1110 -spec srv_lookup(string(), state()) -> {ok, [host_port()]} | network_error().
1111 srv_lookup(_Host, #{xmlns := ?NS_COMPONENT}) ->
1112 %% Do not attempt to lookup SRV for component connections
1113 {error, nxdomain};
1114 srv_lookup(Host, State) ->
1115 %% Only perform SRV lookups for FQDN names
1116 case string:chr(Host, $.) of
1117 0 ->
1118 {error, nxdomain};
1119 _ ->
1120 case inet:parse_address(Host) of
1121 {ok, _} ->
1122 {error, nxdomain};
1123 {error, _} ->
1124 case do_srv_lookup(Host, State) of
1125 {ok, AddrList} ->
1126 h_addr_list_to_host_ports(AddrList);
1127 {error, _} = Err ->
1128 Err
1129 end
1130 end
1131 end.
1132
1133 -spec do_srv_lookup(string(), state()) ->
1134 {ok, h_addr_list()} | network_error().
1135 do_srv_lookup(Host, #{xmlns := NS} = State) ->
1136 Retries = get_dns_retries(State),
1137 SRVType = case NS of
1138 ?NS_SERVER -> "-server._tcp.";
1139 ?NS_CLIENT -> "-client._tcp."
1140 end,
1141 TLSAddrs = case is_starttls_available(State) of
1142 true ->
1143 case do_srv_lookup("_xmpps" ++ SRVType ++ Host,
1144 State, Retries) of
1145 {ok, HostEnt} ->
1146 [{A, true} || A <- HostEnt#hostent.h_addr_list];
1147 {error, _} ->
1148 []
1149 end;
1150 false ->
1151 []
1152 end,
1153 case do_srv_lookup("_xmpp" ++ SRVType ++ Host, State, Retries) of
1154 {ok, HostEntry} ->
1155 Addrs = [{A, false} || A <- HostEntry#hostent.h_addr_list],
1156 {ok, TLSAddrs ++ Addrs};
1157 {error, _} when TLSAddrs /= [] ->
1158 {ok, TLSAddrs};
1159 {error, _} = Err ->
1160 Err
1161 end.
1162
1163 -spec do_srv_lookup(string(), state(), integer()) ->
1164 {ok, inet:hostent()} | network_error().
1165 do_srv_lookup(_SRVName, _State, Retries) when Retries < 1 ->
1166 {error, timeout};
1167 do_srv_lookup(SRVName, State, Retries) ->
1168 Timeout = get_dns_timeout(State),
1169 case inet_res:getbyname(SRVName, srv, Timeout) of
1170 {ok, HostEntry} ->
1171 {ok, HostEntry};
1172 {error, timeout} ->
1173 do_srv_lookup(SRVName, State, Retries - 1);
1174 {error, _} = Err ->
1175 Err
1176 end.
1177
1178 -spec a_lookup([host_port()], state()) ->
1179 {ok, [ip_port()]} | network_error().
1180 a_lookup(HostPorts, State) ->
1181 HostPortFamilies = [{Host, Port, TLS, Family}
1182 || {Host, Port, TLS} <- HostPorts,
1183 Family <- get_address_families(State)],
1184 a_lookup(HostPortFamilies, State, [], {error, nxdomain}).
1185
1186 -spec a_lookup([{inet:hostname() | inet:ip_address(), inet:port_number(),
1187 boolean(), inet:address_family()}],
1188 state(), [ip_port()], network_error()) -> {ok, [ip_port()]} | network_error().
1189 a_lookup([{Addr, Port, TLS, Family}|HostPortFamilies], State, Acc, Err)
1190 when is_tuple(Addr) ->
1191 Acc1 = if tuple_size(Addr) == 4 andalso Family == inet ->
1192 [{Addr, Port, TLS}|Acc];
1193 tuple_size(Addr) == 8 andalso Family == inet6 ->
1194 [{Addr, Port, TLS}|Acc];
1195 true ->
1196 Acc
1197 end,
1198 a_lookup(HostPortFamilies, State, Acc1, Err);
1199 a_lookup([{Host, Port, TLS, Family}|HostPortFamilies], State, Acc, Err) ->
1200 Retries = get_dns_retries(State),
1201 case a_lookup(Host, Port, TLS, Family, State, Retries) of
1202 {error, Reason} ->
1203 a_lookup(HostPortFamilies, State, Acc, {error, Reason});
1204 {ok, AddrPorts} ->
1205 a_lookup(HostPortFamilies, State, Acc ++ AddrPorts, Err)
1206 end;
1207 a_lookup([], _State, [], Err) ->
1208 Err;
1209 a_lookup([], _State, Acc, _) ->
1210 {ok, Acc}.
1211
1212 -spec a_lookup(inet:hostname(), inet:port_number(), boolean(), inet:address_family(),
1213 state(), integer()) -> {ok, [ip_port()]} | network_error().
1214 a_lookup(_Host, _Port, _TLS, _Family, _State, Retries) when Retries < 1 ->
1215 {error, timeout};
1216 a_lookup(Host, Port, TLS, Family, State, Retries) ->
1217 Timeout = get_dns_timeout(State),
1218 Start = current_time(),
1219 case inet:gethostbyname(Host, Family, Timeout) of
1220 {error, nxdomain} = Err ->
1221 %% inet:gethostbyname/3 doesn't return {error, timeout},
1222 %% so we should check if 'nxdomain' is in fact a result
1223 %% of a timeout.
1224 %% We also cannot use inet_res:gethostbyname/3 because
1225 %% it ignores DNS configuration settings (e.g. /etc/hosts)
1226 End = current_time(),
1227 if (End - Start) >= Timeout ->
1228 a_lookup(Host, Port, TLS, Family, State, Retries - 1);
1229 true ->
1230 Err
1231 end;
1232 {error, _} = Err ->
1233 Err;
1234 {ok, HostEntry} ->
1235 host_entry_to_addr_ports(HostEntry, Port, TLS)
1236 end.
1237
1238 -spec h_addr_list_to_host_ports(h_addr_list()) -> {ok, [host_port()]} |
1239 {error, nxdomain}.
1240 h_addr_list_to_host_ports(AddrList) ->
1241 PrioHostPorts = lists:flatmap(
1242 fun({{Priority, Weight, Port, Host}, TLS}) ->
1243 N = case Weight of
1244 0 -> 0;
1245 _ -> (Weight + 1) * p1_rand:uniform()
1246 end,
1247 [{Priority * 65536 - N, Host, Port, TLS}];
1248 (_) ->
1249 []
1250 end, AddrList),
1251 HostPorts = [{Host, Port, TLS}
1252 || {_Priority, Host, Port, TLS} <- lists:usort(PrioHostPorts)],
1253 case HostPorts of
1254 [] -> {error, nxdomain};
1255 _ -> {ok, HostPorts}
1256 end.
1257
1258 -spec host_entry_to_addr_ports(inet:hostent(), inet:port_number(), boolean()) ->
1259 {ok, [ip_port()]} | {error, nxdomain}.
1260 host_entry_to_addr_ports(#hostent{h_addr_list = AddrList}, Port, TLS) ->
1261 AddrPorts = lists:flatmap(
1262 fun(Addr) ->
1263 try get_addr_type(Addr) of
1264 _ -> [{Addr, Port, TLS}]
1265 catch _:_ ->
1266 []
1267 end
1268 end, AddrList),
1269 case AddrPorts of
1270 [] -> {error, nxdomain};
1271 _ -> {ok, AddrPorts}
1272 end.
1273
1274 -spec connect([ip_port()], state()) -> {ok, term(), ip_port()} |
1275 {error, {socket, socket_error_reason()}} |
1276 {error, {tls, tls_error_reason()}}.
1277 connect(AddrPorts, State) ->
1278 case connect(AddrPorts, State, {error, nxdomain}) of
1279 {ok, Socket, {Addr, Port, TLS = true}} ->
1280 case starttls(Socket, State) of
1281 {ok, TLSSocket} -> {ok, TLSSocket, {Addr, Port, TLS}};
1282 {error, Why} -> {error, {tls, Why}}
1283 end;
1284 {ok, Socket, {Addr, Port, TLS = false}} ->
1285 {ok, Socket, {Addr, Port, TLS}};
1286 {error, Why} ->
1287 {error, {socket, Why}}
1288 end.
1289
1290 -spec connect([ip_port()], state(), network_error()) ->
1291 {ok, term(), ip_port()} | network_error().
1292 connect([{Addr, Port, TLS}|AddrPorts], State, _) ->
1293 Type = get_addr_type(Addr),
1294 Opts = [binary, {packet, 0},
1295 {send_timeout, ?TCP_SEND_TIMEOUT},
1296 {send_timeout_close, true},
1297 {active, false}, Type],
1298 Opts1 = try callback(connect_options, Addr, Opts, State)
1299 catch _:{?MODULE, undef} -> Opts
1300 end,
1301 Timeout = get_connect_timeout(State),
1302 try xmpp_socket:connect(Addr, Port, Opts1, Timeout) of
1303 {ok, Socket} ->
1304 {ok, Socket, {Addr, Port, TLS}};
1305 Err ->
1306 connect(AddrPorts, State, Err)
1307 catch _:badarg ->
1308 connect(AddrPorts, State, {error, einval})
1309 end;
1310 connect([], _State, Err) ->
1311 Err.
1312
1313 -spec get_addr_type(inet:ip_address()) -> inet:address_family().
1314 get_addr_type({_, _, _, _}) -> inet;
1315 get_addr_type({_, _, _, _, _, _, _, _}) -> inet6.
1316
1317 -spec get_dns_timeout(state()) -> timeout().
1318 get_dns_timeout(State) ->
1319 Timeout = try callback(dns_timeout, State)
1320 catch _:{?MODULE, undef} -> ?DNS_TIMEOUT
1321 end,
1322 min(Timeout, get_timeout(State)).
1323
1324 -spec get_dns_retries(state()) -> non_neg_integer().
1325 get_dns_retries(State) ->
1326 try callback(dns_retries, State)
1327 catch _:{?MODULE, undef} -> 2
1328 end.
1329
1330 -spec get_default_port(state()) -> inet:port_number().
1331 get_default_port(#{xmlns := NS} = State) ->
1332 try callback(default_port, State)
1333 catch _:{?MODULE, undef} when NS == ?NS_SERVER -> 5269;
1334 _:{?MODULE, undef} when NS == ?NS_CLIENT -> 5222
1335 end.
1336
1337 -spec get_address_families(state()) -> [inet:address_family()].
1338 get_address_families(State) ->
1339 try callback(address_families, State)
1340 catch _:{?MODULE, undef} -> [inet, inet6]
1341 end.
1342
1343 -spec get_connect_timeout(state()) -> timeout().
1344 get_connect_timeout(State) ->
1345 Timeout = try callback(connect_timeout, State)
1346 catch _:{?MODULE, undef} -> ?CONNECT_TIMEOUT
1347 end,
1348 min(Timeout, get_timeout(State)).
1349
1350 %%%===================================================================
1351 %%% Callbacks
1352 %%%===================================================================
1353 callback(F, #{mod := Mod} = State) ->
1354 case erlang:function_exported(Mod, F, 1) of
1355 true -> Mod:F(State);
1356 false -> erlang:error({?MODULE, undef})
1357 end.
1358
1359 callback(F, Arg1, #{mod := Mod} = State) ->
1360 case erlang:function_exported(Mod, F, 2) of
1361 true -> Mod:F(Arg1, State);
1362 false -> erlang:error({?MODULE, undef})
1363 end.
1364
1365 callback(code_change, OldVsn, #{mod := Mod} = State, Extra) ->
1366 %% code_change/3 callback is a special snowflake
1367 case erlang:function_exported(Mod, code_change, 3) of
1368 true -> Mod:code_change(OldVsn, State, Extra);
1369 false -> {ok, State}
1370 end;
1371 callback(F, Arg1, Arg2, #{mod := Mod} = State) ->
1372 case erlang:function_exported(Mod, F, 3) of
1373 true -> Mod:F(Arg1, Arg2, State);
1374 false -> erlang:error({?MODULE, undef})
1375 end.
0 %%%-------------------------------------------------------------------
1 %%%
2 %%% Copyright (C) 2002-2018 ProcessOne, SARL. All Rights Reserved.
3 %%%
4 %%% Licensed under the Apache License, Version 2.0 (the "License");
5 %%% you may not use this file except in compliance with the License.
6 %%% You may obtain a copy of the License at
7 %%%
8 %%% http://www.apache.org/licenses/LICENSE-2.0
9 %%%
10 %%% Unless required by applicable law or agreed to in writing, software
11 %%% distributed under the License is distributed on an "AS IS" BASIS,
12 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %%% See the License for the specific language governing permissions and
14 %%% limitations under the License.
15 %%%
16 %%%-------------------------------------------------------------------
17 -module(xmpp_stream_pkix).
18
19 %% API
20 -export([authenticate/1, authenticate/2, get_cert_domains/1, format_error/1]).
21
22 -include("xmpp.hrl").
23 -include_lib("public_key/include/public_key.hrl").
24 -include("XmppAddr.hrl").
25
26 -type cert() :: #'OTPCertificate'{}.
27
28 %%%===================================================================
29 %%% API
30 %%%===================================================================
31 -spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state())
32 -> {ok, binary()} | {error, atom(), binary()}.
33 authenticate(State) ->
34 authenticate(State, <<"">>).
35
36 -spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state(), binary())
37 -> {ok, binary()} | {error, atom(), binary()}.
38 authenticate(#{xmlns := ?NS_SERVER,
39 socket := Socket} = State, Authzid) ->
40 Peer = maps:get(remote_server, State, Authzid),
41 case verify_server_cert(Peer, Socket) of
42 ok ->
43 {ok, Peer};
44 {error, Reason} ->
45 {error, Reason, Peer}
46 end;
47 authenticate(#{xmlns := ?NS_CLIENT, stream_direction := out,
48 socket := Socket, server := Server}, _) ->
49 case verify_server_cert(Server, Socket) of
50 ok ->
51 {ok, Server};
52 {error, Reason} ->
53 {error, Reason, Server}
54 end;
55 authenticate(#{xmlns := ?NS_CLIENT, stream_direction := in,
56 socket := Socket, server := Server}, Authzid) ->
57 JID = try jid:decode(Authzid)
58 catch _:{bad_jid, <<>>} -> jid:make(Server);
59 _:{bad_jid, _} -> {error, invalid_authzid, Authzid}
60 end,
61 case JID of
62 #jid{user = User, lserver = LServer} ->
63 case verify_cert(Socket) of
64 {ok, Cert} ->
65 JIDs = get_xmpp_addrs(Cert),
66 get_username(JID, JIDs, LServer);
67 {error, Reason} ->
68 {error, Reason, User}
69 end;
70 Err ->
71 Err
72 end.
73
74 format_error(idna_failed) ->
75 {'bad-protocol', <<"Remote domain is not an IDN hostname">>};
76 format_error(hostname_mismatch) ->
77 {'not-authorized', <<"Certificate host name mismatch">>};
78 format_error(jid_mismatch) ->
79 {'not-authorized', <<"Certificate JID mismatch">>};
80 format_error(get_cert_failed) ->
81 {'bad-protocol', <<"Failed to get peer certificate">>};
82 format_error(invalid_authzid) ->
83 {'invalid-authzid', <<"Malformed JID">>};
84 format_error(Other) ->
85 {'not-authorized', erlang:atom_to_binary(Other, utf8)}.
86
87 -spec get_cert_domains(cert()) -> [binary()].
88 get_cert_domains(Cert) ->
89 TBSCert = Cert#'OTPCertificate'.tbsCertificate,
90 {rdnSequence, Subject} = TBSCert#'OTPTBSCertificate'.subject,
91 Extensions = TBSCert#'OTPTBSCertificate'.extensions,
92 get_domain_from_subject(lists:flatten(Subject)) ++
93 get_domains_from_san(Extensions).
94
95 %%%===================================================================
96 %%% Internal functions
97 %%%===================================================================
98 -spec verify_server_cert(binary(), xmpp_socket:socket()) -> ok | {error, atom()}.
99 verify_server_cert(Server, Socket) ->
100 case verify_cert(Socket) of
101 {ok, Cert} ->
102 case xmpp_idna:domain_utf8_to_ascii(Server) of
103 false ->
104 {error, idna_failed};
105 AsciiServer ->
106 case lists:any(
107 fun(D) -> match_domain(AsciiServer, D) end,
108 get_cert_domains(Cert)) of
109 true ->
110 ok;
111 false ->
112 {error, hostname_mismatch}
113 end
114 end;
115 {error, _} = Err ->
116 Err
117 end.
118
119 -spec verify_cert(xmpp_socket:socket()) -> {ok, cert()} | {error, atom()}.
120 verify_cert(Socket) ->
121 case xmpp_socket:get_peer_certificate(Socket, otp) of
122 {ok, Cert} ->
123 case xmpp_socket:get_verify_result(Socket) of
124 0 ->
125 {ok, Cert};
126 VerifyRes ->
127 %% TODO: return atomic errors
128 %% This should be improved in fast_tls
129 Reason = fast_tls:get_cert_verify_string(VerifyRes, Cert),
130 {error, erlang:binary_to_atom(Reason, utf8)}
131 end;
132 error ->
133 {error, get_cert_failed}
134 end.
135
136 -spec get_domain_from_subject([#'AttributeTypeAndValue'{}]) -> [binary()].
137 get_domain_from_subject(AttrVals) ->
138 case lists:keyfind(?'id-at-commonName',
139 #'AttributeTypeAndValue'.type,
140 AttrVals) of
141 #'AttributeTypeAndValue'{value = {_, S}} ->
142 try jid:decode(iolist_to_binary(S)) of
143 #jid{luser = <<"">>, lresource = <<"">>, lserver = Domain} ->
144 [Domain];
145 _ ->
146 []
147 catch _:{bad_jid, _} ->
148 []
149 end;
150 _ ->
151 []
152 end.
153
154 -spec get_domains_from_san([#'Extension'{}] | asn1_NOVALUE) -> [binary()].
155 get_domains_from_san(Extensions) when is_list(Extensions) ->
156 case lists:keyfind(?'id-ce-subjectAltName',
157 #'Extension'.extnID,
158 Extensions) of
159 #'Extension'{extnValue = Vals} ->
160 lists:flatmap(
161 fun({dNSName, S}) ->
162 [iolist_to_binary(S)];
163 ({otherName, AnotherName}) ->
164 case decode_xmpp_addr(AnotherName) of
165 {ok, #jid{luser = <<"">>,
166 lresource = <<"">>,
167 lserver = Domain}} ->
168 case xmpp_idna:domain_utf8_to_ascii(Domain) of
169 false ->
170 [];
171 ASCIIDomain ->
172 [ASCIIDomain]
173 end;
174 _ ->
175 []
176 end;
177 (_) ->
178 []
179 end, Vals);
180 _ ->
181 []
182 end;
183 get_domains_from_san(_) ->
184 [].
185
186 -spec decode_xmpp_addr(#'AnotherName'{}) -> {ok, jid()} | error.
187 decode_xmpp_addr(#'AnotherName'{'type-id' = ?'id-on-xmppAddr',
188 value = XmppAddr}) ->
189 try 'XmppAddr':decode('XmppAddr', XmppAddr) of
190 {ok, JIDStr} ->
191 try {ok, jid:decode(iolist_to_binary(JIDStr))}
192 catch _:{bad_jid, _} -> error
193 end;
194 _ ->
195 error
196 catch _:_ ->
197 error
198 end;
199 decode_xmpp_addr(_) ->
200 error.
201
202 -spec get_xmpp_addrs(cert()) -> [jid()].
203 get_xmpp_addrs(Cert) ->
204 TBSCert = Cert#'OTPCertificate'.tbsCertificate,
205 case TBSCert#'OTPTBSCertificate'.extensions of
206 Extensions when is_list(Extensions) ->
207 case lists:keyfind(?'id-ce-subjectAltName',
208 #'Extension'.extnID,
209 Extensions) of
210 #'Extension'{extnValue = Vals} ->
211 lists:flatmap(
212 fun({otherName, AnotherName}) ->
213 case decode_xmpp_addr(AnotherName) of
214 {ok, JID} -> [JID];
215 _ -> []
216 end;
217 (_) ->
218 []
219 end, Vals);
220 _ ->
221 []
222 end;
223 _ ->
224 []
225 end.
226
227 -spec get_username(jid(), [jid()], binary()) ->
228 {ok, binary()} | {error, jid_mismatch, binary()}.
229 get_username(#jid{user = User, lserver = LS}, _, LServer) when LS /= LServer ->
230 %% The user provided JID from different domain
231 {error, jid_mismatch, User};
232 get_username(#jid{user = <<>>}, [#jid{user = U, lserver = LS}], LServer)
233 when U /= <<>> andalso LS == LServer ->
234 %% The user didn't provide JID or username, and there is only
235 %% one 'non-global' JID matching current domain
236 {ok, U};
237 get_username(#jid{user = User, luser = LUser}, JIDs, LServer) when User /= <<>> ->
238 %% The user provided username
239 lists:foldl(
240 fun(_, {ok, _} = OK) ->
241 OK;
242 (#jid{user = <<>>, lserver = LS}, _) when LS == LServer ->
243 %% Found "global" JID in the certficate
244 %% (i.e. in the form of 'domain.com')
245 %% within current domain, so we force matching
246 {ok, User};
247 (#jid{luser = LU, lserver = LS}, _) when LU == LUser, LS == LServer ->
248 %% Found exact JID matching
249 {ok, User};
250 (_, Err) ->
251 Err
252 end, {error, jid_mismatch, User}, JIDs);
253 get_username(#jid{user = User}, _, _) ->
254 %% Nothing from above is true
255 {error, jid_mismatch, User}.
256
257 -spec match_domain(binary(), binary()) -> boolean().
258 match_domain(Domain, Domain) -> true;
259 match_domain(Domain, <<"*.", Rest/binary>>) ->
260 %% RFC6120 states:
261 %% DNS domain names in server certificates MAY contain the wildcard
262 %% character '*' as the complete left-most label within the identifier
263 case binary:split(Domain, <<$.>>) of
264 [_, Rest] -> true;
265 _ -> false
266 end;
267 match_domain(_, _) ->
268 false.
2929 set_xdata_field/2, has_xdata_var/2,
3030 make_adhoc_response/1, make_adhoc_response/2,
3131 decode_timestamp/1, encode_timestamp/1]).
32 -export([hex/1]).
3233
3334 -include("xmpp.hrl").
3435
144145 [Year, Month, Day, Hour, Minute, Second,
145146 Fraction])).
146147
148 -spec hex(binary()) -> binary().
149 hex(Bin) ->
150 << <<(to_xchar(N div 16)), (to_xchar(N rem 16))>> || <<N>> <= Bin >>.
151
147152 %%%===================================================================
148153 %%% Internal functions
149154 %%%===================================================================
182187 I when I >= Min, I =< Max ->
183188 I
184189 end.
190
191 to_xchar(D) when (D >= 0) and (D < 10) -> D + $0;
192 to_xchar(D) -> D + $a - 10.