Skip to content

Commit

Permalink
Merge pull request #563 from strukturag/dialout-support
Browse files Browse the repository at this point in the history
Implement message handler for dialout support.
  • Loading branch information
fancycode authored Oct 26, 2023
2 parents 7c96325 + 225f5bb commit 0936d40
Show file tree
Hide file tree
Showing 12 changed files with 1,064 additions and 5 deletions.
14 changes: 13 additions & 1 deletion api_async.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
*/
package signaling

import "time"
import (
"encoding/json"
"fmt"
"time"
)

type AsyncMessage struct {
SendTime time.Time `json:"sendtime"`
Expand All @@ -41,6 +45,14 @@ type AsyncMessage struct {
Id string `json:"id"`
}

func (m *AsyncMessage) String() string {
data, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", m, err)
}
return string(data)
}

type AsyncRoomMessage struct {
Type string `json:"type"`

Expand Down
64 changes: 64 additions & 0 deletions api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ import (
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
"time"
)

const (
Expand Down Expand Up @@ -104,6 +106,10 @@ type BackendServerRoomRequest struct {

SwitchTo *BackendRoomSwitchToMessageRequest `json:"switchto,omitempty"`

Dialout *BackendRoomDialoutRequest `json:"dialout,omitempty"`

Transient *BackendRoomTransientRequest `json:"transient,omitempty"`

// Internal properties
ReceivedTime int64 `json:"received,omitempty"`
}
Expand Down Expand Up @@ -170,6 +176,64 @@ type BackendRoomSwitchToMessageRequest struct {
SessionsMap BackendRoomSwitchToSessionsMap `json:"sessionsmap,omitempty"`
}

type BackendRoomDialoutRequest struct {
// E.164 number to dial (e.g. "+1234567890")
Number string `json:"number"`

Options json.RawMessage `json:"options,omitempty"`
}

var (
checkE164Number = regexp.MustCompile(`^\+\d{2,}$`)
)

func isValidNumber(s string) bool {
return checkE164Number.MatchString(s)
}

func (r *BackendRoomDialoutRequest) ValidateNumber() *Error {
if r.Number == "" {
return NewError("number_missing", "No number provided")
}

if !isValidNumber(r.Number) {
return NewError("invalid_number", "Expected E.164 number.")
}

return nil
}

type TransientAction string

const (
TransientActionSet TransientAction = "set"
TransientActionDelete TransientAction = "delete"
)

type BackendRoomTransientRequest struct {
Action TransientAction `json:"action"`
Key string `json:"key"`
Value interface{} `json:"value,omitempty"`
TTL time.Duration `json:"ttl,omitempty"`
}

type BackendServerRoomResponse struct {
Type string `json:"type"`

Dialout *BackendRoomDialoutResponse `json:"dialout,omitempty"`
}

type BackendRoomDialoutError struct {
Code string `json:"code"`
Message string `json:"message,omitempty"`
}

type BackendRoomDialoutResponse struct {
CallId string `json:"callid,omitempty"`

Error *Error `json:"error,omitempty"`
}

// Requests from the signaling server to the Nextcloud backend.

type BackendClientAuthRequest struct {
Expand Down
24 changes: 24 additions & 0 deletions api_backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,27 @@ func TestBackendChecksum(t *testing.T) {
t.Errorf("Checksum %s could not be validated from request", check1)
}
}

func TestValidNumbers(t *testing.T) {
valid := []string{
"+12",
"+12345",
}
invalid := []string{
"+1",
"12345",
" +12345",
" +12345 ",
"+123-45",
}
for _, number := range valid {
if !isValidNumber(number) {
t.Errorf("number %s should be valid", number)
}
}
for _, number := range invalid {
if isValidNumber(number) {
t.Errorf("number %s should not be valid", number)
}
}
}
78 changes: 78 additions & 0 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package signaling

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
Expand Down Expand Up @@ -164,6 +165,10 @@ type ServerMessage struct {
Event *EventServerMessage `json:"event,omitempty"`

TransientData *TransientDataServerMessage `json:"transient,omitempty"`

Internal *InternalServerMessage `json:"internal,omitempty"`

Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
}

func (r *ServerMessage) CloseAfterSend(session Session) bool {
Expand Down Expand Up @@ -444,12 +449,14 @@ const (
ServerFeatureWelcome = "welcome"
ServerFeatureHelloV2 = "hello-v2"
ServerFeatureSwitchTo = "switchto"
ServerFeatureDialout = "dialout"

// Features to send to internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions"

// Possible client features from the "hello" request.
ClientFeatureInternalInCall = "internal-incall"
ClientFeatureStartDialout = "start-dialout"
)

var (
Expand All @@ -460,6 +467,7 @@ var (
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
DefaultFeaturesInternal = []string{
ServerFeatureInternalVirtualSessions,
Expand All @@ -468,6 +476,7 @@ var (
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
DefaultWelcomeFeatures = []string{
ServerFeatureAudioVideoPermissions,
Expand All @@ -477,6 +486,7 @@ var (
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
)

Expand Down Expand Up @@ -684,6 +694,52 @@ func (m *InCallInternalClientMessage) CheckValid() error {
return nil
}

type DialoutStatus string

var (
DialoutStatusAccepted DialoutStatus = "accepted"
DialoutStatusRinging DialoutStatus = "ringing"
DialoutStatusConnected DialoutStatus = "connected"
DialoutStatusRejected DialoutStatus = "rejected"
DialoutStatusCleared DialoutStatus = "cleared"
)

type DialoutStatusInternalClientMessage struct {
CallId string `json:"callid"`
Status DialoutStatus `json:"status"`

// Cause is set if Status is "cleared" or "rejected".
Cause string `json:"cause,omitempty"`
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}

type DialoutInternalClientMessage struct {
Type string `json:"type"`

RoomId string `json:"roomid,omitempty"`

Error *Error `json:"error,omitempty"`

Status *DialoutStatusInternalClientMessage `json:"status,omitempty"`
}

func (m *DialoutInternalClientMessage) CheckValid() error {
switch m.Type {
case "":
return errors.New("type missing")
case "error":
if m.Error == nil {
return errors.New("error missing")
}
case "status":
if m.Status == nil {
return errors.New("status missing")
}
}
return nil
}

type InternalClientMessage struct {
Type string `json:"type"`

Expand All @@ -694,10 +750,14 @@ type InternalClientMessage struct {
RemoveSession *RemoveSessionInternalClientMessage `json:"removesession,omitempty"`

InCall *InCallInternalClientMessage `json:"incall,omitempty"`

Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
}

func (m *InternalClientMessage) CheckValid() error {
switch m.Type {
case "":
return errors.New("type missing")
case "addsession":
if m.AddSession == nil {
return fmt.Errorf("addsession missing")
Expand All @@ -722,10 +782,28 @@ func (m *InternalClientMessage) CheckValid() error {
} else if err := m.InCall.CheckValid(); err != nil {
return err
}
case "dialout":
if m.Dialout == nil {
return fmt.Errorf("dialout missing")
} else if err := m.Dialout.CheckValid(); err != nil {
return err
}
}
return nil
}

type InternalServerDialoutRequest struct {
RoomId string `json:"roomid"`

Request *BackendRoomDialoutRequest `json:"request"`
}

type InternalServerMessage struct {
Type string `json:"type"`

Dialout *InternalServerDialoutRequest `json:"dialout,omitempty"`
}

// Type "event"

type RoomEventServerMessage struct {
Expand Down
Loading

0 comments on commit 0936d40

Please sign in to comment.