diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..d2656c3a --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,27 @@ +include: package:pedantic/analysis_options.yaml + +analyzer: + errors: + unused_local_variable: ignore + unnecessary_new: ignore + slash_for_doc_comments: ignore + library_prefixes: ignore + unused_field: ignore + top_level_function_literal_block: ignore + avoid_init_to_null: ignore + prefer_is_empty: ignore + unused_element: ignore + curly_braces_in_flow_control_structures: ignore + unnecessary_null_in_if_null_operators: ignore + prefer_contains: ignore + missing_return: ignore + override_on_non_overriding_getter: ignore + override_on_non_overriding_field: ignore + override_on_non_overriding_method: ignore + avoid_types_as_parameter_names: ignore + empty_catches: ignore + unawaited_futures: ignore + use_rethrow_when_possible: ignore + unused_import: ignore + must_be_immutable: ignore + diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 00000000..2ddde2a5 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,73 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 00000000..d2656c3a --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,27 @@ +include: package:pedantic/analysis_options.yaml + +analyzer: + errors: + unused_local_variable: ignore + unnecessary_new: ignore + slash_for_doc_comments: ignore + library_prefixes: ignore + unused_field: ignore + top_level_function_literal_block: ignore + avoid_init_to_null: ignore + prefer_is_empty: ignore + unused_element: ignore + curly_braces_in_flow_control_structures: ignore + unnecessary_null_in_if_null_operators: ignore + prefer_contains: ignore + missing_return: ignore + override_on_non_overriding_getter: ignore + override_on_non_overriding_field: ignore + override_on_non_overriding_method: ignore + avoid_types_as_parameter_names: ignore + empty_catches: ignore + unawaited_futures: ignore + use_rethrow_when_possible: ignore + unused_import: ignore + must_be_immutable: ignore + diff --git a/lib/src/Config.dart b/lib/src/Config.dart index e05f339d..6a9f6192 100644 --- a/lib/src/Config.dart +++ b/lib/src/Config.dart @@ -1,3 +1,4 @@ +import 'Constants.dart'; import 'Utils.dart' as Utils; import 'Constants.dart' as DartSIP_C; import 'Grammar.dart'; @@ -33,7 +34,7 @@ class Settings { // Session parameters. var session_timers = true; - var session_timers_refresh_method = DartSIP_C.UPDATE; + SipMethod session_timers_refresh_method = SipMethod.UPDATE; var no_answer_timeout = 60; // Registration parameters. @@ -178,15 +179,14 @@ class Checks { dst.session_timers = session_timers; } }, - 'session_timers_refresh_method': (src, dst) { - var method = src.session_timers_refresh_method; - if (method == null) return; - if (method is String) { - method = method.toUpperCase(); - if (method == DartSIP_C.INVITE || method == DartSIP_C.UPDATE) { - dst.session_timers_refresh_method = method; + 'session_timers_refresh_method': ( src, dst) { + Settings srcSettings = src as Settings; + Settings dstSettings = dst as Settings; + SipMethod method = srcSettings.session_timers_refresh_method; + if (method == SipMethod.INVITE || method == SipMethod.UPDATE) { + dstSettings.session_timers_refresh_method = method; } - } + }, 'password': (src, dst) { var password = src.password; diff --git a/lib/src/Constants.dart b/lib/src/Constants.dart index 188a19ac..6fdfed61 100644 --- a/lib/src/Constants.dart +++ b/lib/src/Constants.dart @@ -1,58 +1,59 @@ -var USER_AGENT = 'dart-sip-ua v0.0.1'; +var USER_AGENT = 'dart-sip-ua v0.0.1'; - // SIP scheme. -var SIP = 'sip'; +// SIP scheme. +var SIP = 'sip'; var SIPS = 'sips'; - // End and Failure causes. +// End and Failure causes. class Causes { // Generic error causes. static final CONNECTION_ERROR = 'Connection Error'; - static final REQUEST_TIMEOUT = 'Request Timeout'; + static final REQUEST_TIMEOUT = 'Request Timeout'; static final SIP_FAILURE_CODE = 'SIP Failure Code'; - static final INTERNAL_ERROR = 'Internal Error'; + static final INTERNAL_ERROR = 'Internal Error'; // SIP error causes. - static final BUSY = 'Busy'; - static final REJECTED = 'Rejected'; - static final REDIRECTED = 'Redirected'; - static final UNAVAILABLE = 'Unavailable'; - static final NOT_FOUND = 'Not Found'; - static final ADDRESS_INCOMPLETE = 'Address Incomplete'; - static final INCOMPATIBLE_SDP = 'Incompatible SDP'; - static final MISSING_SDP = 'Missing SDP'; + static final BUSY = 'Busy'; + static final REJECTED = 'Rejected'; + static final REDIRECTED = 'Redirected'; + static final UNAVAILABLE = 'Unavailable'; + static final NOT_FOUND = 'Not Found'; + static final ADDRESS_INCOMPLETE = 'Address Incomplete'; + static final INCOMPATIBLE_SDP = 'Incompatible SDP'; + static final MISSING_SDP = 'Missing SDP'; static final AUTHENTICATION_ERROR = 'Authentication Error'; - // Session error causes. - static final BYE = 'Terminated'; - static final WEBRTC_ERROR = 'WebRTC Error'; - static final CANCELED = 'Canceled'; - static final NO_ANSWER = 'No Answer'; - static final EXPIRES = 'Expires'; - static final NO_ACK = 'No ACK'; - static final DIALOG_ERROR = 'Dialog Error'; + // Session error causes. + static final BYE = 'Terminated'; + static final WEBRTC_ERROR = 'WebRTC Error'; + static final CANCELED = 'Canceled'; + static final NO_ANSWER = 'No Answer'; + static final EXPIRES = 'Expires'; + static final NO_ACK = 'No ACK'; + static final DIALOG_ERROR = 'Dialog Error'; static final USER_DENIED_MEDIA_ACCESS = 'User Denied Media Access'; - static final BAD_MEDIA_DESCRIPTION = 'Bad Media Description'; - static final RTP_TIMEOUT = 'RTP Timeout'; + static final BAD_MEDIA_DESCRIPTION = 'Bad Media Description'; + static final RTP_TIMEOUT = 'RTP Timeout'; } - var SIP_ERROR_CAUSES = { - Causes.REDIRECTED : [ 300, 301, 302, 305, 380 ], - Causes.BUSY : [ 486, 600 ], - Causes.REJECTED : [ 403, 603 ], - Causes.NOT_FOUND : [ 404, 604 ], - Causes.UNAVAILABLE : [ 480, 410, 408, 430 ], - Causes.ADDRESS_INCOMPLETE : [ 484, 424 ], - Causes.INCOMPATIBLE_SDP : [ 488, 606 ], - Causes.AUTHENTICATION_ERROR : [ 401, 407 ] - }; - class causes { +var SIP_ERROR_CAUSES = { + Causes.REDIRECTED: [300, 301, 302, 305, 380], + Causes.BUSY: [486, 600], + Causes.REJECTED: [403, 603], + Causes.NOT_FOUND: [404, 604], + Causes.UNAVAILABLE: [480, 410, 408, 430], + Causes.ADDRESS_INCOMPLETE: [484, 424], + Causes.INCOMPATIBLE_SDP: [488, 606], + Causes.AUTHENTICATION_ERROR: [401, 407] +}; + +class causes { static final CONNECTION_ERROR = Causes.CONNECTION_ERROR; static final REQUEST_TIMEOUT = Causes.REQUEST_TIMEOUT; static final SIP_FAILURE_CODE = Causes.SIP_FAILURE_CODE; static final INTERNAL_ERROR = Causes.INTERNAL_ERROR; - // SIP error causes. + // SIP error causes. static final BUSY = Causes.BUSY; static final REJECTED = Causes.REJECTED; static final REDIRECTED = Causes.REDIRECTED; @@ -63,7 +64,7 @@ class Causes { static final MISSING_SDP = Causes.MISSING_SDP; static final AUTHENTICATION_ERROR = Causes.AUTHENTICATION_ERROR; - // Session error causes. + // Session error causes. static final BYE = Causes.BYE; static final WEBRTC_ERROR = Causes.WEBRTC_ERROR; static final CANCELED = Causes.CANCELED; @@ -74,9 +75,10 @@ class Causes { static final USER_DENIED_MEDIA_ACCESS = Causes.USER_DENIED_MEDIA_ACCESS; static final BAD_MEDIA_DESCRIPTION = Causes.BAD_MEDIA_DESCRIPTION; static final RTP_TIMEOUT = Causes.RTP_TIMEOUT; - } +} - // SIP Methods. +// SIP Methods. +/* replaced with ENUM ! const ACK = 'ACK'; const BYE = 'BYE'; const CANCEL = 'CANCEL'; @@ -89,88 +91,124 @@ class Causes { const REFER = 'REFER'; const UPDATE = 'UPDATE'; const SUBSCRIBE = 'SUBSCRIBE'; +*/ +enum SipMethod { + ACK, + BYE, + CANCEL, + GET, + INFO, + INVITE, + MESSAGE, + NOTIFY, + OPTIONS, + REGISTER, + REFER, + UPDATE, + SUBSCRIBE +} + +class SipMethodHelper { + static String getName(SipMethod method) { + int period = method.toString().indexOf("."); + return method.toString().substring(period + 1); + } + + static SipMethod fromString(String name) { + if (name != null) { + String cleanName = name.toUpperCase(); + for (SipMethod method in SipMethod.values) { + if (getName(method) == cleanName) { + return method; + } + } + return null; + } + return null; + } +} - /* SIP Response Reasons +/* SIP Response Reasons * DOC: https://www.iana.org/assignments/sip-parameters * Copied from https://github.com/versatica/OverSIP/blob/master/lib/oversip/sip/constants.rb#L7 */ - var REASON_PHRASE = { - 100 : 'Trying', - 180 : 'Ringing', - 181 : 'Call Is Being Forwarded', - 182 : 'Queued', - 183 : 'Session Progress', - 199 : 'Early Dialog Terminated', // draft-ietf-sipcore-199 - 200 : 'OK', - 202 : 'Accepted', // RFC 3265 - 204 : 'No Notification', // RFC 5839 - 300 : 'Multiple Choices', - 301 : 'Moved Permanently', - 302 : 'Moved Temporarily', - 305 : 'Use Proxy', - 380 : 'Alternative Service', - 400 : 'Bad Request', - 401 : 'Unauthorized', - 402 : 'Payment Required', - 403 : 'Forbidden', - 404 : 'Not Found', - 405 : 'Method Not Allowed', - 406 : 'Not Acceptable', - 407 : 'Proxy Authentication Required', - 408 : 'Request Timeout', - 410 : 'Gone', - 412 : 'Conditional Request Failed', // RFC 3903 - 413 : 'Request Entity Too Large', - 414 : 'Request-URI Too Long', - 415 : 'Unsupported Media Type', - 416 : 'Unsupported URI Scheme', - 417 : 'Unknown Resource-Priority', // RFC 4412 - 420 : 'Bad Extension', - 421 : 'Extension Required', - 422 : 'Session Interval Too Small', // RFC 4028 - 423 : 'Interval Too Brief', - 424 : 'Bad Location Information', // RFC 6442 - 428 : 'Use Identity Header', // RFC 4474 - 429 : 'Provide Referrer Identity', // RFC 3892 - 430 : 'Flow Failed', // RFC 5626 - 433 : 'Anonymity Disallowed', // RFC 5079 - 436 : 'Bad Identity-Info', // RFC 4474 - 437 : 'Unsupported Certificate', // RFC 4744 - 438 : 'Invalid Identity Header', // RFC 4744 - 439 : 'First Hop Lacks Outbound Support', // RFC 5626 - 440 : 'Max-Breadth Exceeded', // RFC 5393 - 469 : 'Bad Info Package', // draft-ietf-sipcore-info-events - 470 : 'Consent Needed', // RFC 5360 - 478 : 'Unresolvable Destination', // Custom code copied from Kamailio. - 480 : 'Temporarily Unavailable', - 481 : 'Call/Transaction Does Not Exist', - 482 : 'Loop Detected', - 483 : 'Too Many Hops', - 484 : 'Address Incomplete', - 485 : 'Ambiguous', - 486 : 'Busy Here', - 487 : 'Request Terminated', - 488 : 'Not Acceptable Here', - 489 : 'Bad Event', // RFC 3265 - 491 : 'Request Pending', - 493 : 'Undecipherable', - 494 : 'Security Agreement Required', // RFC 3329 - 500 : 'DartSIP Internal Error', - 501 : 'Not Implemented', - 502 : 'Bad Gateway', - 503 : 'Service Unavailable', - 504 : 'Server Time-out', - 505 : 'Version Not Supported', - 513 : 'Message Too Large', - 580 : 'Precondition Failure', // RFC 3312 - 600 : 'Busy Everywhere', - 603 : 'Decline', - 604 : 'Does Not Exist Anywhere', - 606 : 'Not Acceptable' - }; +var REASON_PHRASE = { + 100: 'Trying', + 180: 'Ringing', + 181: 'Call Is Being Forwarded', + 182: 'Queued', + 183: 'Session Progress', + 199: 'Early Dialog Terminated', // draft-ietf-sipcore-199 + 200: 'OK', + 202: 'Accepted', // RFC 3265 + 204: 'No Notification', // RFC 5839 + 300: 'Multiple Choices', + 301: 'Moved Permanently', + 302: 'Moved Temporarily', + 305: 'Use Proxy', + 380: 'Alternative Service', + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 410: 'Gone', + 412: 'Conditional Request Failed', // RFC 3903 + 413: 'Request Entity Too Large', + 414: 'Request-URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Unsupported URI Scheme', + 417: 'Unknown Resource-Priority', // RFC 4412 + 420: 'Bad Extension', + 421: 'Extension Required', + 422: 'Session Interval Too Small', // RFC 4028 + 423: 'Interval Too Brief', + 424: 'Bad Location Information', // RFC 6442 + 428: 'Use Identity Header', // RFC 4474 + 429: 'Provide Referrer Identity', // RFC 3892 + 430: 'Flow Failed', // RFC 5626 + 433: 'Anonymity Disallowed', // RFC 5079 + 436: 'Bad Identity-Info', // RFC 4474 + 437: 'Unsupported Certificate', // RFC 4744 + 438: 'Invalid Identity Header', // RFC 4744 + 439: 'First Hop Lacks Outbound Support', // RFC 5626 + 440: 'Max-Breadth Exceeded', // RFC 5393 + 469: 'Bad Info Package', // draft-ietf-sipcore-info-events + 470: 'Consent Needed', // RFC 5360 + 478: 'Unresolvable Destination', // Custom code copied from Kamailio. + 480: 'Temporarily Unavailable', + 481: 'Call/Transaction Does Not Exist', + 482: 'Loop Detected', + 483: 'Too Many Hops', + 484: 'Address Incomplete', + 485: 'Ambiguous', + 486: 'Busy Here', + 487: 'Request Terminated', + 488: 'Not Acceptable Here', + 489: 'Bad Event', // RFC 3265 + 491: 'Request Pending', + 493: 'Undecipherable', + 494: 'Security Agreement Required', // RFC 3329 + 500: 'DartSIP Internal Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Server Time-out', + 505: 'Version Not Supported', + 513: 'Message Too Large', + 580: 'Precondition Failure', // RFC 3312 + 600: 'Busy Everywhere', + 603: 'Decline', + 604: 'Does Not Exist Anywhere', + 606: 'Not Acceptable' +}; - var ALLOWED_METHODS = 'INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO'; - var ACCEPTED_BODY_TYPES = 'application/sdp, application/dtmf-relay'; - var MAX_FORWARDS = 69; - var SESSION_EXPIRES = 90; - var MIN_SESSION_EXPIRES = 60; +var ALLOWED_METHODS = 'INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO'; +var ACCEPTED_BODY_TYPES = 'application/sdp, application/dtmf-relay'; +var MAX_FORWARDS = 69; +var SESSION_EXPIRES = 90; +var MIN_SESSION_EXPIRES = 60; diff --git a/lib/src/Dialog.dart b/lib/src/Dialog.dart index 55e206cc..0bbfc91e 100644 --- a/lib/src/Dialog.dart +++ b/lib/src/Dialog.dart @@ -1,3 +1,6 @@ +import '../sip_ua.dart'; +import 'Constants.dart'; +import 'RTCSession.dart'; import 'SIPMessage.dart' as SIPMessage; import 'Constants.dart' as DartSIP_C; import 'Transactions.dart' as Transactions; @@ -30,8 +33,8 @@ class Id { // RFC 3261 12.1. class Dialog { - var _owner; - var _ua; + RTCSession _owner; + UA _ua; var _uac_pending_reply; var _uas_pending_reply; var _state; @@ -113,7 +116,7 @@ class Dialog { this._local_seqnum = num; } - get owner => this._owner; + RTCSession get owner => this._owner; get uac_pending_reply => this._uac_pending_reply; @@ -139,12 +142,12 @@ class Dialog { this._ua.destroyDialog(this); } - sendRequest(method, options) { + SIPMessage.OutgoingRequest sendRequest(SipMethod method, options) { options = options ?? {}; var extraHeaders = Utils.cloneArray(options['extraHeaders']); var eventHandlers = options['eventHandlers'] ?? {}; var body = options['body'] ?? null; - var request = this._createRequest(method, extraHeaders, body); + SIPMessage.OutgoingRequest request = this._createRequest(method, extraHeaders, body); // Increase the local CSeq on authentication. eventHandlers['onAuthenticated'] = (request) { @@ -166,11 +169,11 @@ class Dialog { } // ACK received. Cleanup this._ack_seqnum. - if (request.method == DartSIP_C.ACK && this._ack_seqnum != null) { + if (request.method == SipMethod.ACK && this._ack_seqnum != null) { this._ack_seqnum = null; } // INVITE received. Set this._ack_seqnum. - else if (request.method == DartSIP_C.INVITE) { + else if (request.method == SipMethod.INVITE) { this._ack_seqnum = request.cseq; } @@ -178,14 +181,14 @@ class Dialog { } // RFC 3261 12.2.1.1. - _createRequest(method, extraHeaders, body) { + SIPMessage.OutgoingRequest _createRequest(SipMethod method, extraHeaders, body) { extraHeaders = Utils.cloneArray(extraHeaders); if (this._local_seqnum == null) { this._local_seqnum = Utils.Math.floor(Utils.Math.randomDouble() * 10000); } - var cseq = (method == DartSIP_C.CANCEL || method == DartSIP_C.ACK) + var cseq = (method == SipMethod.CANCEL || method == SipMethod.ACK) ? this._local_seqnum : this._local_seqnum += 1; @@ -209,11 +212,11 @@ class Dialog { } // RFC 3261 12.2.2. - _checkInDialogRequest(request) { + _checkInDialogRequest(SIPMessage.IncomingRequest request) { if (this._remote_seqnum == null) { this._remote_seqnum = request.cseq; } else if (request.cseq < this._remote_seqnum) { - if (request.method == DartSIP_C.ACK) { + if (request.method == SipMethod.ACK) { // We are not expecting any ACK with lower seqnum than the current one. // Or this is not the ACK we are waiting for. if (this._ack_seqnum == null || request.cseq != this._ack_seqnum) { @@ -229,8 +232,8 @@ class Dialog { } // RFC3261 14.2 Modifying an Existing Session -UAS BEHAVIOR-. - if (request.method == DartSIP_C.INVITE || - (request.method == DartSIP_C.UPDATE && request.body != null)) { + if (request.method == SipMethod.INVITE || + (request.method == SipMethod.UPDATE && request.body != null)) { if (this._uac_pending_reply == true) { request.reply(491); } else if (this._uas_pending_reply == true) { @@ -261,7 +264,7 @@ class Dialog { } }); } - } else if (request.method == DartSIP_C.NOTIFY) { + } else if (request.method == SipMethod.NOTIFY) { // RFC6665 3.2 Replace the dialog's remote target URI if the request is accepted. if (request.hasHeader('contact')) { request.server_transaction.on('stateChanged', () { diff --git a/lib/src/Dialog/RequestSender.dart b/lib/src/Dialog/RequestSender.dart index 303710d8..1aa477b3 100644 --- a/lib/src/Dialog/RequestSender.dart +++ b/lib/src/Dialog/RequestSender.dart @@ -1,4 +1,8 @@ +import '../../sip_ua.dart'; import '../Constants.dart' as DartSIP_C; +import '../Constants.dart'; +import '../Dialog.dart'; +import '../SIPMessage.dart'; import '../Transactions.dart' as Transactions; import '../RTCSession.dart' as RTCSession; import '../RequestSender.dart'; @@ -15,15 +19,15 @@ var EventHandlers = { }; class DialogRequestSender { - var _dialog; - var _ua; - var _request; + Dialog _dialog; + UA _ua; + OutgoingRequest _request; var _eventHandlers; var _reattempt; var _reattemptTimer; var _request_sender; - DialogRequestSender(dialog, request, eventHandlers) { + DialogRequestSender(Dialog dialog, OutgoingRequest request, eventHandlers) { this._dialog = dialog; this._ua = dialog.ua; this._request = request; @@ -43,7 +47,7 @@ class DialogRequestSender { }); } - get request => this._request; + OutgoingRequest get request => this._request; send() { var request_sender = new RequestSender(this._ua, this._request, { @@ -57,8 +61,8 @@ class DialogRequestSender { request_sender.send(); // RFC3261 14.2 Modifying an Existing Session -UAC BEHAVIOR-. - if ((this._request.method == DartSIP_C.INVITE || - (this._request.method == DartSIP_C.UPDATE && this._request.body != null)) && + if ((this._request.method == SipMethod.INVITE || + (this._request.method == SipMethod.UPDATE && this._request.body != null)) && request_sender.clientTransaction.state != Transactions.C.STATUS_TERMINATED) { this._dialog.uac_pending_reply = true; @@ -84,7 +88,7 @@ class DialogRequestSender { // RFC3261 12.2.1.2 408 or 481 is received for a request within a dialog. if (response.status_code == 408 || response.status_code == 481) { this._eventHandlers['onDialogError'](response); - } else if (response.method == DartSIP_C.INVITE && + } else if (response.method == SipMethod.INVITE && response.status_code == 491) { if (this._reattempt != null) { if (response.status_code >= 200 && response.status_code < 300) { diff --git a/lib/src/DigestAuthentication.dart b/lib/src/DigestAuthentication.dart index e593bbc2..09e2595b 100644 --- a/lib/src/DigestAuthentication.dart +++ b/lib/src/DigestAuthentication.dart @@ -1,3 +1,4 @@ +import 'Constants.dart'; import 'Utils.dart' as Utils; import 'logger.dart'; @@ -38,7 +39,7 @@ class DigestAuthentication { var _opaque; var _stale; var _qop; - var _method; + SipMethod _method; var _uri; var _ha1; var _response; @@ -71,7 +72,7 @@ class DigestAuthentication { * received in a response to that request. * Returns true if auth was successfully generated, false otherwise. */ - authenticate(method, Challenge challenge, [ruri, cnonce, body]) { + authenticate(SipMethod method, Challenge challenge, [ruri, cnonce, body]) { this._algorithm = challenge.algorithm; this._realm = challenge.realm; this._nonce = challenge.nonce; @@ -173,7 +174,7 @@ class DigestAuthentication { if (this._qop == 'auth') { // HA2 = MD5(A2) = MD5(method:digestURI). - a2 = '${this._method}:${this._uri}'; + a2 = '${SipMethodHelper.getName(this._method)}:${this._uri}'; ha2 = Utils.calculateMD5(a2); debug('authenticate() | using qop=auth [a2:${a2}]'); @@ -184,7 +185,7 @@ class DigestAuthentication { } else if (this._qop == 'auth-int') { // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody)). a2 = - '${this._method}:${this._uri}:${Utils.calculateMD5(body != null ? body : '')}'; + '${SipMethodHelper.getName(this._method)}:${this._uri}:${Utils.calculateMD5(body != null ? body : '')}'; ha2 = Utils.calculateMD5(a2); debug('authenticate() | using qop=auth-int [a2:${a2}]'); @@ -194,7 +195,7 @@ class DigestAuthentication { '${this._ha1}:${this._nonce}:${this._ncHex}:${this._cnonce}:auth-int:${ha2}'); } else if (this._qop == null) { // HA2 = MD5(A2) = MD5(method:digestURI). - a2 = '${this._method}:${this._uri}'; + a2 = '${SipMethodHelper.getName(this._method)}:${this._uri}'; ha2 = Utils.calculateMD5(a2); debug('authenticate() | using qop=null [a2:${a2}]'); diff --git a/lib/src/Message.dart b/lib/src/Message.dart index 2dce6ae2..91fc7842 100644 --- a/lib/src/Message.dart +++ b/lib/src/Message.dart @@ -1,5 +1,7 @@ import 'package:events2/events2.dart'; +import '../sip_ua.dart'; import 'Constants.dart' as DartSIP_C; +import 'Constants.dart'; import 'SIPMessage.dart' as SIPMessage; import 'Utils.dart' as Utils; import 'RequestSender.dart'; @@ -7,7 +9,7 @@ import 'Exceptions.dart' as Exceptions; import 'logger.dart'; class Message extends EventEmitter { - var _ua; + UA _ua; var _request; var _closed; var _direction; @@ -19,7 +21,7 @@ class Message extends EventEmitter { debug(msg) => logger.debug(msg); debugerror(error) => logger.error(error); - Message(ua) { + Message(UA ua) { this._ua = ua; this._request = null; this._closed = false; @@ -72,7 +74,7 @@ class Message extends EventEmitter { extraHeaders.add('Content-Type: $contentType'); this._request = new SIPMessage.OutgoingRequest( - DartSIP_C.MESSAGE, target, this._ua, null, extraHeaders); + SipMethod.MESSAGE, target, this._ua, null, extraHeaders); if (body != null) { this._request.body = body; } diff --git a/lib/src/Parser.dart b/lib/src/Parser.dart index 9251cf5d..3eb64df1 100644 --- a/lib/src/Parser.dart +++ b/lib/src/Parser.dart @@ -21,7 +21,14 @@ parseMessage(data, ua) { // Parse first line. Check if it is a Request or a Reply. var firstLine = data.substring(0, headerEnd); - var parsed = Grammar.parse(firstLine, 'Request_Response'); + var parsed; + try{ + parsed = Grammar.parse(firstLine, 'Request_Response'); + }catch (FormatException ) + { + // Catch exception and fake the expected -1 result + parsed = -1; + } if (parsed == -1) { debugerror( diff --git a/lib/src/RTCSession.dart b/lib/src/RTCSession.dart index 6e78f877..4b1642b0 100644 --- a/lib/src/RTCSession.dart +++ b/lib/src/RTCSession.dart @@ -4,6 +4,8 @@ import 'package:events2/events2.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; import 'package:flutter_webrtc/webrtc.dart'; +import '../sip_ua.dart'; +import 'Constants.dart'; import 'RTCSession/DTMF.dart' as RTCSession_DTMF; import 'RTCSession/Info.dart' as RTCSession_Info; import 'RTCSession/ReferNotifier.dart' as RTCSession_ReferNotifier; @@ -11,6 +13,7 @@ import 'RTCSession/ReferSubscriber.dart' as RTCSession_ReferSubscriber; import 'Constants.dart' as DartSIP_C; import 'Exceptions.dart' as Exceptions; +import 'SIPMessage.dart'; import 'Transactions.dart' as Transactions; import 'Utils.dart' as Utils; import 'Timers.dart'; @@ -60,13 +63,13 @@ class RFC4028Timers { class RTCSession extends EventEmitter { var _id; - var _ua; + UA _ua; var _request; bool _late_sdp; var _rtcOfferConstraints; MediaStream _localMediaStream; var _data; - var _earlyDialogs; + Map _earlyDialogs; var _from_tag; var _to_tag; var _rtcAnswerConstraints; @@ -76,7 +79,7 @@ class RTCSession extends EventEmitter { var _sessionTimers; var _cancel_reason; var _status; - var _dialog; + Dialog _dialog; RTCPeerConnection _connection; var _iceGatheringState; bool _localMediaStreamLocallyGenerated; @@ -103,9 +106,9 @@ class RTCSession extends EventEmitter { debug(msg) => logger.debug(msg); debugerror(error) => logger.error(error); - dynamic receiveRequest; + Function(IncomingRequest ) receiveRequest; - RTCSession(ua) { + RTCSession(UA ua) { debug('new'); this._id = null; @@ -764,13 +767,13 @@ class RTCSession extends EventEmitter { this._request.server_transaction.state != Transactions.C.STATUS_TERMINATED) { /// Save the dialog for later restoration. - var dialog = this._dialog; + Dialog dialog = this._dialog; // Send the BYE as soon as the ACK is received... - this.receiveRequest = (method) { - if (method == DartSIP_C.ACK) { + this.receiveRequest = (IncomingMessage request) { + if (request.method == SipMethod.ACK) { this.sendRequest( - DartSIP_C.BYE, {'extraHeaders': extraHeaders, 'body': body}); + SipMethod.BYE, {'extraHeaders': extraHeaders, 'body': body}); dialog.terminate(); } }; @@ -780,7 +783,7 @@ class RTCSession extends EventEmitter { if (this._request.server_transaction.state == Transactions.C.STATUS_TERMINATED) { this.sendRequest( - DartSIP_C.BYE, {'extraHeaders': extraHeaders, 'body': body}); + SipMethod.BYE, {'extraHeaders': extraHeaders, 'body': body}); dialog.terminate(); } }); @@ -794,7 +797,7 @@ class RTCSession extends EventEmitter { this._ua.newDialog(dialog); } else { this.sendRequest( - DartSIP_C.BYE, {'extraHeaders': extraHeaders, 'body': body}); + SipMethod.BYE, {'extraHeaders': extraHeaders, 'body': body}); this._ended('local', null, cause); } @@ -1176,7 +1179,7 @@ class RTCSession extends EventEmitter { /** * Send a generic in-dialog Request */ - sendRequest(method, [options]) { + sendRequest(SipMethod method, [options]) { debug('sendRequest()'); return this._dialog.sendRequest(method, options); @@ -1188,7 +1191,7 @@ class RTCSession extends EventEmitter { _receiveRequest(request) async { debug('receiveRequest()'); - if (request.method == DartSIP_C.CANCEL) { + if (request.method == SipMethod.CANCEL) { /* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL * was in progress and that the UAC MAY continue with the session established by * any 2xx response, or MAY terminate with BYE. DartSIP does continue with the @@ -1209,7 +1212,7 @@ class RTCSession extends EventEmitter { } else { // Requests arriving here are in-dialog requests. switch (request.method) { - case DartSIP_C.ACK: + case SipMethod.ACK: if (this._status != C.STATUS_WAITING_FOR_ACK) { return; } @@ -1250,7 +1253,7 @@ class RTCSession extends EventEmitter { this._confirmed('remote', request); } break; - case DartSIP_C.BYE: + case SipMethod.BYE: if (this._status == C.STATUS_CONFIRMED) { request.reply(200); this._ended('remote', request, DartSIP_C.causes.BYE); @@ -1262,7 +1265,7 @@ class RTCSession extends EventEmitter { request.reply(403, 'Wrong Status'); } break; - case DartSIP_C.INVITE: + case SipMethod.INVITE: if (this._status == C.STATUS_CONFIRMED) { if (request.hasHeader('replaces')) { this._receiveReplaces(request); @@ -1273,7 +1276,7 @@ class RTCSession extends EventEmitter { request.reply(403, 'Wrong Status'); } break; - case DartSIP_C.INFO: + case SipMethod.INFO: if (this._status == C.STATUS_1XX_RECEIVED || this._status == C.STATUS_WAITING_FOR_ANSWER || this._status == C.STATUS_ANSWERED || @@ -1293,21 +1296,21 @@ class RTCSession extends EventEmitter { request.reply(403, 'Wrong Status'); } break; - case DartSIP_C.UPDATE: + case SipMethod.UPDATE: if (this._status == C.STATUS_CONFIRMED) { this._receiveUpdate(request); } else { request.reply(403, 'Wrong Status'); } break; - case DartSIP_C.REFER: + case SipMethod.REFER: if (this._status == C.STATUS_CONFIRMED) { this._receiveRefer(request); } else { request.reply(403, 'Wrong Status'); } break; - case DartSIP_C.NOTIFY: + case SipMethod.NOTIFY: if (this._status == C.STATUS_CONFIRMED) { this._receiveNotify(request); } else { @@ -1383,7 +1386,7 @@ class RTCSession extends EventEmitter { } // No established yet. - if (!this._dialog) { + if (this._dialog==null) { debug('_isReadyToReOffer() | session not established yet'); return false; @@ -1444,7 +1447,7 @@ class RTCSession extends EventEmitter { } // Terminate early dialogs. - this._earlyDialogs.forEach((dialog, _) { + this._earlyDialogs.forEach((dialog, _) { this._earlyDialogs[dialog].terminate(); }); this._earlyDialogs.clear(); @@ -1496,7 +1499,7 @@ class RTCSession extends EventEmitter { debug('no ACK received, terminating the session'); clearTimeout(this._timers.invite2xxTimer); - this.sendRequest(DartSIP_C.BYE); + this.sendRequest(SipMethod.BYE); this._ended('remote', null, DartSIP_C.causes.NO_ACK); } }, Timers.TIMER_H); @@ -1625,7 +1628,7 @@ class RTCSession extends EventEmitter { var local_tag = (type == 'UAS') ? message.to_tag : message.from_tag; var remote_tag = (type == 'UAS') ? message.from_tag : message.to_tag; var id = message.call_id + local_tag + remote_tag; - var early_dialog = this._earlyDialogs[id]; + Dialog early_dialog = this._earlyDialogs[id]; // Early Dialog. if (early != null) { @@ -1934,7 +1937,7 @@ class RTCSession extends EventEmitter { return false; } - var session = new RTCSession(this._ua); + RTCSession session = new RTCSession(this._ua); session.on('progress', ({response}) { notifier.notify(response.status_code, response.reason_phrase); @@ -2039,7 +2042,7 @@ class RTCSession extends EventEmitter { return false; } - var session = new RTCSession(this._ua); + RTCSession session = new RTCSession(this._ua); // Terminate the current session when the new one is confirmed. session.on('confirmed', () { @@ -2140,7 +2143,8 @@ class RTCSession extends EventEmitter { this.emit('sending', {'request': this._request}); request_sender.send(); - } catch (error) { + } catch (error,s) { + print("$error $s"); this._failed('local', null, DartSIP_C.causes.WEBRTC_ERROR); if (this._status == C.STATUS_TERMINATED) { return; @@ -2164,18 +2168,18 @@ class RTCSession extends EventEmitter { if (this._dialog.id.call_id == response.call_id && this._dialog.id.local_tag == response.from_tag && this._dialog.id.remote_tag == response.to_tag) { - this.sendRequest(DartSIP_C.ACK); + this.sendRequest(SipMethod.ACK); return; } else { // If not, send an ACK and terminate. try { - var dialog = new Dialog(this, response, 'UAC'); + Dialog dialog = new Dialog(this, response, 'UAC'); } catch (error) { debug(error); return; } - this.sendRequest(DartSIP_C.ACK); - this.sendRequest(DartSIP_C.BYE); + this.sendRequest(SipMethod.ACK); + this.sendRequest(SipMethod.BYE); return; } } @@ -2262,7 +2266,7 @@ class RTCSession extends EventEmitter { // Be ready for 200 with SDP after a 180/183 with SDP. // We created a SDP 'answer' for it, so check the current signaling state. - //if (this._connection.signalingState == 'stable') ///TODO: ??? + //if (this._connection.signalingState == RTCSignalingState.RTCSignalingStateStable) /*{ try { var offer = @@ -2275,11 +2279,14 @@ class RTCSession extends EventEmitter { }*/ try { + //TODO: Commented out because this seems to cause errors with Asterisk + await this._connection.setRemoteDescription(answer); + // Handle Session Timers. this._handleSessionTimersInIncomingResponse(response); this._accepted('remote', response); - this.sendRequest(DartSIP_C.ACK); + this.sendRequest(SipMethod.ACK); this._confirmed('local', null); } catch (error) { this._acceptAndTerminate(response, 488, 'Not Acceptable Here'); @@ -2328,7 +2335,7 @@ class RTCSession extends EventEmitter { return; } - this.sendRequest(DartSIP_C.ACK); + this.sendRequest(SipMethod.ACK); // If it is a 2XX retransmission exit now. if (succeeded != null) { @@ -2375,7 +2382,7 @@ class RTCSession extends EventEmitter { debug('emit "sdp"'); this.emit('sdp', e); - this.sendRequest(DartSIP_C.INVITE, { + this.sendRequest(SipMethod.INVITE, { 'extraHeaders': extraHeaders, 'body': sdp, 'eventHandlers': { @@ -2496,7 +2503,7 @@ class RTCSession extends EventEmitter { debug('emit "sdp"'); this.emit('sdp', e); - this.sendRequest(DartSIP_C.UPDATE, { + this.sendRequest(SipMethod.UPDATE, { 'extraHeaders': extraHeaders, 'body': sdp, 'eventHandlers': { @@ -2523,7 +2530,7 @@ class RTCSession extends EventEmitter { } } else { // No SDP. - this.sendRequest(DartSIP_C.UPDATE, { + this.sendRequest(SipMethod.UPDATE, { 'extraHeaders': extraHeaders, 'eventHandlers': { 'onSuccessResponse': (response) { @@ -2560,8 +2567,8 @@ class RTCSession extends EventEmitter { // An error on dialog creation will fire 'failed' event. if (this._dialog != null || this._createDialog(response, 'UAC')) { - this.sendRequest(DartSIP_C.ACK); - this.sendRequest(DartSIP_C.BYE, {'extraHeaders': extraHeaders}); + this.sendRequest(SipMethod.ACK); + this.sendRequest(SipMethod.BYE, {'extraHeaders': extraHeaders}); } // Update session status. @@ -2712,7 +2719,7 @@ class RTCSession extends EventEmitter { debug('runSessionTimer() | sending session refresh request'); - if (this._sessionTimers.refreshMethod == DartSIP_C.UPDATE) { + if (this._sessionTimers.refreshMethod == SipMethod.UPDATE) { this._sendUpdate(); } else { this._sendReinvite(); diff --git a/lib/src/RTCSession/DTMF.dart b/lib/src/RTCSession/DTMF.dart index 8d1263f6..b38df159 100644 --- a/lib/src/RTCSession/DTMF.dart +++ b/lib/src/RTCSession/DTMF.dart @@ -1,5 +1,6 @@ import 'package:events2/events2.dart'; import '../Constants.dart' as DartSIP_C; +import '../Constants.dart'; import '../Exceptions.dart' as Exceptions; import '../RTCSession.dart' as RTCSession; import '../Utils.dart' as Utils; @@ -83,7 +84,7 @@ class DTMF extends EventEmitter { this._session.newDTMF( {'originator': 'local', 'dtmf': this, 'request': this._request}); - this._session.sendRequest(DartSIP_C.INFO, { + this._session.sendRequest(SipMethod.INFO, { 'extraHeaders': extraHeaders, 'eventHandlers': { 'onSuccessResponse': (response) { diff --git a/lib/src/RTCSession/Info.dart b/lib/src/RTCSession/Info.dart index 71b5aecb..484d720b 100644 --- a/lib/src/RTCSession/Info.dart +++ b/lib/src/RTCSession/Info.dart @@ -1,5 +1,6 @@ import 'package:events2/events2.dart'; import '../Constants.dart' as DartSIP_C; +import '../Constants.dart'; import '../Exceptions.dart' as Exceptions; import '../RTCSession.dart' as RTCSession; import '../Utils.dart' as Utils; @@ -51,7 +52,7 @@ class Info extends EventEmitter { this._session.newInfo( {'originator': 'local', 'info': this, 'request': this.request}); - this._session.sendRequest(DartSIP_C.INFO, { + this._session.sendRequest(SipMethod.INFO, { 'extraHeaders': extraHeaders, 'eventHandlers': { 'onSuccessResponse': (response) { diff --git a/lib/src/RTCSession/ReferNotifier.dart b/lib/src/RTCSession/ReferNotifier.dart index 8a08fa93..cff0d0d4 100644 --- a/lib/src/RTCSession/ReferNotifier.dart +++ b/lib/src/RTCSession/ReferNotifier.dart @@ -1,4 +1,5 @@ import '../Constants.dart' as DartSIP_C; +import '../Constants.dart'; import '../logger.dart'; class C { @@ -44,7 +45,7 @@ class ReferNotifier { } // Put this in a try/catch block. - this._session.sendRequest(DartSIP_C.NOTIFY, { + this._session.sendRequest(SipMethod.NOTIFY, { 'extraHeaders': [ 'Event: ${C.event_type};id=${this._id}', 'Subscription-State: ${state}', diff --git a/lib/src/RTCSession/ReferSubscriber.dart b/lib/src/RTCSession/ReferSubscriber.dart index 5cce99ef..ec541c00 100644 --- a/lib/src/RTCSession/ReferSubscriber.dart +++ b/lib/src/RTCSession/ReferSubscriber.dart @@ -1,5 +1,6 @@ import 'package:events2/events2.dart'; import '../Constants.dart' as DartSIP_C; +import '../Constants.dart'; import '../Grammar.dart'; import '../Utils.dart' as Utils; import '../logger.dart'; @@ -52,7 +53,7 @@ class ReferSubscriber extends EventEmitter { extraHeaders.add('Contact: ${this._session.contact}'); - var request = this._session.sendRequest(DartSIP_C.REFER, { + var request = this._session.sendRequest(SipMethod.REFER, { 'extraHeaders': extraHeaders, 'eventHandlers': { 'onSuccessResponse': (response) { diff --git a/lib/src/Registrator.dart b/lib/src/Registrator.dart index 7487991c..102294cb 100644 --- a/lib/src/Registrator.dart +++ b/lib/src/Registrator.dart @@ -1,3 +1,4 @@ +import 'Constants.dart'; import 'Utils.dart' as Utils; import 'Timers.dart'; import 'Constants.dart' as DartSIP_C; @@ -109,7 +110,7 @@ class Registrator { print(this._contact); var request = new SIPMessage.OutgoingRequest( - DartSIP_C.REGISTER, + SipMethod.REGISTER, this._registrar, this._ua, { @@ -268,7 +269,7 @@ class Registrator { extraHeaders.add('Expires: 0'); var request = new SIPMessage.OutgoingRequest( - DartSIP_C.REGISTER, + SipMethod.REGISTER, this._registrar, this._ua, { diff --git a/lib/src/RequestSender.dart b/lib/src/RequestSender.dart index 1df260a2..bda61f57 100644 --- a/lib/src/RequestSender.dart +++ b/lib/src/RequestSender.dart @@ -1,7 +1,11 @@ + + +import '../sip_ua.dart'; import 'Constants.dart' as DartSIP_C; +import 'Constants.dart'; import 'DigestAuthentication.dart'; import 'Transactions.dart' as Transactions; -import 'UA.dart' as UA; +import 'UA.dart' as UAC; import 'logger.dart'; // Default event handlers. @@ -13,9 +17,9 @@ var EventHandlers = { }; class RequestSender { - var _ua; + UA _ua; var _eventHandlers; - var _method; + SipMethod _method; var _request; var _auth; var _challenged; @@ -25,7 +29,7 @@ class RequestSender { debug(msg) => logger.debug(msg); debugerror(error) => logger.error(error); - RequestSender(ua, request, eventHandlers) { + RequestSender(UA ua, request, eventHandlers) { this._ua = ua; this._eventHandlers = eventHandlers; this._method = request.method; @@ -42,8 +46,8 @@ class RequestSender { }); // If ua is in closing process or even closed just allow sending Bye and ACK. - if (ua.status == UA.C.STATUS_USER_CLOSED && - (this._method != DartSIP_C.BYE || this._method != DartSIP_C.ACK)) { + if (ua.status == UAC.C.STATUS_USER_CLOSED && + (this._method != SipMethod.BYE || this._method != SipMethod.ACK)) { this._eventHandlers['onTransportError'](); } } @@ -61,11 +65,11 @@ class RequestSender { }; switch (this._method) { - case 'INVITE': + case SipMethod.INVITE: this.clientTransaction = new Transactions.InviteClientTransaction( this._ua, this._ua.transport, this._request, eventHandlers); break; - case 'ACK': + case SipMethod.ACK: this.clientTransaction = new Transactions.AckClientTransaction( this._ua, this._ua.transport, this._request, eventHandlers); break; @@ -149,7 +153,7 @@ class RequestSender { this._request.cseq += 1; this ._request - .setHeader('cseq', '${this._request.cseq} ${this._method}'); + .setHeader('cseq', '${this._request.cseq} ${SipMethodHelper.getName(this._method)}'); this ._request .setHeader(authorization_header_name, this._auth.toString()); diff --git a/lib/src/SIPMessage.dart b/lib/src/SIPMessage.dart index 8e60ec2a..c5e68516 100644 --- a/lib/src/SIPMessage.dart +++ b/lib/src/SIPMessage.dart @@ -1,4 +1,6 @@ import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; +import '../sip_ua.dart'; +import 'Constants.dart'; import 'NameAddrHeader.dart'; import 'Exceptions.dart' as Exceptions; import 'Grammar.dart'; @@ -20,9 +22,9 @@ debug(msg) => logger.debug(msg); * -param {String} [body] */ class OutgoingRequest { - var ua; + UA ua; var headers; - var method; + SipMethod method; var ruri; var body; var extraHeaders = []; @@ -32,7 +34,7 @@ class OutgoingRequest { var cseq; var sdp; - OutgoingRequest(method, ruri, ua, [params, extraHeaders, body]) { + OutgoingRequest(SipMethod method, ruri, UA ua, [params, extraHeaders, body]) { // Mandatory parameters check. if (method == null || ruri == null || ua == null) { throw new Exceptions.TypeError( @@ -99,7 +101,7 @@ class OutgoingRequest { var cseq = params['cseq'] ?? Utils.Math.floor(Utils.Math.randomDouble() * 10000); this.cseq = cseq; - this.setHeader('cseq', '${cseq} ${method}'); + this.setHeader('cseq', '${cseq} ${SipMethodHelper.getName(method)}'); } /** @@ -210,7 +212,7 @@ class OutgoingRequest { } toString() { - var msg = '${this.method} ${this.ruri} SIP/2.0\r\n'; + var msg = '${SipMethodHelper.getName(this.method)} ${this.ruri} SIP/2.0\r\n'; this.headers.forEach((headerName, headerValues) { headerValues.forEach((value){ @@ -226,11 +228,11 @@ class OutgoingRequest { var supported = []; switch (this.method) { - case DartSIP_C.REGISTER: + case SipMethod.REGISTER: supported.add('path'); supported.add('gruu'); break; - case DartSIP_C.INVITE: + case SipMethod.INVITE: if (this.ua.configuration.session_timers) { supported.add('timer'); } @@ -240,12 +242,15 @@ class OutgoingRequest { supported.add('ice'); supported.add('replaces'); break; - case DartSIP_C.UPDATE: + case SipMethod.UPDATE: if (this.ua.configuration.session_timers) { supported.add('timer'); } supported.add('ice'); break; + default: + break; + } supported.add('outbound'); @@ -289,7 +294,7 @@ class OutgoingRequest { class InitialOutgoingInviteRequest extends OutgoingRequest { var transaction; InitialOutgoingInviteRequest(ruri, ua, [params, extraHeaders, body]) - : super(DartSIP_C.INVITE, ruri, ua, params, extraHeaders, body) { + : super(SipMethod.INVITE, ruri, ua, params, extraHeaders, body) { this.transaction = null; } @@ -301,7 +306,7 @@ class InitialOutgoingInviteRequest extends OutgoingRequest { var request = new InitialOutgoingInviteRequest(this.ruri, this.ua); this.headers.forEach((name, value) { - request.headers[name] = this.headers[name].slice(); + request.headers[name] = new List.from(this.headers[name]); }); request.body = this.body; @@ -320,7 +325,7 @@ class InitialOutgoingInviteRequest extends OutgoingRequest { class IncomingMessage { var data; var headers; - var method; + SipMethod method; var via; var via_branch; var call_id; @@ -493,12 +498,12 @@ class IncomingMessage { } class IncomingRequest extends IncomingMessage { - var ua; + UA ua; var ruri; var transport; var server_transaction; - IncomingRequest(ua) : super() { + IncomingRequest(UA ua) : super() { this.ua = ua; this.headers = {}; this.ruri = null; @@ -534,7 +539,7 @@ class IncomingRequest extends IncomingMessage { var response = 'SIP/2.0 ${code} ${reason}\r\n'; - if (this.method == DartSIP_C.INVITE && code > 100 && code <= 200) { + if (this.method == SipMethod.INVITE && code > 100 && code <= 200) { var headers = this.getHeaders('record-route'); for (var header in headers) { @@ -557,7 +562,7 @@ class IncomingRequest extends IncomingMessage { response += 'To: ${to}\r\n'; response += 'From: ${this.getHeader('From')}\r\n'; response += 'Call-ID: ${this.call_id}\r\n'; - response += 'CSeq: ${this.cseq} ${this.method}\r\n'; + response += 'CSeq: ${this.cseq} ${SipMethodHelper.getName(this.method)}\r\n'; for (var header in extraHeaders) { response += '${header.trim()}\r\n'; @@ -565,7 +570,7 @@ class IncomingRequest extends IncomingMessage { // Supported. switch (this.method) { - case DartSIP_C.INVITE: + case SipMethod.INVITE: if (this.ua.configuration.session_timers) { supported.add('timer'); } @@ -575,7 +580,7 @@ class IncomingRequest extends IncomingMessage { supported.add('ice'); supported.add('replaces'); break; - case DartSIP_C.UPDATE: + case SipMethod.UPDATE: if (this.ua.configuration.session_timers) { supported.add('timer'); } @@ -583,12 +588,15 @@ class IncomingRequest extends IncomingMessage { supported.add('ice'); } supported.add('replaces'); + break; + default: + break; } supported.add('outbound'); // Allow and Accept. - if (this.method == DartSIP_C.OPTIONS) { + if (this.method == SipMethod.OPTIONS) { response += 'Allow: ${DartSIP_C.ALLOWED_METHODS}\r\n'; response += 'Accept: ${DartSIP_C.ACCEPTED_BODY_TYPES}\r\n'; } else if (code == 405) { @@ -648,7 +656,7 @@ class IncomingRequest extends IncomingMessage { response += 'To: ${to}\r\n'; response += 'From: ${this.getHeader('From')}\r\n'; response += 'Call-ID: ${this.call_id}\r\n'; - response += 'CSeq: ${this.cseq} ${this.method}\r\n'; + response += 'CSeq: ${this.cseq} ${SipMethodHelper.getName(this.method)}\r\n'; response += 'Content-Length: ${0}\r\n\r\n'; this.transport.send(response); diff --git a/lib/src/Transactions.dart b/lib/src/Transactions.dart index 59ccdd60..4fd75929 100644 --- a/lib/src/Transactions.dart +++ b/lib/src/Transactions.dart @@ -1,7 +1,10 @@ import 'package:events2/events2.dart'; +import '../sip_ua.dart'; import 'Constants.dart' as DartSIP_C; +import 'Constants.dart'; import 'SIPMessage.dart' as SIPMessage; import 'Timers.dart'; +import 'Transport.dart'; import 'Utils.dart'; import 'logger.dart'; @@ -36,14 +39,14 @@ class C { class NonInviteClientTransaction extends EventEmitter { var type; var id; - var ua; - var transport; + UA ua; + Transport transport; var request; var eventHandlers; var F, K; var state; - NonInviteClientTransaction(ua, transport, request, eventHandlers) { + NonInviteClientTransaction(UA ua, Transport transport, request, eventHandlers) { this.type = C.NON_INVITE_CLIENT; this.id = 'z9hG4bK${Math.floor(Math.random())}'; this.ua = ua; @@ -97,7 +100,7 @@ class NonInviteClientTransaction extends EventEmitter { this.ua.destroyTransaction(this); } - receiveResponse(response) { + receiveResponse(SIPMessage.IncomingResponse response) { var status_code = response.status_code; if (status_code < 200) { @@ -135,14 +138,14 @@ class NonInviteClientTransaction extends EventEmitter { class InviteClientTransaction extends EventEmitter { var type; var id; - var ua; - var transport; + UA ua; + Transport transport; var request; var eventHandlers; var state; var B, D, M; - InviteClientTransaction(ua, transport, request, eventHandlers) { + InviteClientTransaction(UA ua, Transport transport, request, eventHandlers) { this.type = C.INVITE_CLIENT; this.id = 'z9hG4bK${Math.floor(Math.random() * 10000000)}'; this.ua = ua; @@ -220,7 +223,7 @@ class InviteClientTransaction extends EventEmitter { sendACK(response) { var ack = new SIPMessage.OutgoingRequest( - DartSIP_C.ACK, this.request.ruri, this.ua, { + SipMethod.ACK, this.request.ruri, this.ua, { 'route_set': this.request.getHeaders('route'), 'call_id': this.request.getHeader('call-id'), 'cseq': this.request.cseq @@ -244,7 +247,7 @@ class InviteClientTransaction extends EventEmitter { } var cancel = new SIPMessage.OutgoingRequest( - DartSIP_C.CANCEL, this.request.ruri, this.ua, { + SipMethod.CANCEL, this.request.ruri, this.ua, { 'route_set': this.request.getHeaders('route'), 'call_id': this.request.getHeader('call-id'), 'cseq': this.request.cseq @@ -261,7 +264,7 @@ class InviteClientTransaction extends EventEmitter { this.transport.send(cancel); } - receiveResponse(response) { + receiveResponse(SIPMessage.IncomingResponse response) { var status_code = response.status_code; if (status_code >= 100 && status_code <= 199) { @@ -306,11 +309,11 @@ class InviteClientTransaction extends EventEmitter { class AckClientTransaction extends EventEmitter { var id; - var transport; + Transport transport; var request; var eventHandlers; - AckClientTransaction(ua, transport, request, eventHandlers) { + AckClientTransaction(UA ua, Transport transport, request, eventHandlers) { this.id = 'z9hG4bK${Math.floor(Math.random() * 10000000)}'; this.transport = transport; this.request = request; @@ -338,15 +341,15 @@ class AckClientTransaction extends EventEmitter { class NonInviteServerTransaction extends EventEmitter { var type; var id; - var ua; - var transport; + UA ua; + Transport transport; var request; var last_response; var state; var transportError; var J; - NonInviteServerTransaction(ua, transport, request) { + NonInviteServerTransaction(UA ua, Transport transport, request) { this.type = C.NON_INVITE_SERVER; this.id = request.via_branch; this.ua = ua; @@ -437,8 +440,8 @@ class NonInviteServerTransaction extends EventEmitter { class InviteServerTransaction extends EventEmitter { var type; var id; - var ua; - var transport; + UA ua; + Transport transport; var request; var last_response; var state; @@ -446,7 +449,7 @@ class InviteServerTransaction extends EventEmitter { var transportError; var L, H, I; - InviteServerTransaction(ua, transport, request) { + InviteServerTransaction(UA ua, Transport transport, request) { this.type = C.INVITE_SERVER; this.id = request.via_branch; this.ua = ua; @@ -616,7 +619,7 @@ class InviteServerTransaction extends EventEmitter { checkTransaction(_transactions, request) { var tr; switch (request.method) { - case DartSIP_C.INVITE: + case SipMethod.INVITE: tr = _transactions['ist'][request.via_branch]; if (tr != null) { switch (tr.state) { @@ -633,7 +636,7 @@ checkTransaction(_transactions, request) { return true; } break; - case DartSIP_C.ACK: + case SipMethod.ACK: tr = _transactions['ist'][request.via_branch]; // RFC 6026 7.1. @@ -654,7 +657,7 @@ checkTransaction(_transactions, request) { return false; } break; - case DartSIP_C.CANCEL: + case SipMethod.CANCEL: tr = _transactions['ist'][request.via_branch]; if (tr != null) { request.reply_sl(200); diff --git a/lib/src/Transport.dart b/lib/src/Transport.dart index d0c7509d..4f30d7d0 100644 --- a/lib/src/Transport.dart +++ b/lib/src/Transport.dart @@ -159,7 +159,7 @@ class Transport { debug('send()'); if (!this.isConnected()) { - debugerror('unable to send message, transport is not connected'); + debugerror('unable to send message, transport is not connected. Current state is ${this.status}'); return false; } diff --git a/lib/src/UA.dart b/lib/src/UA.dart index b03f9e3e..bfce6eb2 100644 --- a/lib/src/UA.dart +++ b/lib/src/UA.dart @@ -1,7 +1,10 @@ import 'package:events2/events2.dart'; import 'Config.dart' as config; +import 'Config.dart'; import 'Constants.dart' as DartSIP_C; +import 'Constants.dart'; +import 'Dialog.dart'; import 'Exceptions.dart' as Exceptions; import 'Registrator.dart'; import 'RTCSession.dart'; @@ -73,12 +76,12 @@ class Contact { */ class UA extends EventEmitter { var _cache; - var _configuration; + Settings _configuration; var _dynConfiguration; - var _dialogs; + Map _dialogs; var _applicants; - var _sessions = {}; - var _transport; + Map _sessions = {}; + Transport _transport; var _contact; var _status; var _error; @@ -90,7 +93,7 @@ class UA extends EventEmitter { debug(msg) => logger.debug(msg); debugerror(error) => logger.error(error); - UA(configuration) { + UA(Settings configuration) { debug('new() [configuration:${configuration.toString()}]'); this._cache = {'credentials': {}}; @@ -136,9 +139,9 @@ class UA extends EventEmitter { get contact => this._contact; - get configuration => this._configuration; + Settings get configuration => this._configuration; - get transport => this._transport; + Transport get transport => this._transport; get transactions => this._transactions; @@ -414,7 +417,7 @@ class UA extends EventEmitter { /** * new Dialog */ - newDialog(dialog) { + newDialog(Dialog dialog) { this._dialogs[dialog.id.toString()] = dialog; } @@ -484,13 +487,13 @@ class UA extends EventEmitter { * Request reception */ receiveRequest(request) { - var method = request.method; + SipMethod method = request.method; // Check that request URI points to us. if (request.ruri.user != this._configuration.uri.user && request.ruri.user != this._contact.uri.user) { debug('Request-URI does not point to us'); - if (request.method != DartSIP_C.ACK) { + if (request.method != SipMethod.ACK) { request.reply_sl(404); } @@ -510,11 +513,11 @@ class UA extends EventEmitter { } // Create the server transaction. - if (method == DartSIP_C.INVITE) { + if (method == SipMethod.INVITE) { /* eslint-disable no-new */ new Transactions.InviteServerTransaction(this, this._transport, request); /* eslint-enable no-new */ - } else if (method != DartSIP_C.ACK && method != DartSIP_C.CANCEL) { + } else if (method != SipMethod.ACK && method != SipMethod.CANCEL) { /* eslint-disable no-new */ new Transactions.NonInviteServerTransaction( this, this._transport, request); @@ -526,16 +529,16 @@ class UA extends EventEmitter { * received within a dialog (for example, an OPTIONS request). * They are processed as if they had been received outside the dialog. */ - if (method == DartSIP_C.OPTIONS) { + if (method == SipMethod.OPTIONS) { request.reply(200); - } else if (method == DartSIP_C.MESSAGE) { + } else if (method == SipMethod.MESSAGE) { if (this.listeners('newMessage') == null) { request.reply(405); return; } var message = new Message(this); message.init_incoming(request); - } else if (method == DartSIP_C.INVITE) { + } else if (method == SipMethod.INVITE) { // Initial INVITE. if (request.to_tag != null && this.listeners('newRTCSession').length == 0) { @@ -545,13 +548,13 @@ class UA extends EventEmitter { } } - var dialog; - var session; + Dialog dialog; + RTCSession session; // Initial Request. if (request.to_tag == null) { switch (method) { - case DartSIP_C.INVITE: + case SipMethod.INVITE: if (window.hasRTCPeerConnection) { if (request.hasHeader('replaces')) { var replaces = request.replaces; @@ -577,11 +580,11 @@ class UA extends EventEmitter { request.reply(488); } break; - case DartSIP_C.BYE: + case SipMethod.BYE: // Out of dialog BYE received. request.reply(481); break; - case DartSIP_C.CANCEL: + case SipMethod.CANCEL: session = this ._findSession(request.call_id, request.from_tag, request.to_tag); if (session != null) { @@ -590,13 +593,13 @@ class UA extends EventEmitter { debug('received CANCEL request for a non existent session'); } break; - case DartSIP_C.ACK: + case SipMethod.ACK: /* Absorb it. * ACK request without a corresponding Invite Transaction * and without To tag. */ break; - case DartSIP_C.NOTIFY: + case SipMethod.NOTIFY: // Receive new sip event. this.emit('sipEvent', {'event': request.event, 'request': request}); request.reply(200); @@ -613,7 +616,7 @@ class UA extends EventEmitter { if (dialog != null) { dialog.receiveRequest(request); - } else if (method == DartSIP_C.NOTIFY) { + } else if (method == SipMethod.NOTIFY) { session = this ._findSession(request.call_id, request.from_tag, request.to_tag); if (session != null) { @@ -629,7 +632,7 @@ class UA extends EventEmitter { * Exception: ACK for an Invite request for which a dialog has not * been created. */ - else if (method != DartSIP_C.ACK) { + else if (method != SipMethod.ACK) { request.reply(481); } } @@ -642,7 +645,7 @@ class UA extends EventEmitter { /** * Get the session to which the request belongs to, if any. */ - _findSession(call_id, from_tag, to_tag) { + RTCSession _findSession(String call_id, String from_tag, String to_tag) { var sessionIDa = call_id + (from_tag ?? ''); var sessionA = this._sessions[sessionIDa]; var sessionIDb = call_id + (to_tag ?? ''); @@ -660,9 +663,9 @@ class UA extends EventEmitter { /** * Get the dialog to which the request belongs to, if any. */ - _findDialog(call_id, from_tag, to_tag) { + Dialog _findDialog(String call_id, String from_tag, String to_tag) { var id = call_id + from_tag + to_tag; - var dialog = this._dialogs[id]; + Dialog dialog = this._dialogs[id]; if (dialog != null) { return dialog; @@ -892,15 +895,15 @@ class UA extends EventEmitter { */ var transaction; - + switch (message.method) { - case DartSIP_C.INVITE: + case SipMethod.INVITE: transaction = this._transactions['ict'][message.via_branch]; if (transaction != null) { transaction.receiveResponse(message); } break; - case DartSIP_C.ACK: + case SipMethod.ACK: // Just in case ;-). break; default: diff --git a/lib/src/grammar_parser.dart b/lib/src/grammar_parser.dart index 1afff48e..78b49171 100644 --- a/lib/src/grammar_parser.dart +++ b/lib/src/grammar_parser.dart @@ -2,6 +2,7 @@ // Processing tool available at https://github.com/mezoni/peg import 'dart:core'; +import 'Constants.dart'; import 'URI.dart'; import 'NameAddrHeader.dart'; @@ -10,7 +11,7 @@ class Data { var port; var host_type; var value; - var method; + String _method; var reason_phrase; URI uri; var uri_headers; @@ -52,6 +53,8 @@ class Data { var text; var uuid; Data(); + + SipMethod get method=>SipMethodHelper.fromString(_method); } class GrammarParser { static final List _ascii = new List.generate(128, (c) => new String.fromCharCode(c)); @@ -4696,8 +4699,8 @@ class GrammarParser { var pos0 = _startPos, offset = $start; { ///CODE_START - data.method = _text(); - $$ = data.method; + data._method = _text(); + $$ = data._method; ///CODE_END } } diff --git a/lib/src/sanityCheck.dart b/lib/src/sanityCheck.dart index 4abc1d89..090f73f5 100644 --- a/lib/src/sanityCheck.dart +++ b/lib/src/sanityCheck.dart @@ -1,4 +1,5 @@ import 'Constants.dart' as DartSIP_C; +import 'Constants.dart'; import 'SIPMessage.dart' as SIPMessage; import 'Utils.dart' as Utils; import 'logger.dart'; @@ -118,7 +119,7 @@ rfc3261_8_2_2_2() { } // INVITE request. - if (message.method == DartSIP_C.INVITE) { + if (message.method == SipMethod.INVITE) { // If the branch matches the key of any IST then assume it is a retransmission // and ignore the INVITE. // TODO: we should reply the last response. @@ -222,7 +223,7 @@ reply(status_code) { response += 'To: ${to}\r\n'; response += 'From: ${message.getHeader('From')}\r\n'; response += 'Call-ID: ${message.call_id}\r\n'; - response += 'CSeq: ${message.cseq} ${message.method}\r\n'; + response += 'CSeq: ${message.cseq} ${SipMethodHelper.getName(message.method)}\r\n'; response += '\r\n'; transport.send(response); diff --git a/test/test_digest_authentication.dart b/test/test_digest_authentication.dart index dee8785f..4d3893da 100644 --- a/test/test_digest_authentication.dart +++ b/test/test_digest_authentication.dart @@ -1,6 +1,8 @@ import 'package:test/test.dart'; import 'package:sip_ua/src/DigestAuthentication.dart'; +import 'package:sip_ua/src/Constants.dart'; + // Results of this tests originally obtained from RFC 2617 and: // 'https://pernau.at/kd/sipdigest.php' @@ -8,7 +10,7 @@ var testFunctions = [ () => test("DigestAuthentication: parse no auth testrealm@host.com -RFC 2617-", () { - var method = 'GET'; + SipMethod method = SipMethod.GET; var ruri = '/dir/index.html'; var cnonce = '0a4f113b'; var credentials = Credentials.fromMap({ @@ -33,7 +35,7 @@ var testFunctions = [ expect(digest.response, '6629fae49393a05397450978507c4ef1'); }), () => test('DigestAuthentication: digest authenticate qop = null', () { - var method = 'REGISTER'; + SipMethod method = SipMethod.REGISTER; var ruri = 'sip:testrealm@host.com'; var credentials = Credentials.fromMap({ 'username': 'testuser', @@ -57,7 +59,7 @@ var testFunctions = [ expect(digest.response, 'f99e05f591f147facbc94ff23b4b1dee'); }), () => test('DigestAuthentication: digest authenticate qop = auth', () { - var method = 'REGISTER'; + SipMethod method = SipMethod.REGISTER; var ruri = 'sip:testrealm@host.com'; var cnonce = '0a4f113b'; var credentials = Credentials.fromMap({ @@ -84,7 +86,7 @@ var testFunctions = [ () => test( 'DigestAuthentication: digest authenticate qop = auth-int and empty body', () { - var method = 'REGISTER'; + SipMethod method = SipMethod.REGISTER; var ruri = 'sip:testrealm@host.com'; var cnonce = '0a4f113b'; var credentials = Credentials.fromMap({ @@ -111,7 +113,7 @@ var testFunctions = [ () => test( 'DigestAuthentication: digest authenticate qop = auth-int and non-empty body', () { - var method = 'REGISTER'; + SipMethod method = SipMethod.REGISTER; var ruri = 'sip:testrealm@host.com'; var body = 'TEST BODY'; var cnonce = '0a4f113b';