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
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 | ||
0 | 20 | # Version 1.2.2 |
1 | 21 | |
2 | 22 | * Updating fast_xml to version 1.1.32. |
26 | 26 | dialyzer/erlang.plt: |
27 | 27 | @mkdir -p dialyzer |
28 | 28 | @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; \ | |
30 | 30 | status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi |
31 | 31 | |
32 | 32 | dialyzer/deps.plt: |
250 | 250 | - [XEP-0033](https://xmpp.org/extensions/xep-0033.html): Extended Stanza Addressing |
251 | 251 | - [XEP-0039](https://xmpp.org/extensions/xep-0039.html): Statistics Gathering |
252 | 252 | - [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 | |
253 | 254 | - [XEP-0048](https://xmpp.org/extensions/xep-0048.html): Bookmarks |
254 | 255 | - [XEP-0049](https://xmpp.org/extensions/xep-0049.html): Private XML Storage |
255 | 256 | - [XEP-0050](https://xmpp.org/extensions/xep-0050.html): Ad-Hoc Commands |
270 | 271 | - [XEP-0138](https://xmpp.org/extensions/xep-0138.html): Stream Compression |
271 | 272 | - [XEP-0153](https://xmpp.org/extensions/xep-0153.html): vCard-Based Avatars |
272 | 273 | - [XEP-0158](https://xmpp.org/extensions/xep-0158.html): CAPTCHA Forms |
274 | - [XEP-0166](https://xmpp.org/extensions/xep-0166.html): Jingle | |
273 | 275 | - [XEP-0172](https://xmpp.org/extensions/xep-0172.html): User Nickname |
274 | 276 | - [XEP-0184](https://xmpp.org/extensions/xep-0184.html): Message Delivery Receipts |
275 | 277 | - [XEP-0191](https://xmpp.org/extensions/xep-0191.html): Blocking Command |
280 | 282 | - [XEP-0220](https://xmpp.org/extensions/xep-0220.html): Server Dialback |
281 | 283 | - [XEP-0221](https://xmpp.org/extensions/xep-0221.html): Data Forms Media Element |
282 | 284 | - [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 | |
283 | 286 | - [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 | |
284 | 289 | - [XEP-0264](https://xmpp.org/extensions/xep-0264.html): Jingle Content Thumbnails |
285 | 290 | - [XEP-0279](https://xmpp.org/extensions/xep-0279.html): Server IP Check |
286 | 291 | - [XEP-0280](https://xmpp.org/extensions/xep-0280.html): Message Carbons |
287 | 292 | - [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 | |
288 | 294 | - [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 | |
289 | 296 | - [XEP-0334](https://xmpp.org/extensions/xep-0334.html): Message Processing Hints |
290 | 297 | - [XEP-0352](https://xmpp.org/extensions/xep-0352.html): Client State Indication |
291 | 298 | - [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 |
355 | 355 | > xmpp:get_id(#message{id = <<"abcd">>}). |
356 | 356 | <<"abcd">> |
357 | 357 | ``` |
358 | **Example 2**: obtaining `id` of `xmlele()` | |
358 | **Example 2**: obtaining `id` of `xmlel()` | |
359 | 359 | ```erlang |
360 | 360 | > xmpp:get_id(#xmlel{attrs = [{<<"id">>, <<"abcd">>}]}). |
361 | 361 | <<"abcd">> |
132 | 132 | -define(NS_FEATURE_COMPRESS, |
133 | 133 | <<"http://jabber.org/features/compress">>). |
134 | 134 | -define(NS_FEATURE_MSGOFFLINE, <<"msgoffline">>). |
135 | -define(NS_FEATURE_PRESENCE, <<"presence">>). | |
136 | -define(NS_FEATURE_IQ, <<"iq">>). | |
135 | 137 | -define(NS_FLEX_OFFLINE, <<"http://jabber.org/protocol/offline">>). |
136 | 138 | -define(NS_COMPRESS, |
137 | 139 | <<"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). |
96 | 96 | 'no-permanent-store' | 'no-permanent-storage'}). |
97 | 97 | -type hint() :: #hint{}. |
98 | 98 | |
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 | ||
99 | 107 | -type xmpp_host() :: binary() | inet:ip_address() | |
100 | 108 | {binary() | inet:ip_address(), inet:port_number()}. |
101 | 109 | |
134 | 142 | port = 1080 :: non_neg_integer()}). |
135 | 143 | -type streamhost() :: #streamhost{}. |
136 | 144 | |
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 | ||
137 | 170 | -record(sm_resume, {h :: non_neg_integer(), |
138 | 171 | previd = <<>> :: binary(), |
139 | 172 | xmlns = <<>> :: binary()}). |
140 | 173 | -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{}. | |
166 | 174 | |
167 | 175 | -record(ping, {}). |
168 | 176 | -type ping() :: #ping{}. |
188 | 196 | type :: 'both' | 'get' | 'managed_entity' | 'none' | 'outgoing' | 'roster' | 'set'}). |
189 | 197 | -type privilege_perm() :: #privilege_perm{}. |
190 | 198 | |
199 | -record(ibb_open, {sid = <<>> :: binary(), | |
200 | 'block-size' :: non_neg_integer(), | |
201 | stanza = iq :: 'iq' | 'message'}). | |
202 | -type ibb_open() :: #ibb_open{}. | |
203 | ||
191 | 204 | -record(muc_decline, {reason = <<>> :: binary(), |
192 | 205 | from :: undefined | jid:jid(), |
193 | 206 | to :: undefined | jid:jid()}). |
204 | 217 | xmlns = <<>> :: binary()}). |
205 | 218 | -type upload_request_0() :: #upload_request_0{}. |
206 | 219 | |
220 | -record(ibb_data, {sid = <<>> :: binary(), | |
221 | seq :: non_neg_integer(), | |
222 | data = <<>> :: binary()}). | |
223 | -type ibb_data() :: #ibb_data{}. | |
224 | ||
207 | 225 | -record(sm_a, {h :: non_neg_integer(), |
208 | 226 | xmlns = <<>> :: binary()}). |
209 | 227 | -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{}. | |
210 | 239 | |
211 | 240 | -record(starttls_proceed, {}). |
212 | 241 | -type starttls_proceed() :: #starttls_proceed{}. |
232 | 261 | events = [] :: [binary()]}). |
233 | 262 | -type muc_subscribe() :: #muc_subscribe{}. |
234 | 263 | |
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 | ||
235 | 269 | -record(stanza_id, {by :: jid:jid(), |
236 | 270 | id = <<>> :: binary()}). |
237 | 271 | -type stanza_id() :: #stanza_id{}. |
267 | 301 | -record(private, {sub_els = [] :: [xmpp_element() | fxml:xmlel()]}). |
268 | 302 | -type private() :: #private{}. |
269 | 303 | |
304 | -record(ibb_close, {sid = <<>> :: binary()}). | |
305 | -type ibb_close() :: #ibb_close{}. | |
306 | ||
270 | 307 | -record(sm_enable, {max :: 'undefined' | non_neg_integer(), |
271 | 308 | resume = false :: boolean(), |
272 | 309 | xmlns = <<>> :: binary()}). |
277 | 314 | |
278 | 315 | -record(gone, {uri = <<>> :: binary()}). |
279 | 316 | -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{}. | |
280 | 326 | |
281 | 327 | -record(delegation_query, {to :: jid:jid(), |
282 | 328 | delegate = [] :: [binary()]}). |
326 | 372 | sub_els = [] :: [xmpp_element() | fxml:xmlel()]}). |
327 | 373 | -type avatar_pointer() :: #avatar_pointer{}. |
328 | 374 | |
375 | -record(sm_r, {xmlns = <<>> :: binary()}). | |
376 | -type sm_r() :: #sm_r{}. | |
377 | ||
329 | 378 | -record(muc_actor, {jid :: undefined | jid:jid(), |
330 | 379 | nick = <<>> :: binary()}). |
331 | 380 | -type muc_actor() :: #muc_actor{}. |
340 | 389 | error :: 'undefined' | #stat_error{}}). |
341 | 390 | -type stat() :: #stat{}. |
342 | 391 | |
343 | -record(sm_r, {xmlns = <<>> :: binary()}). | |
344 | -type sm_r() :: #sm_r{}. | |
345 | ||
346 | 392 | -record(addresses, {list = [] :: [#address{}]}). |
347 | 393 | -type addresses() :: #addresses{}. |
348 | 394 | |
352 | 398 | -record(last, {seconds :: 'undefined' | non_neg_integer(), |
353 | 399 | status = <<>> :: binary()}). |
354 | 400 | -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{}. | |
355 | 407 | |
356 | 408 | -record(redirect, {uri = <<>> :: binary()}). |
357 | 409 | -type redirect() :: #redirect{}. |
363 | 415 | xmlns = <<>> :: binary()}). |
364 | 416 | -type sm_enabled() :: #sm_enabled{}. |
365 | 417 | |
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 | ||
372 | 418 | -record(upload_file_too_large, {'max-file-size' :: 'undefined' | integer(), |
373 | 419 | xmlns = <<>> :: binary()}). |
374 | 420 | -type upload_file_too_large() :: #upload_file_too_large{}. |
383 | 429 | jid :: jid:jid()}). |
384 | 430 | -type ps_subscribe() :: #ps_subscribe{}. |
385 | 431 | |
432 | -record(idle, {since :: erlang:timestamp()}). | |
433 | -type idle() :: #idle{}. | |
434 | ||
386 | 435 | -record(muc_unique, {name = <<>> :: binary()}). |
387 | 436 | -type muc_unique() :: #muc_unique{}. |
388 | 437 | |
421 | 470 | -record(compression, {methods = [] :: [binary()]}). |
422 | 471 | -type compression() :: #compression{}. |
423 | 472 | |
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{}]}). | |
425 | 478 | -type muc_subscriptions() :: #muc_subscriptions{}. |
426 | 479 | |
427 | 480 | -record(ps_subscription, {xmlns = <<>> :: binary(), |
479 | 532 | node = <<>> :: binary()}). |
480 | 533 | -type stats() :: #stats{}. |
481 | 534 | |
482 | -record(sic, {ip :: undefined | inet:ip_address(), | |
483 | port :: 'undefined' | non_neg_integer(), | |
484 | xmlns = <<>> :: binary()}). | |
485 | -type sic() :: #sic{}. | |
486 | ||
487 | 535 | -record(ps_items, {xmlns = <<>> :: binary(), |
488 | 536 | node = <<>> :: binary(), |
489 | 537 | items = [] :: [#ps_item{}], |
501 | 549 | -record(stream_features, {sub_els = [] :: [xmpp_element() | fxml:xmlel()]}). |
502 | 550 | -type stream_features() :: #stream_features{}. |
503 | 551 | |
552 | -record(sic, {ip :: undefined | inet:ip_address(), | |
553 | port :: 'undefined' | non_neg_integer(), | |
554 | xmlns = <<>> :: binary()}). | |
555 | -type sic() :: #sic{}. | |
556 | ||
504 | 557 | -record(sasl_abort, {}). |
505 | 558 | -type sasl_abort() :: #sasl_abort{}. |
506 | 559 | |
535 | 588 | xmlns = <<>> :: binary()}). |
536 | 589 | -type upload_slot() :: #upload_slot{}. |
537 | 590 | |
538 | -record(mix_participant, {jid :: jid:jid(), | |
539 | nick = <<>> :: binary()}). | |
540 | -type mix_participant() :: #mix_participant{}. | |
541 | ||
542 | 591 | -record(vcard_geo, {lat :: 'undefined' | binary(), |
543 | 592 | lon :: 'undefined' | binary()}). |
544 | 593 | -type vcard_geo() :: #vcard_geo{}. |
545 | 594 | |
546 | -record(compressed, {}). | |
547 | -type compressed() :: #compressed{}. | |
548 | ||
549 | 595 | -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', |
550 | 596 | text = [] :: [#text{}]}). |
551 | 597 | -type sasl_failure() :: #sasl_failure{}. |
552 | 598 | |
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{}. | |
555 | 601 | |
556 | 602 | -record(xevent, {offline = false :: boolean(), |
557 | 603 | delivered = false :: boolean(), |
573 | 619 | -record(origin_id, {id = <<>> :: binary()}). |
574 | 620 | -type origin_id() :: #origin_id{}. |
575 | 621 | |
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 | ||
576 | 632 | -record(version, {name :: 'undefined' | binary(), |
577 | 633 | ver :: 'undefined' | binary(), |
578 | 634 | os :: 'undefined' | binary()}). |
579 | 635 | -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{}. | |
580 | 654 | |
581 | 655 | -record(legacy_auth_feature, {}). |
582 | 656 | -type legacy_auth_feature() :: #legacy_auth_feature{}. |
596 | 670 | |
597 | 671 | -record(vcard_xupdate, {hash :: 'undefined' | binary()}). |
598 | 672 | -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{}. | |
606 | 673 | |
607 | 674 | -record(bytestreams, {hosts = [] :: [#streamhost{}], |
608 | 675 | used :: undefined | jid:jid(), |
636 | 703 | node = <<>> :: binary()}). |
637 | 704 | -type push_disable() :: #push_disable{}. |
638 | 705 | |
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 | ||
639 | 730 | -record(adhoc_actions, {execute :: 'complete' | 'next' | 'prev' | 'undefined', |
640 | 731 | prev = false :: boolean(), |
641 | 732 | next = false :: boolean(), |
645 | 736 | -record(vcard_org, {name :: 'undefined' | binary(), |
646 | 737 | units = [] :: [binary()]}). |
647 | 738 | -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{}. | |
669 | 739 | |
670 | 740 | -record(vcard_tel, {home = false :: boolean(), |
671 | 741 | work = false :: boolean(), |
683 | 753 | number :: 'undefined' | binary()}). |
684 | 754 | -type vcard_tel() :: #vcard_tel{}. |
685 | 755 | |
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 | ||
686 | 777 | -record(muc_destroy, {xmlns = <<>> :: binary(), |
687 | 778 | jid :: undefined | jid:jid(), |
688 | 779 | reason = <<>> :: binary(), |
704 | 795 | suffix :: 'undefined' | binary()}). |
705 | 796 | -type vcard_name() :: #vcard_name{}. |
706 | 797 | |
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 | ||
707 | 811 | -record(identity, {category = <<>> :: binary(), |
708 | 812 | type = <<>> :: binary(), |
709 | 813 | lang = <<>> :: binary(), |
710 | 814 | name = <<>> :: binary()}). |
711 | 815 | -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{}. | |
725 | 816 | |
726 | 817 | -record(bookmark_conference, {name = <<>> :: binary(), |
727 | 818 | jid :: jid:jid(), |
843 | 934 | -type pubsub_owner() :: #pubsub_owner{}. |
844 | 935 | |
845 | 936 | -record(ps_options, {node = <<>> :: binary(), |
846 | jid :: jid:jid(), | |
937 | jid :: undefined | jid:jid(), | |
847 | 938 | subid = <<>> :: binary(), |
848 | 939 | xdata :: 'undefined' | #xdata{}}). |
849 | 940 | -type ps_options() :: #ps_options{}. |
928 | 1019 | -record(push_notification, {xdata :: 'undefined' | #xdata{}}). |
929 | 1020 | -type push_notification() :: #push_notification{}. |
930 | 1021 | |
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 | ||
931 | 1036 | -record(mix_join, {jid :: undefined | jid:jid(), |
932 | 1037 | subscribe = [] :: [binary()]}). |
933 | 1038 | -type mix_join() :: #mix_join{}. |
951 | 1056 | active :: 'none' | 'undefined' | binary()}). |
952 | 1057 | -type privacy_query() :: #privacy_query{}. |
953 | 1058 | |
954 | -record(stream_error, {reason :: atom() | #'see-other-host'{}, | |
955 | text = [] :: [#text{}]}). | |
956 | -type stream_error() :: #stream_error{}. | |
957 | ||
958 | 1059 | -record(sm_failed, {reason :: atom() | #gone{} | #redirect{}, |
959 | 1060 | text = [] :: [#text{}], |
960 | 1061 | h :: 'undefined' | non_neg_integer(), |
968 | 1069 | text = [] :: [#text{}], |
969 | 1070 | sub_els = [] :: [xmpp_element() | fxml:xmlel()]}). |
970 | 1071 | -type stanza_error() :: #stanza_error{}. |
1072 | ||
1073 | -record(stream_error, {reason :: atom() | #'see-other-host'{}, | |
1074 | text = [] :: [#text{}]}). | |
1075 | -type stream_error() :: #stream_error{}. | |
971 | 1076 | |
972 | 1077 | -record(vcard_logo, {type :: 'undefined' | binary(), |
973 | 1078 | binval :: 'undefined' | binary(), |
1009 | 1114 | utc :: undefined | erlang:timestamp()}). |
1010 | 1115 | -type time() :: #time{}. |
1011 | 1116 | |
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() | | |
1013 | 1147 | 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() | | |
1014 | 1193 | 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() | | |
1018 | 1203 | 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() | | |
1021 | 1237 | 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() | | |
1044 | 1247 | 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() | | |
1061 | 1248 | avatar_meta() | |
1062 | time() | | |
1063 | db_feature() | | |
1064 | mix_leave() | | |
1065 | vcard_temp() | | |
1066 | sm_resumed() | | |
1067 | 1249 | 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() | | |
1115 | 1251 | vcard_email() | |
1116 | 1252 | 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() | | |
1120 | 1268 | 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() | | |
1127 | 1286 | 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() | | |
1144 | 1288 | 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() | | |
1147 | 1301 | 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() | | |
1163 | 1309 | iq() | |
1164 | ps_unsubscribe() | | |
1165 | sasl_abort() | | |
1310 | register() | | |
1311 | time() | | |
1312 | push_enable() | | |
1313 | starttls_proceed() | | |
1314 | pubsub_owner() | | |
1166 | 1315 | 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(). |
18 | 18 | %%% |
19 | 19 | %%%---------------------------------------------------------------------- |
20 | 20 | |
21 | {erl_opts, [debug_info, {src_dirs, ["src"]}, | |
21 | {erl_opts, [debug_info, {src_dirs, ["asn1", "src"]}, | |
22 | 22 | nowarn_export_all, |
23 | 23 | {i, "include"}, |
24 | 24 | {i, "../fast_xml/include"}, |
30 | 30 | {"priv/lib/xmpp_uri.so", ["c_src/xmpp_uri.c"]}, |
31 | 31 | {"priv/lib/xmpp_lang.so", ["c_src/xmpp_lang.c"]}]}. |
32 | 32 | |
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"}}}]}. | |
36 | 38 | |
37 | 39 | {clean_files, ["c_src/jid.gcda", "c_src/jid.gcno"]}. |
38 | 40 |
2189 | 2189 | attrs = [#attr{name = <<"node">>}, |
2190 | 2190 | #attr{name = <<"subid">>}, |
2191 | 2191 | #attr{name = <<"jid">>, |
2192 | required = true, | |
2193 | 2192 | dec = {jid, decode, []}, |
2194 | 2193 | enc = {jid, encode, []}}], |
2195 | 2194 | refs = [#ref{name = xdata, min = 0, max = 1, |
2913 | 2912 | #elem{name = <<"subscription">>, |
2914 | 2913 | xmlns = <<"urn:xmpp:mucsub:0">>, |
2915 | 2914 | module = p1_mucsub, |
2916 | result = '$jid', | |
2917 | attrs = [#attr{name = <<"jid">>, | |
2915 | result = {muc_subscription, '$jid', '$events'}, | |
2916 | attrs = [#attr{name = <<"jid">>, | |
2918 | 2917 | required = true, |
2919 | 2918 | dec = {jid, decode, []}, |
2920 | enc = {jid, encode, []}}]}). | |
2919 | enc = {jid, encode, []}}], | |
2920 | refs = [#ref{name = muc_subscribe_event, label = '$events'}]}). | |
2921 | 2921 | |
2922 | 2922 | -xml(muc_subscriptions, |
2923 | 2923 | #elem{name = <<"subscriptions">>, |
4197 | 4197 | #ref{name = avatar_pointer, label = '$pointer', |
4198 | 4198 | min = 0, max = 1}]}). |
4199 | 4199 | |
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 | ||
4200 | 4685 | -spec dec_tzo(_) -> {integer(), integer()}. |
4201 | 4686 | dec_tzo(Val) -> |
4202 | 4687 | [H1, M1] = binary:split(Val, <<":">>), |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
35 | 35 | {<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>}, |
36 | 36 | {<<"subscription">>, <<"urn:xmpp:mucsub:0">>}]. |
37 | 37 | |
38 | do_encode({muc_subscription, _, _} = Subscription, | |
39 | TopXMLNS) -> | |
40 | encode_muc_subscription(Subscription, TopXMLNS); | |
38 | 41 | do_encode({muc_subscriptions, _} = Subscriptions, |
39 | 42 | TopXMLNS) -> |
40 | 43 | encode_muc_subscriptions(Subscriptions, TopXMLNS); |
47 | 50 | |
48 | 51 | do_get_name({muc_subscribe, _, _, _, _}) -> |
49 | 52 | <<"subscribe">>; |
53 | do_get_name({muc_subscription, _, _}) -> | |
54 | <<"subscription">>; | |
50 | 55 | do_get_name({muc_subscriptions, _}) -> |
51 | 56 | <<"subscriptions">>; |
52 | 57 | do_get_name({muc_unsubscribe, _, _}) -> |
53 | 58 | <<"unsubscribe">>. |
54 | 59 | |
55 | 60 | do_get_ns({muc_subscribe, _, _, _, _}) -> |
61 | <<"urn:xmpp:mucsub:0">>; | |
62 | do_get_ns({muc_subscription, _, _}) -> | |
56 | 63 | <<"urn:xmpp:mucsub:0">>; |
57 | 64 | do_get_ns({muc_subscriptions, _}) -> |
58 | 65 | <<"urn:xmpp:mucsub:0">>; |
59 | 66 | do_get_ns({muc_unsubscribe, _, _}) -> |
60 | 67 | <<"urn:xmpp:mucsub:0">>. |
61 | 68 | |
69 | pp(muc_subscription, 2) -> [jid, events]; | |
62 | 70 | pp(muc_subscriptions, 1) -> [list]; |
63 | 71 | pp(muc_subscribe, 4) -> [nick, password, jid, events]; |
64 | 72 | pp(muc_unsubscribe, 2) -> [nick, jid]; |
65 | 73 | pp(_, _) -> no. |
66 | 74 | |
67 | 75 | 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}]. | |
70 | 78 | |
71 | 79 | decode_muc_unsubscribe(__TopXMLNS, __Opts, |
72 | 80 | {xmlel, <<"unsubscribe">>, _attrs, _els}) -> |
340 | 348 | |
341 | 349 | decode_muc_subscription(__TopXMLNS, __Opts, |
342 | 350 | {xmlel, <<"subscription">>, _attrs, _els}) -> |
351 | Events = decode_muc_subscription_els(__TopXMLNS, __Opts, | |
352 | _els, []), | |
343 | 353 | Jid = decode_muc_subscription_attrs(__TopXMLNS, _attrs, |
344 | 354 | 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). | |
346 | 379 | |
347 | 380 | decode_muc_subscription_attrs(__TopXMLNS, |
348 | 381 | [{<<"jid">>, _val} | _attrs], _Jid) -> |
353 | 386 | decode_muc_subscription_attrs(__TopXMLNS, [], Jid) -> |
354 | 387 | decode_muc_subscription_attr_jid(__TopXMLNS, Jid). |
355 | 388 | |
356 | encode_muc_subscription(Jid, __TopXMLNS) -> | |
389 | encode_muc_subscription({muc_subscription, Jid, Events}, | |
390 | __TopXMLNS) -> | |
357 | 391 | __NewTopXMLNS = |
358 | 392 | xmpp_codec:choose_top_xmlns(<<"urn:xmpp:mucsub:0">>, [], |
359 | 393 | __TopXMLNS), |
360 | _els = [], | |
394 | _els = | |
395 | lists:reverse('encode_muc_subscription_$events'(Events, | |
396 | __NewTopXMLNS, [])), | |
361 | 397 | _attrs = encode_muc_subscription_attr_jid(Jid, |
362 | 398 | xmpp_codec:enc_xmlns_attrs(__NewTopXMLNS, |
363 | 399 | __TopXMLNS)), |
364 | 400 | {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]). | |
365 | 411 | |
366 | 412 | decode_muc_subscription_attr_jid(__TopXMLNS, |
367 | 413 | undefined) -> |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | end. |
15 | 15 | |
16 | 16 | -export_type([property/0, result/0, form/0]). |
17 | 17 | |
18 | -dialyzer({nowarn_function, {dec_int, 3}}). | |
19 | ||
18 | 20 | dec_int(Val, Min, Max) -> |
19 | case list_to_integer(binary_to_list(Val)) of | |
21 | case erlang:binary_to_integer(Val) of | |
20 | 22 | Int when Int =< Max, Min == infinity -> Int; |
21 | 23 | Int when Int =< Max, Int >= Min -> Int |
22 | 24 | 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). |
120 | 120 | dec_int(Val, infinity, infinity). |
121 | 121 | |
122 | 122 | dec_int(Val, Min, Max) -> |
123 | case list_to_integer(binary_to_list(Val)) of | |
123 | case erlang:binary_to_integer(Val) of | |
124 | 124 | Int when Int =< Max, Min == infinity -> |
125 | 125 | Int; |
126 | 126 | Int when Int =< Max, Int >= Min -> |
292 | 292 | emit("-export([decode/1, decode/2, format_error/1, io_format_error/1]).~n"), |
293 | 293 | emit("-include(\"xmpp_codec.hrl\").~n"), |
294 | 294 | 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. | |
296 | 305 | |
297 | 306 | mk_type_definitions(Fs, State) -> |
298 | 307 | 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]. |
2686 | 2686 | [{<<"subid">>, _val} | _acc]. |
2687 | 2687 | |
2688 | 2688 | decode_pubsub_options_attr_jid(__TopXMLNS, undefined) -> |
2689 | erlang:error({xmpp_codec, | |
2690 | {missing_attr, <<"jid">>, <<"options">>, __TopXMLNS}}); | |
2689 | undefined; | |
2691 | 2690 | decode_pubsub_options_attr_jid(__TopXMLNS, _val) -> |
2692 | 2691 | case catch jid:decode(_val) of |
2693 | 2692 | {'EXIT', _} -> |
2697 | 2696 | _res -> _res |
2698 | 2697 | end. |
2699 | 2698 | |
2699 | encode_pubsub_options_attr_jid(undefined, _acc) -> _acc; | |
2700 | 2700 | encode_pubsub_options_attr_jid(_val, _acc) -> |
2701 | 2701 | [{<<"jid">>, jid:encode(_val)} | _acc]. |
2702 | 2702 |
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]. |
23 | 23 | |
24 | 24 | {application, xmpp, |
25 | 25 | [{description, "Erlang/Elixir XMPP parsing and serialization library"}, |
26 | {vsn, "1.2.2"}, | |
26 | {vsn, "1.2.5"}, | |
27 | 27 | {modules, []}, |
28 | 28 | {registered, []}, |
29 | 29 | {applications, [kernel, stdlib]}, |
31 | 31 | {env, []}, |
32 | 32 | |
33 | 33 | %% 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"]}, | |
35 | 35 | {licenses, ["Apache 2.0"]}, |
36 | 36 | {maintainers, ["ProcessOne"]}, |
37 | 37 | {links, [{"Github", "https://github.com/processone/xmpp"}]}]}. |
23 | 23 | |
24 | 24 | -module(xmpp). |
25 | 25 | -behaviour(application). |
26 | -dialyzer({nowarn_function, get_els/1}). | |
26 | 27 | |
27 | 28 | %% API |
28 | 29 | %% Application callbacks |
42 | 43 | append_subtags/2, prep_lang/1, register_codec/1, unregister_codec/1, |
43 | 44 | set_tr_callback/1, format_stanza_error/1, format_stanza_error/2, |
44 | 45 | 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]). | |
46 | 47 | |
47 | 48 | %% XMPP errors |
48 | 49 | -export([err_bad_request/0, err_bad_request/2, |
101 | 102 | -type reason_text() :: binary() | {io:format(), list()}. |
102 | 103 | -type lang() :: binary(). |
103 | 104 | -type decode_option() :: ignore_els. |
105 | -type config_option() :: {debug, boolean()} | {fqdn, [binary()]}. | |
106 | ||
107 | -export_type([decode_option/0, config_option/0]). | |
104 | 108 | |
105 | 109 | %%%=================================================================== |
106 | 110 | %%% Application callbacks |
109 | 113 | try |
110 | 114 | {ok, _} = application:ensure_all_started(fast_xml), |
111 | 115 | {ok, _} = application:ensure_all_started(stringprep), |
116 | {ok, _} = application:ensure_all_started(fast_tls), | |
117 | {ok, _} = application:ensure_all_started(ezlib), | |
112 | 118 | ok = jid:start(), |
113 | 119 | ok = xmpp_uri:start(), |
114 | 120 | 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, []), | |
115 | 124 | xmpp_sup:start_link() |
116 | 125 | catch _:{badmatch, Err} -> |
117 | 126 | Err |
570 | 579 | {module, xmpp_tr} = code:load_binary(xmpp_tr, "nofile", Code), |
571 | 580 | ok. |
572 | 581 | |
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 | ||
573 | 605 | %%%=================================================================== |
574 | 606 | %%% Functions to construct general XMPP errors |
575 | 607 | %%%=================================================================== |
224 | 224 | get_mod(<<"x">>, <<"jabber:x:conference">>) -> xep0249; |
225 | 225 | get_mod(<<"jid">>, <<"urn:xmpp:mam:1">>) -> xep0313; |
226 | 226 | get_mod(<<"never">>, <<"urn:xmpp:mam:0">>) -> xep0313; |
227 | get_mod(<<"tie-break">>, | |
228 | <<"urn:xmpp:jingle:errors:1">>) -> | |
229 | xep0166; | |
227 | 230 | get_mod(<<"item">>, |
228 | 231 | <<"http://jabber.org/protocol/pubsub">>) -> |
229 | 232 | xep0060; |
270 | 273 | get_mod(<<"item">>, |
271 | 274 | <<"http://jabber.org/protocol/muc#admin">>) -> |
272 | 275 | xep0045; |
276 | get_mod(<<"gone">>, <<"urn:xmpp:jingle:1">>) -> xep0166; | |
277 | get_mod(<<"range">>, | |
278 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
279 | xep0234; | |
273 | 280 | get_mod(<<"error">>, <<"jabber:component:accept">>) -> |
274 | 281 | rfc6120; |
275 | 282 | get_mod(<<"password">>, <<"jabber:iq:auth">>) -> |
277 | 284 | get_mod(<<"TZ">>, <<"vcard-temp">>) -> xep0054; |
278 | 285 | get_mod(<<"request">>, <<"urn:xmpp:http:upload">>) -> |
279 | 286 | xep0363; |
287 | get_mod(<<"timeout">>, <<"urn:xmpp:jingle:1">>) -> | |
288 | xep0166; | |
289 | get_mod(<<"content">>, <<"urn:xmpp:jingle:1">>) -> | |
290 | xep0166; | |
280 | 291 | get_mod(<<"nick">>, <<"storage:bookmarks">>) -> xep0048; |
281 | 292 | get_mod(<<"iq">>, <<"jabber:component:accept">>) -> |
282 | 293 | rfc6120; |
288 | 299 | xep0045; |
289 | 300 | get_mod(<<"thumbnail">>, <<"urn:xmpp:thumbs:1">>) -> |
290 | 301 | xep0264; |
302 | get_mod(<<"security-required">>, | |
303 | <<"urn:xmpp:jingle:errors:1">>) -> | |
304 | xep0166; | |
291 | 305 | get_mod(<<"url">>, <<"jabber:iq:register">>) -> xep0077; |
292 | 306 | get_mod(<<"GIVEN">>, <<"vcard-temp">>) -> xep0054; |
293 | 307 | get_mod(<<"TYPE">>, <<"vcard-temp">>) -> xep0054; |
301 | 315 | xep0045; |
302 | 316 | get_mod(<<"media">>, <<"urn:xmpp:media-element">>) -> |
303 | 317 | xep0221; |
318 | get_mod(<<"out-of-order">>, | |
319 | <<"urn:xmpp:jingle:errors:1">>) -> | |
320 | xep0166; | |
304 | 321 | get_mod(<<"KEYWORD">>, <<"vcard-temp">>) -> xep0054; |
305 | 322 | get_mod(<<"result">>, <<"urn:xmpp:mam:tmp">>) -> |
306 | 323 | xep0313; |
307 | 324 | get_mod(<<"active">>, <<"urn:xmpp:csi:0">>) -> xep0352; |
308 | 325 | get_mod(<<"slot">>, <<"urn:xmpp:http:upload">>) -> |
309 | 326 | xep0363; |
327 | get_mod(<<"data">>, | |
328 | <<"http://jabber.org/protocol/ibb">>) -> | |
329 | xep0047; | |
310 | 330 | get_mod(<<"query">>, <<"jabber:iq:roster">>) -> rfc6121; |
311 | 331 | get_mod(<<"digest">>, <<"jabber:iq:auth">>) -> xep0078; |
312 | 332 | get_mod(<<"NUMBER">>, <<"vcard-temp">>) -> xep0054; |
345 | 365 | get_mod(<<"received">>, <<"urn:xmpp:carbons:2">>) -> |
346 | 366 | xep0280; |
347 | 367 | get_mod(<<"x">>, <<"jabber:x:event">>) -> xep0022; |
368 | get_mod(<<"general-error">>, <<"urn:xmpp:jingle:1">>) -> | |
369 | xep0166; | |
348 | 370 | get_mod(<<"nick">>, <<"jabber:iq:register">>) -> |
349 | 371 | xep0077; |
350 | 372 | get_mod(<<"internal-server-error">>, |
388 | 410 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
389 | 411 | rfc6120; |
390 | 412 | get_mod(<<"r">>, <<"urn:xmpp:sm:3">>) -> xep0198; |
413 | get_mod(<<"unsupported-info">>, | |
414 | <<"urn:xmpp:jingle:errors:1">>) -> | |
415 | xep0166; | |
391 | 416 | get_mod(<<"query">>, <<"jabber:iq:version">>) -> |
392 | 417 | xep0092; |
393 | 418 | get_mod(<<"archived">>, <<"urn:xmpp:mam:tmp">>) -> |
419 | 444 | get_mod(<<"instructions">>, <<"jabber:x:data">>) -> |
420 | 445 | xep0004; |
421 | 446 | get_mod(<<"join">>, <<"urn:xmpp:mix:0">>) -> xep0369; |
447 | get_mod(<<"idle">>, <<"urn:xmpp:idle:1">>) -> xep0319; | |
422 | 448 | get_mod(<<"retract">>, |
423 | 449 | <<"http://jabber.org/protocol/pubsub#event">>) -> |
424 | 450 | xep0060; |
441 | 467 | xep0077; |
442 | 468 | get_mod(<<"query">>, <<"urn:xmpp:delegation:1">>) -> |
443 | 469 | xep0355; |
470 | get_mod(<<"hash">>, <<"urn:xmpp:hashes:2">>) -> xep0300; | |
444 | 471 | get_mod(<<"invalid-options">>, |
445 | 472 | <<"http://jabber.org/protocol/pubsub#errors">>) -> |
446 | 473 | xep0060; |
477 | 504 | get_mod(<<"no-permanent-storage">>, |
478 | 505 | <<"urn:xmpp:hints">>) -> |
479 | 506 | xep0334; |
507 | get_mod(<<"unsupported-applications">>, | |
508 | <<"urn:xmpp:jingle:1">>) -> | |
509 | xep0166; | |
480 | 510 | get_mod(<<"text">>, <<"urn:xmpp:reporting:0">>) -> |
481 | 511 | xep0377; |
482 | 512 | get_mod(<<"credentials-expired">>, |
491 | 521 | get_mod(<<"address">>, |
492 | 522 | <<"http://jabber.org/protocol/address">>) -> |
493 | 523 | 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; | |
494 | 530 | get_mod(<<"c">>, |
495 | 531 | <<"http://jabber.org/protocol/caps">>) -> |
496 | 532 | xep0115; |
524 | 560 | get_mod(<<"stream:stream">>, <<"jabber:client">>) -> |
525 | 561 | rfc6120; |
526 | 562 | get_mod(<<"data">>, <<"urn:xmpp:bob">>) -> xep0231; |
563 | get_mod(<<"success">>, <<"urn:xmpp:jingle:1">>) -> | |
564 | xep0166; | |
527 | 565 | get_mod(<<"method">>, |
528 | 566 | <<"http://jabber.org/protocol/compress">>) -> |
529 | 567 | xep0138; |
530 | 568 | get_mod(<<"x">>, <<"jabber:x:data">>) -> xep0004; |
569 | get_mod(<<"transport">>, | |
570 | <<"urn:xmpp:jingle:transports:s5b:1">>) -> | |
571 | xep0260; | |
531 | 572 | get_mod(<<"CONFIDENTIAL">>, <<"vcard-temp">>) -> |
532 | 573 | xep0054; |
533 | 574 | get_mod(<<"subscribe">>, |
534 | 575 | <<"http://jabber.org/protocol/pubsub">>) -> |
535 | 576 | xep0060; |
577 | get_mod(<<"media-type">>, | |
578 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
579 | xep0234; | |
536 | 580 | get_mod(<<"stream:features">>, <<"jabber:client">>) -> |
537 | 581 | rfc6120; |
538 | 582 | get_mod(<<"fin">>, <<"urn:xmpp:mam:1">>) -> xep0313; |
549 | 593 | get_mod(<<"errors">>, |
550 | 594 | <<"urn:xmpp:features:dialback">>) -> |
551 | 595 | 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; | |
552 | 607 | get_mod(<<"host-gone">>, |
553 | 608 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
554 | 609 | rfc6120; |
560 | 615 | get_mod(<<"not-subscribed">>, |
561 | 616 | <<"http://jabber.org/protocol/pubsub#errors">>) -> |
562 | 617 | xep0060; |
618 | get_mod(<<"size">>, | |
619 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
620 | xep0234; | |
563 | 621 | get_mod(<<"query">>, |
564 | 622 | <<"http://jabber.org/protocol/stats">>) -> |
565 | 623 | xep0039; |
599 | 657 | get_mod(<<"a">>, <<"urn:xmpp:sm:3">>) -> xep0198; |
600 | 658 | get_mod(<<"info">>, <<"urn:xmpp:avatar:metadata">>) -> |
601 | 659 | xep0084; |
660 | get_mod(<<"date">>, | |
661 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
662 | xep0234; | |
602 | 663 | get_mod(<<"not-allowed">>, |
603 | 664 | <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) -> |
604 | 665 | rfc6120; |
615 | 676 | get_mod(<<"file-too-large">>, |
616 | 677 | <<"eu:siacs:conversations:http:upload">>) -> |
617 | 678 | xep0363; |
679 | get_mod(<<"received">>, | |
680 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
681 | xep0234; | |
618 | 682 | get_mod(<<"JABBERID">>, <<"vcard-temp">>) -> xep0054; |
619 | 683 | get_mod(<<"subscription">>, |
620 | 684 | <<"http://jabber.org/protocol/pubsub#event">>) -> |
621 | 685 | xep0060; |
686 | get_mod(<<"decline">>, <<"urn:xmpp:jingle:1">>) -> | |
687 | xep0166; | |
622 | 688 | get_mod(<<"key">>, <<"jabber:iq:register">>) -> xep0077; |
623 | 689 | get_mod(<<"unsupported">>, |
624 | 690 | <<"http://jabber.org/protocol/pubsub#errors">>) -> |
646 | 712 | get_mod(<<"max-file-size">>, |
647 | 713 | <<"urn:xmpp:http:upload">>) -> |
648 | 714 | 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; | |
649 | 721 | get_mod(<<"not-well-formed">>, |
650 | 722 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
651 | 723 | rfc6120; |
654 | 726 | xep0363; |
655 | 727 | get_mod(<<"perm">>, <<"urn:xmpp:privilege:1">>) -> |
656 | 728 | xep0356; |
729 | get_mod(<<"close">>, | |
730 | <<"http://jabber.org/protocol/ibb">>) -> | |
731 | xep0047; | |
657 | 732 | get_mod(<<"internal-server-error">>, |
658 | 733 | <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) -> |
659 | 734 | rfc6120; |
668 | 743 | get_mod(<<"metadata">>, |
669 | 744 | <<"urn:xmpp:avatar:metadata">>) -> |
670 | 745 | 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; | |
678 | 746 | get_mod(<<"password">>, |
679 | 747 | <<"http://jabber.org/protocol/muc#owner">>) -> |
680 | 748 | xep0045; |
681 | get_mod(<<"error">>, <<"jabber:server">>) -> rfc6120; | |
682 | 749 | get_mod(<<"method">>, |
683 | 750 | <<"http://jabber.org/features/compress">>) -> |
684 | 751 | xep0138; |
685 | get_mod(<<"VIDEO">>, <<"vcard-temp">>) -> xep0054; | |
686 | get_mod(<<"redirect">>, | |
687 | <<"http://jabber.org/protocol/pubsub#event">>) -> | |
688 | xep0060; | |
689 | 752 | get_mod(<<"invalid-jid">>, |
690 | 753 | <<"http://jabber.org/protocol/pubsub#errors">>) -> |
691 | 754 | xep0060; |
692 | get_mod(<<"destroy">>, | |
693 | <<"http://jabber.org/protocol/muc#user">>) -> | |
694 | xep0045; | |
695 | get_mod(<<"id">>, <<"jabber:x:event">>) -> xep0022; | |
696 | 755 | 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; | |
701 | 756 | get_mod(<<"LOCALITY">>, <<"vcard-temp">>) -> xep0054; |
702 | get_mod(<<"redirect">>, | |
703 | <<"http://jabber.org/protocol/pubsub">>) -> | |
704 | xep0060; | |
705 | 757 | get_mod(<<"inactive">>, |
706 | 758 | <<"http://jabber.org/protocol/chatstates">>) -> |
707 | 759 | 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; | |
716 | 760 | get_mod(<<"affiliations">>, |
717 | 761 | <<"http://jabber.org/protocol/pubsub#owner">>) -> |
718 | 762 | xep0060; |
719 | 763 | get_mod(<<"first">>, |
720 | 764 | <<"http://jabber.org/protocol/rsm">>) -> |
721 | 765 | xep0059; |
722 | get_mod(<<"fin">>, <<"urn:xmpp:mam:0">>) -> xep0313; | |
723 | get_mod(<<"enabled">>, <<"urn:xmpp:sm:2">>) -> xep0198; | |
724 | 766 | get_mod(<<"get">>, |
725 | 767 | <<"eu:siacs:conversations:http:upload">>) -> |
726 | 768 | xep0363; |
769 | get_mod(<<"failed-application">>, | |
770 | <<"urn:xmpp:jingle:1">>) -> | |
771 | xep0166; | |
727 | 772 | get_mod(<<"priority">>, <<"jabber:server">>) -> rfc6120; |
728 | 773 | get_mod(<<"redirect">>, |
729 | 774 | <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) -> |
731 | 776 | get_mod(<<"encryption-required">>, |
732 | 777 | <<"urn:ietf:params:xml:ns:xmpp-sasl">>) -> |
733 | 778 | 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; | |
742 | 779 | get_mod(<<"stanza-id">>, <<"urn:xmpp:sid:0">>) -> |
743 | 780 | xep0359; |
744 | 781 | get_mod(<<"complete">>, |
745 | 782 | <<"http://jabber.org/protocol/commands">>) -> |
746 | 783 | xep0050; |
747 | get_mod(<<"file-too-large">>, | |
748 | <<"urn:xmpp:http:upload:0">>) -> | |
749 | xep0363; | |
750 | 784 | get_mod(<<"enable">>, <<"urn:xmpp:push:0">>) -> xep0357; |
751 | 785 | get_mod(<<"time">>, <<"urn:xmpp:time">>) -> xep0202; |
752 | get_mod(<<"conflict">>, | |
753 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> | |
754 | rfc6120; | |
755 | 786 | get_mod(<<"invalid-namespace">>, |
756 | 787 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
757 | 788 | rfc6120; |
758 | get_mod(<<"X400">>, <<"vcard-temp">>) -> xep0054; | |
759 | 789 | get_mod(<<"PREFIX">>, <<"vcard-temp">>) -> xep0054; |
760 | get_mod(<<"affiliation">>, | |
761 | <<"http://jabber.org/protocol/pubsub">>) -> | |
762 | xep0060; | |
763 | 790 | get_mod(<<"affiliations">>, |
764 | 791 | <<"http://jabber.org/protocol/pubsub">>) -> |
765 | 792 | 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; | |
771 | 796 | get_mod(<<"aborted">>, |
772 | 797 | <<"urn:ietf:params:xml:ns:xmpp-sasl">>) -> |
773 | 798 | rfc6120; |
774 | 799 | get_mod(<<"address">>, <<"jabber:iq:register">>) -> |
775 | 800 | xep0077; |
776 | get_mod(<<"VOICE">>, <<"vcard-temp">>) -> xep0054; | |
777 | 801 | get_mod(<<"LOGO">>, <<"vcard-temp">>) -> xep0054; |
778 | get_mod(<<"vCard">>, <<"vcard-temp">>) -> xep0054; | |
779 | 802 | get_mod(<<"retract">>, |
780 | 803 | <<"http://jabber.org/protocol/pubsub">>) -> |
781 | 804 | xep0060; |
782 | get_mod(<<"gone">>, | |
783 | <<"http://jabber.org/protocol/chatstates">>) -> | |
784 | xep0085; | |
785 | 805 | get_mod(<<"destroy">>, |
786 | 806 | <<"http://jabber.org/protocol/muc#owner">>) -> |
787 | 807 | 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; | |
795 | 808 | get_mod(<<"compress">>, |
796 | 809 | <<"http://jabber.org/protocol/compress">>) -> |
797 | 810 | xep0138; |
798 | get_mod(<<"ORGNAME">>, <<"vcard-temp">>) -> xep0054; | |
799 | get_mod(<<"NOTE">>, <<"vcard-temp">>) -> xep0054; | |
800 | get_mod(<<"REV">>, <<"vcard-temp">>) -> xep0054; | |
801 | 811 | get_mod(<<"ADR">>, <<"vcard-temp">>) -> xep0054; |
802 | 812 | get_mod(<<"continue">>, |
803 | 813 | <<"http://jabber.org/protocol/muc#user">>) -> |
804 | 814 | xep0045; |
805 | 815 | get_mod(<<"subscription">>, <<"urn:xmpp:mucsub:0">>) -> |
806 | 816 | p1_mucsub; |
807 | get_mod(<<"query">>, <<"urn:xmpp:mam:1">>) -> xep0313; | |
808 | 817 | get_mod(<<"item">>, <<"jabber:iq:search">>) -> xep0055; |
809 | 818 | get_mod(<<"stream:stream">>, <<"jabber:server">>) -> |
810 | 819 | 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; | |
820 | 820 | get_mod(<<"proceed">>, |
821 | 821 | <<"urn:ietf:params:xml:ns:xmpp-tls">>) -> |
822 | 822 | rfc6120; |
825 | 825 | get_mod(<<"undefined-condition">>, |
826 | 826 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
827 | 827 | rfc6120; |
828 | get_mod(<<"unsupported-feature">>, | |
829 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> | |
830 | rfc6120; | |
831 | 828 | 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; | |
842 | 829 | get_mod(<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>) -> |
843 | 830 | p1_mucsub; |
844 | 831 | 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; | |
851 | 832 | get_mod(<<"slot">>, <<"urn:xmpp:http:upload:0">>) -> |
852 | 833 | 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; | |
853 | 840 | get_mod(<<"query">>, <<"jabber:iq:privacy">>) -> |
854 | 841 | xep0016; |
855 | 842 | get_mod(<<"forbidden">>, |
866 | 853 | get_mod(<<"request">>, |
867 | 854 | <<"eu:siacs:conversations:http:upload">>) -> |
868 | 855 | xep0363; |
856 | get_mod(<<"file">>, | |
857 | <<"urn:xmpp:jingle:apps:file-transfer:5">>) -> | |
858 | xep0234; | |
869 | 859 | get_mod(<<"query">>, <<"jabber:iq:auth">>) -> xep0078; |
870 | 860 | get_mod(<<"username">>, <<"jabber:iq:register">>) -> |
871 | 861 | xep0077; |
890 | 880 | <<"http://jabber.org/protocol/rsm">>) -> |
891 | 881 | xep0059; |
892 | 882 | 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; | |
893 | 886 | get_mod(<<"active">>, <<"jabber:iq:privacy">>) -> |
894 | 887 | xep0016; |
895 | 888 | get_mod(<<"text">>, |
917 | 910 | get_mod(<<"max-file-size">>, |
918 | 911 | <<"urn:xmpp:http:upload:0">>) -> |
919 | 912 | xep0363; |
913 | get_mod(<<"cancel">>, <<"urn:xmpp:jingle:1">>) -> | |
914 | xep0166; | |
920 | 915 | get_mod(<<"error">>, |
921 | 916 | <<"http://jabber.org/protocol/stats">>) -> |
922 | 917 | xep0039; |
936 | 931 | xep0357; |
937 | 932 | get_mod(<<"delegated">>, <<"urn:xmpp:delegation:1">>) -> |
938 | 933 | xep0355; |
934 | get_mod(<<"hash-used">>, <<"urn:xmpp:hashes:2">>) -> | |
935 | xep0300; | |
936 | get_mod(<<"security-error">>, | |
937 | <<"urn:xmpp:jingle:1">>) -> | |
938 | xep0166; | |
939 | 939 | get_mod(<<"presence">>, |
940 | 940 | <<"jabber:component:accept">>) -> |
941 | 941 | rfc6120; |
976 | 976 | get_mod(<<"fetch">>, |
977 | 977 | <<"http://jabber.org/protocol/offline">>) -> |
978 | 978 | xep0013; |
979 | get_mod(<<"failed-transport">>, | |
980 | <<"urn:xmpp:jingle:1">>) -> | |
981 | xep0166; | |
979 | 982 | get_mod(<<"abuse">>, <<"urn:xmpp:reporting:0">>) -> |
980 | 983 | xep0377; |
981 | 984 | get_mod(<<"state">>, <<"jabber:iq:register">>) -> |
1181 | 1184 | get_mod(<<"subject">>, <<"jabber:client">>) -> rfc6120; |
1182 | 1185 | get_mod(<<"date">>, <<"jabber:iq:register">>) -> |
1183 | 1186 | xep0077; |
1187 | get_mod(<<"transport">>, | |
1188 | <<"urn:xmpp:jingle:transports:ibb:1">>) -> | |
1189 | xep0261; | |
1184 | 1190 | get_mod(<<"auth">>, |
1185 | 1191 | <<"urn:ietf:params:xml:ns:xmpp-sasl">>) -> |
1186 | 1192 | rfc6120; |
1203 | 1209 | get_mod(<<"reason">>, |
1204 | 1210 | <<"http://jabber.org/protocol/muc#owner">>) -> |
1205 | 1211 | xep0045; |
1212 | get_mod(<<"candidate-error">>, | |
1213 | <<"urn:xmpp:jingle:transports:s5b:1">>) -> | |
1214 | xep0260; | |
1206 | 1215 | get_mod(<<"password">>, <<"storage:bookmarks">>) -> |
1207 | 1216 | xep0048; |
1208 | 1217 | get_mod(<<"message">>, <<"jabber:component:accept">>) -> |
1216 | 1225 | <<"http://jabber.org/protocol/muc#user">>) -> |
1217 | 1226 | xep0045; |
1218 | 1227 | get_mod(<<"never">>, <<"urn:xmpp:mam:2">>) -> xep0313; |
1228 | get_mod(<<"proxy-error">>, | |
1229 | <<"urn:xmpp:jingle:transports:s5b:1">>) -> | |
1230 | xep0260; | |
1219 | 1231 | get_mod(<<"stat">>, |
1220 | 1232 | <<"http://jabber.org/protocol/stats">>) -> |
1221 | 1233 | xep0039; |
1222 | 1234 | get_mod(<<"always">>, <<"urn:xmpp:mam:tmp">>) -> |
1223 | 1235 | xep0313; |
1224 | 1236 | get_mod(<<"desc">>, <<"jabber:x:oob">>) -> xep0066; |
1237 | get_mod(<<"reason">>, <<"urn:xmpp:jingle:1">>) -> | |
1238 | xep0166; | |
1225 | 1239 | get_mod(<<"thread">>, <<"jabber:server">>) -> rfc6120; |
1226 | 1240 | get_mod(<<"host-unknown">>, |
1227 | 1241 | <<"urn:ietf:params:xml:ns:xmpp-streams">>) -> |
1272 | 1286 | get_mod(<<"delegation">>, |
1273 | 1287 | <<"urn:xmpp:delegation:1">>) -> |
1274 | 1288 | xep0355; |
1289 | get_mod(<<"expired">>, <<"urn:xmpp:jingle:1">>) -> | |
1290 | xep0166; | |
1275 | 1291 | get_mod(<<"bad-request">>, |
1276 | 1292 | <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) -> |
1277 | 1293 | rfc6120; |
1283 | 1299 | xep0280; |
1284 | 1300 | get_mod(<<"db:result">>, <<"jabber:server">>) -> |
1285 | 1301 | xep0220; |
1302 | get_mod(<<"busy">>, <<"urn:xmpp:jingle:1">>) -> xep0166; | |
1303 | get_mod(<<"text">>, <<"urn:xmpp:jingle:1">>) -> xep0166; | |
1286 | 1304 | get_mod(<<"group">>, <<"jabber:iq:roster">>) -> rfc6121; |
1287 | 1305 | get_mod(<<"incorrect-encoding">>, |
1288 | 1306 | <<"urn:ietf:params:xml:ns:xmpp-sasl">>) -> |
1298 | 1316 | <<"http://jabber.org/protocol/pubsub#event">>) -> |
1299 | 1317 | xep0060; |
1300 | 1318 | get_mod(<<"resumed">>, <<"urn:xmpp:sm:3">>) -> xep0198; |
1319 | get_mod(<<"activated">>, | |
1320 | <<"urn:xmpp:jingle:transports:s5b:1">>) -> | |
1321 | xep0260; | |
1301 | 1322 | get_mod(<<"conference">>, <<"storage:bookmarks">>) -> |
1302 | 1323 | xep0048; |
1303 | 1324 | get_mod(<<"registered">>, <<"jabber:iq:register">>) -> |
1357 | 1378 | get_mod(<<"default">>, |
1358 | 1379 | <<"http://jabber.org/protocol/pubsub">>) -> |
1359 | 1380 | xep0060; |
1381 | get_mod(<<"alternative-session">>, | |
1382 | <<"urn:xmpp:jingle:1">>) -> | |
1383 | xep0166; | |
1360 | 1384 | get_mod(<<"remote-server-timeout">>, |
1361 | 1385 | <<"urn:ietf:params:xml:ns:xmpp-stanzas">>) -> |
1362 | 1386 | rfc6120; |
1366 | 1390 | get_mod(<<"items">>, |
1367 | 1391 | <<"http://jabber.org/protocol/pubsub">>) -> |
1368 | 1392 | 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; | |
1369 | 1501 | get_mod(Name, XMLNS) -> |
1370 | 1502 | xmpp_codec_external:lookup(Name, XMLNS). |
1371 | 1503 | |
1376 | 1508 | get_mod({compression, _}) -> xep0138; |
1377 | 1509 | get_mod({mam_prefs, _, _, _, _}) -> xep0313; |
1378 | 1510 | get_mod({upload_file_too_large, _, _}) -> xep0363; |
1511 | get_mod({jingle_ft_description, _}) -> xep0234; | |
1379 | 1512 | get_mod({muc_user, _, _, _, _, _, _}) -> xep0045; |
1380 | 1513 | get_mod({bytestreams, _, _, _, _, _, _}) -> xep0065; |
1381 | get_mod({muc_invite, _, _, _, _}) -> xep0045; | |
1382 | 1514 | get_mod({sasl_auth, _, _}) -> rfc6120; |
1383 | 1515 | get_mod({vcard_logo, _, _, _}) -> xep0054; |
1384 | 1516 | get_mod({ps_item, _, _, _, _, _}) -> xep0060; |
1385 | get_mod({ps_event, _, _, _, _, _, _}) -> xep0060; | |
1386 | 1517 | get_mod({mam_fin, _, _, _, _, _}) -> xep0313; |
1518 | get_mod({jingle_ft_error, _}) -> xep0234; | |
1387 | 1519 | get_mod({legacy_auth, _, _, _, _}) -> xep0078; |
1388 | get_mod({sasl_mechanisms, _}) -> rfc6120; | |
1389 | get_mod({starttls, _}) -> rfc6120; | |
1390 | 1520 | get_mod({stream_features, _}) -> rfc6120; |
1391 | 1521 | get_mod({muc_item, _, _, _, _, _, _, _}) -> xep0045; |
1392 | get_mod({stream_start, _, _, _, _, _, _, _, _}) -> | |
1393 | rfc6120; | |
1394 | 1522 | get_mod({block_list, _}) -> xep0191; |
1395 | 1523 | get_mod({'see-other-host', _}) -> rfc6120; |
1396 | 1524 | get_mod({muc_destroy, _, _, _, _}) -> xep0045; |
1398 | 1526 | get_mod({carbons_sent, _}) -> xep0280; |
1399 | 1527 | get_mod({hint, _}) -> xep0334; |
1400 | 1528 | get_mod({origin_id, _}) -> xep0359; |
1529 | get_mod({hash_used, _}) -> xep0300; | |
1401 | 1530 | get_mod({disco_items, _, _, _}) -> xep0030; |
1402 | 1531 | get_mod({private, _}) -> xep0049; |
1403 | 1532 | get_mod({sasl_challenge, _}) -> rfc6120; |
1405 | 1534 | get_mod({vcard_xupdate, _}) -> xep0153; |
1406 | 1535 | get_mod({db_feature, _}) -> xep0220; |
1407 | 1536 | get_mod({handshake, _}) -> xep0114; |
1537 | get_mod({ibb_close, _}) -> xep0047; | |
1408 | 1538 | get_mod({block_item, _, _}) -> xep0191; |
1409 | 1539 | get_mod({message, _, _, _, _, _, _, _, _, _, _}) -> |
1410 | 1540 | rfc6120; |
1420 | 1550 | xep0054; |
1421 | 1551 | get_mod({xdata_option, _, _}) -> xep0004; |
1422 | 1552 | get_mod({ps_unsubscribe, _, _, _}) -> xep0060; |
1553 | get_mod({muc_subscription, _, _}) -> p1_mucsub; | |
1423 | 1554 | get_mod({sm_resume, _, _, _}) -> xep0198; |
1424 | 1555 | get_mod({push_enable, _, _, _}) -> xep0357; |
1556 | get_mod({jingle_ft_range, _, _, _}) -> xep0234; | |
1425 | 1557 | get_mod({vcard_geo, _, _}) -> xep0054; |
1426 | 1558 | get_mod({mam_query, _, _, _, _, _, _, _, _}) -> xep0313; |
1427 | 1559 | get_mod({xevent, _, _, _, _, _}) -> xep0022; |
1428 | 1560 | get_mod({db_result, _, _, _, _, _}) -> xep0220; |
1561 | get_mod({jingle_content, _, _, _, _, _}) -> xep0166; | |
1429 | 1562 | get_mod({bookmark_conference, _, _, _, _, _}) -> |
1430 | 1563 | xep0048; |
1431 | 1564 | get_mod({feature_register}) -> xep0077; |
1438 | 1571 | get_mod({disco_info, _, _, _, _}) -> xep0030; |
1439 | 1572 | get_mod({sm_a, _, _}) -> xep0198; |
1440 | 1573 | get_mod({oob_x, _, _, _}) -> xep0066; |
1574 | get_mod({jingle_s5b_transport, _, _, _, _, _, _, _}) -> | |
1575 | xep0260; | |
1441 | 1576 | get_mod({privacy_query, _, _, _}) -> xep0016; |
1442 | 1577 | get_mod({block, _}) -> xep0191; |
1443 | 1578 | get_mod({vcard_label, _, _, _, _, _, _, _, _}) -> |
1447 | 1582 | get_mod({delay, _, _, _}) -> xep0203; |
1448 | 1583 | get_mod({muc_actor, _, _}) -> xep0045; |
1449 | 1584 | get_mod({nick, _}) -> xep0172; |
1585 | get_mod({jingle, _, _, _, _, _, _, _}) -> xep0166; | |
1450 | 1586 | get_mod({stats, _, _}) -> xep0039; |
1451 | 1587 | get_mod({vcard_temp, _, _, _, _, _, _, _, _, _, _, _, _, |
1452 | 1588 | _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}) -> |
1459 | 1595 | get_mod({privacy_list, _, _}) -> xep0016; |
1460 | 1596 | get_mod({report, _, _}) -> xep0377; |
1461 | 1597 | get_mod({message_thread, _, _}) -> rfc6120; |
1462 | get_mod({text, _, _}) -> rfc6120; | |
1463 | 1598 | get_mod({shim, _}) -> xep0131; |
1464 | 1599 | get_mod({mam_archived, _, _}) -> xep0313; |
1465 | 1600 | get_mod({forwarded, _, _}) -> xep0297; |
1467 | 1602 | get_mod({mix_participant, _, _}) -> xep0369; |
1468 | 1603 | get_mod({stanza_id, _, _}) -> xep0359; |
1469 | 1604 | get_mod({delegated, _, _}) -> xep0355; |
1605 | get_mod({text, _, _}) -> xep0234; | |
1606 | get_mod({jingle_ft_received, _, _}) -> xep0234; | |
1470 | 1607 | get_mod({bookmark_storage, _, _}) -> xep0048; |
1471 | 1608 | get_mod({sasl_failure, _, _}) -> rfc6120; |
1472 | 1609 | get_mod({xmpp_session, _}) -> rfc3921; |
1486 | 1623 | get_mod({mam_result, _, _, _, _}) -> xep0313; |
1487 | 1624 | get_mod({sm_failed, _, _, _, _}) -> xep0198; |
1488 | 1625 | get_mod({upload_request, _, _, _, _}) -> xep0363; |
1626 | get_mod({jingle_ft_checksum, _, _, _}) -> xep0234; | |
1489 | 1627 | get_mod({version, _, _, _}) -> xep0092; |
1490 | 1628 | get_mod({vcard_org, _, _}) -> xep0054; |
1491 | 1629 | get_mod({ps_items, _, _, _, _, _, _}) -> xep0060; |
1492 | 1630 | get_mod({muc_subscribe, _, _, _, _}) -> p1_mucsub; |
1493 | 1631 | get_mod({feature_csi}) -> xep0352; |
1632 | get_mod({ibb_open, _, _, _}) -> xep0047; | |
1494 | 1633 | get_mod({roster_item, _, _, _, _, _}) -> rfc6121; |
1495 | 1634 | get_mod({vcard_tel, _, _, _, _, _, _, _, _, _, _, _, _, |
1496 | 1635 | _, _}) -> |
1500 | 1639 | get_mod({search_item, _, _, _, _, _}) -> xep0055; |
1501 | 1640 | get_mod({xcaptcha, _}) -> xep0158; |
1502 | 1641 | get_mod({avatar_data, _}) -> xep0084; |
1642 | get_mod({hash, _, _}) -> xep0300; | |
1503 | 1643 | get_mod({roster_query, _, _}) -> rfc6121; |
1504 | 1644 | get_mod({vcard_key, _, _}) -> xep0054; |
1505 | 1645 | get_mod({sm_r, _}) -> xep0198; |
1506 | 1646 | get_mod({media_uri, _, _}) -> xep0221; |
1507 | 1647 | get_mod({upload_slot_0, _, _, _}) -> xep0363; |
1508 | 1648 | get_mod({delegation_query, _, _}) -> xep0355; |
1649 | get_mod({jingle_error, _}) -> xep0166; | |
1650 | get_mod({jingle_s5b_candidate, _, _, _, _, _, _}) -> | |
1651 | xep0260; | |
1509 | 1652 | get_mod({sasl_success, _}) -> rfc6120; |
1510 | 1653 | get_mod({compress, _}) -> xep0138; |
1511 | 1654 | get_mod({xdata_field, _, _, _, _, _, _, _, _}) -> |
1515 | 1658 | get_mod({adhoc_command, _, _, _, _, _, _, _, _}) -> |
1516 | 1659 | xep0050; |
1517 | 1660 | get_mod({media, _, _, _}) -> xep0221; |
1661 | get_mod({ibb_data, _, _, _}) -> xep0047; | |
1518 | 1662 | get_mod({identity, _, _, _, _}) -> xep0030; |
1519 | 1663 | get_mod({redirect, _}) -> rfc6120; |
1520 | 1664 | get_mod({muc_history, _, _, _, _}) -> xep0045; |
1521 | 1665 | get_mod({muc_owner, _, _, _}) -> xep0045; |
1666 | get_mod({jingle_ft_file, _, _, _, _, _, _, _, _}) -> | |
1667 | xep0234; | |
1522 | 1668 | get_mod({bookmark_url, _, _}) -> xep0048; |
1523 | 1669 | get_mod({gone, _}) -> rfc6120; |
1524 | 1670 | get_mod({sasl_response, _}) -> rfc6120; |
1526 | 1672 | get_mod({muc_unique, _}) -> xep0045; |
1527 | 1673 | get_mod({receipt_response, _}) -> xep0184; |
1528 | 1674 | get_mod({sic, _, _, _}) -> xep0279; |
1675 | get_mod({idle, _}) -> xep0319; | |
1529 | 1676 | get_mod({sm_resumed, _, _, _}) -> xep0198; |
1530 | 1677 | get_mod({offline_item, _, _}) -> xep0013; |
1531 | 1678 | get_mod({db_verify, _, _, _, _, _, _}) -> xep0220; |
1532 | 1679 | get_mod({upload_slot, _, _, _}) -> xep0363; |
1680 | get_mod({jingle_ibb_transport, _, _, _}) -> xep0261; | |
1533 | 1681 | get_mod({muc_decline, _, _, _}) -> xep0045; |
1534 | 1682 | get_mod({sm_enable, _, _, _}) -> xep0198; |
1535 | 1683 | get_mod({offline, _, _, _}) -> xep0013; |
1567 | 1715 | get_mod({privilege, _, _}) -> xep0356; |
1568 | 1716 | get_mod({stanza_error, _, _, _, _, _, _}) -> rfc6120; |
1569 | 1717 | 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; | |
1570 | 1725 | 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. |
29 | 29 | set_xdata_field/2, has_xdata_var/2, |
30 | 30 | make_adhoc_response/1, make_adhoc_response/2, |
31 | 31 | decode_timestamp/1, encode_timestamp/1]). |
32 | -export([hex/1]). | |
32 | 33 | |
33 | 34 | -include("xmpp.hrl"). |
34 | 35 | |
144 | 145 | [Year, Month, Day, Hour, Minute, Second, |
145 | 146 | Fraction])). |
146 | 147 | |
148 | -spec hex(binary()) -> binary(). | |
149 | hex(Bin) -> | |
150 | << <<(to_xchar(N div 16)), (to_xchar(N rem 16))>> || <<N>> <= Bin >>. | |
151 | ||
147 | 152 | %%%=================================================================== |
148 | 153 | %%% Internal functions |
149 | 154 | %%%=================================================================== |
182 | 187 | I when I >= Min, I =< Max -> |
183 | 188 | I |
184 | 189 | end. |
190 | ||
191 | to_xchar(D) when (D >= 0) and (D < 10) -> D + $0; | |
192 | to_xchar(D) -> D + $a - 10. |