smalltalk
/
osmo-st-all
Archived
1
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
osmo-st-all/callagent/session/SIPIncomingCall.st

193 lines
6.9 KiB
Smalltalk

"
(C) 2011-2014 by Holger Hans Peter Freyther
All Rights Reserved
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"
SIPCallBase subclass: SIPIncomingCall [
| branch wasTrying wasRinging localSDP remoteSDP |
<category: 'OsmoSIP-Callagent'>
<comment: 'I represent an incoming call. One can call trying
rining, pickedUp on me to establish the call. Once the final
ACK has arrived my session will be established.
TODO: Do we need a callTimedOut _after_ sending the 200 and not
receiving an ACK?
'>
LegalStates := nil.
SIPIncomingCall class >> stateAccepted [ <category: 'states'> ^#accepted]
SIPIncomingCall class >> stateRejected [ <category: 'states'> ^#rejected]
SIPIncomingCall class >> legalStates [
<category: 'states'>
^ LegalStates ifNil: [
LegalStates := {
self stateInvite -> self stateInvite.
self stateInvite -> self stateRejected.
self stateInvite -> self stateFailed.
self stateInvite -> self stateAccepted.
self stateAccepted -> self stateAccepted.
self stateAccepted -> self stateSession.
self stateRejected -> self stateRejected.
self stateSession -> self stateHangup.
self stateSession -> self stateRemoteHangup.
}
]
]
SIPIncomingCall class >> initWith: anInvite dialog: dialog on: anAgent [
^self new
initialize;
remoteSDP: anInvite sdp;
useragent: anAgent;
confirmDialog: dialog with: anInvite;
yourself
]
initialize [
state := self class stateInvite.
wasTrying := false.
wasRinging := false.
]
confirmDialog: aDialog with: aRequest [
| via newDialog |
"TODO: look at where the data was actually received!"
via := (aRequest parameter: 'Via' ifAbsent: []).
branch := via branch.
newDialog := (SIPDialog localFromMessage: aRequest)
destIp: via address;
destPort: via port;
confirm;
yourself.
initial_dialog := newDialog.
dialog := newDialog.
next_cseq := initial_dialog cseq + 1.
self registerDialog.
]
remoteSDP: aSDP [
remoteSDP := aSDP
]
remoteSDP [
^remoteSDP
]
reject [
<category: 'accept'>
(self moveToState: self class stateRejected) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to reject.'
expandMacrosWith: self callId) area: #sip.
^false].
self sendResponse: 603 text: 'Not Found' data: nil.
self unregisterDialog.
]
trying [
<category: 'accept'>
(self moveToState: self class stateInvite) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to send invite'
expandMacrosWith: self callId) area: #sip.
^false].
wasTrying := true.
self sendResponse: 100 text: 'Trying' data: nil.
]
ringing [
<category: 'accept'>
(self moveToState: self class stateInvite) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to send ringing'
expandMacrosWith: self callId) area: #sip.
^false].
wasRinging := true.
self sendResponse: 180 text: 'Ringing' data: nil.
]
pickUp: aSDPFile [
<category: 'accept'>
(self moveToState: self class stateAccepted) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to send ringing'
expandMacrosWith: self callId) area: #sip.
^false].
localSDP := aSDPFile.
self sendResponse: 200 text: 'OK' data: localSDP.
]
respondTo: aDialog code: aCode text: aText data: aFile cseq: aCseq[
| resp |
resp := (SIPResponse code: aCode with: aText)
addParameter: 'Via' value: (ua generateVia: branch);
addParameter: 'From' value: aDialog generateFrom;
addParameter: 'To' value: aDialog generateTo;
addParameter: 'Call-ID' value: aDialog callId;
addParameter: 'CSeq' value: ('<1p> <2s>'
expandMacrosWith: aDialog cseq with: aCseq);
sdp: aFile;
yourself.
ua queueData: resp asDatagram dialog: aDialog.
]
sendResponse: aCode text: aText data: aFile [
^self respondTo: dialog code: aCode text: aText data: aFile cseq: 'INVITE'
]
remoteReInvite: aRequest dialog: aDialog [
<category: 'retransmission'>
self state = self class stateRejected
ifTrue: [^self reject].
self state = self class stateAccepted
ifTrue: [^self pickUp: localSDP].
wasRinging ifTrue: [^self ringing].
wasTrying ifTrue: [^self trying].
^self error: ('SIPIncomingCall(<1s>) unknown action for state <2s>'
expandMacrosWith: self callId with: self state) area: #sip.
]
sessionAcked: anAck dialog: aDialog [
"TODO: This could be an ACK for a reject as well! Deal with it!"
(self moveToState: self class stateSession) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to send ringing'
expandMacrosWith: self callId) area: #sip.
^false].
"We have a new session now"
self sessionNew.
]
sessionCanceled: aCancel dialog: aDialog [
(self moveToState: self class stateFailed) ifFalse: [
self logError: ('SIPIncomingCall(<1s>) failed to handle cancel'
expandMacrosWith: self callId) area: #sip.
self respondTo: aDialog code: '200' text: 'OK' data: nil.
^false].
"Tell the user the session has failed. Use the existing
dialogue to say the request was terminated and to reply
to the CANCEL transaction itself."
self sessionFailed.
self sendResponse: 487 text: 'Request Terminated' data: nil.
self unregisterDialog.
self respondTo: aDialog code: '200' text: 'OK' data: nil cseq: 'CANCEL'.
]
]