-
-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathdialog_session.go
169 lines (141 loc) · 4.84 KB
/
dialog_session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2024, Emir Aganovic
package diago
import (
"context"
"fmt"
"strings"
"github.com/emiago/sipgo"
"github.com/emiago/sipgo/sip"
"github.com/rs/zerolog/log"
)
type DialogSession interface {
Id() string
Context() context.Context
Hangup(ctx context.Context) error
Media() *DialogMedia
DialogSIP() *sipgo.Dialog
Do(ctx context.Context, req *sip.Request) (*sip.Response, error)
}
//
// Here are many common functions built for dialog
//
func dialogRefer(ctx context.Context, d DialogSession, recipient sip.Uri, referTo sip.Uri) error {
if d.DialogSIP().LoadState() != sip.DialogStateConfirmed {
return fmt.Errorf("Can only be called on answered dialog")
}
req := sip.NewRequest(sip.REFER, recipient)
// Invite request tags must be preserved but switched
req.AppendHeader(sip.NewHeader("Refer-To", referTo.String()))
res, err := d.Do(ctx, req)
if err != nil {
return err
}
if res.StatusCode != sip.StatusAccepted {
return sipgo.ErrDialogResponse{
Res: res,
}
}
return nil
}
func dialogHandleReferNotify(d DialogSession, req *sip.Request, tx sip.ServerTransaction) {
// TODO how to know this is refer
contentType := req.ContentType().Value()
// For now very basic check
if !strings.HasPrefix(contentType, "message/sipfrag;version=2.0") {
tx.Respond(sip.NewResponseFromRequest(req, sip.StatusBadRequest, "Bad Request", nil))
return
}
frag := string(req.Body())
if len(frag) < len("SIP/2.0 100 xx") {
tx.Respond(sip.NewResponseFromRequest(req, sip.StatusBadRequest, "Bad Request", nil))
return
}
tx.Respond(sip.NewResponseFromRequest(req, sip.StatusOK, "OK", nil))
log.Info().Msg("Handling NOTIFY: " + string(req.Body()))
switch frag[:11] {
case "SIP/2.0 100":
case "SIP/2.0 200":
d.Hangup(d.Context())
}
}
func dialogHandleRefer(d DialogSession, dg *Diago, req *sip.Request, tx sip.ServerTransaction, onReferDialog func(referDialog *DialogClientSession)) {
referTo := req.GetHeader("Refer-To")
// https://datatracker.ietf.org/doc/html/rfc3515#section-2.4.2
// An agent responding to a REFER method MUST return a 400 (Bad Request)
// if the request contained zero or more than one Refer-To header field
// values.
log := &dg.log
if referTo == nil {
log.Info().Msg("Received REFER without Refer-To header")
tx.Respond(sip.NewResponseFromRequest(req, 400, "Bad Request", nil))
return
}
referToUri := sip.Uri{}
if err := sip.ParseUri(referTo.Value(), &referToUri); err != nil {
log.Info().Err(err).Msg("Received REFER bud failed to parse Refer-To uri")
tx.Respond(sip.NewResponseFromRequest(req, 400, "Bad Request", nil))
return
}
contact := req.Contact()
if contact == nil {
tx.Respond(sip.NewResponseFromRequest(req, 400, "Bad Request", []byte("No Contact Header")))
return
}
// TODO can we locate this more checks
log.Info().Msg("Accepting refer")
// emptySess := &DialogClientSession{
// DialogClientSession: &sipgo.DialogClientSession{
// Dialog: sipgo.Dialog{
// InviteRequest: sip.NewRequest(),
// },
// },
// }
// emptySess.Init()
// onRefDialog(&DialogClientSession{})
tx.Respond(sip.NewResponseFromRequest(req, 202, "Accepted", nil))
// TODO after this we could get BYE immediately, but caller would not be able
// to take control over refer dialog
addSipFrag := func(req *sip.Request, statusCode sip.StatusCode, reason string) {
req.AppendHeader(sip.NewHeader("Event", "refer"))
req.AppendHeader(sip.NewHeader("Content-Type", "message/sipfrag;version=2.0"))
frag := fmt.Sprintf("SIP/2.0 %d %s", statusCode, reason)
req.SetBody([]byte(frag))
}
ctx := d.Context()
notify := sip.NewRequest(sip.NOTIFY, contact.Address)
notify100 := notify.Clone()
addSipFrag(notify100, 100, "Trying")
// FROM, TO, CALLID must be same to make SUBSCRIBE working
_, err := d.Do(ctx, notify100)
if err != nil {
log.Info().Err(err).Msg("REFER NOTIFY 100 failed to sent")
return
}
// if res.StatusCode != 200 {
// log.Info().Int("res", int(res.StatusCode)).Msg("REFER NOTIFY 100 non 200 resposne")
// return
// }
referDialog, err := dg.Invite(ctx, referToUri, InviteOptions{})
if err != nil {
// DO notify?
log.Error().Err(err).Msg("REFER dialog failed to dial")
return
}
// We send ref dialog to processing. After sending 200 OK this session will terminate
// TODO this should be called before Invite started as caller needs to be notified before
onReferDialog(referDialog)
notify200 := notify.Clone()
addSipFrag(notify200, 200, "OK")
_, err = d.Do(ctx, notify200)
if err != nil {
log.Info().Err(err).Msg("REFER NOTIFY 100 failed to sent")
return
}
// Now this dialog will receive BYE and it will terminate
// We need to send this referDialog to control of caller
// if res.StatusCode != 200 {
// dg.log.Info().Int("res", int(res.StatusCode)).Msg("REFER NOTIFY 100 non 200 resposne")
// return
// }
}