@ -31,9 +31,9 @@ const (
@@ -31,9 +31,9 @@ const (
)
type ChannellingAPI interface {
OnConnect ( Client , * Session )
OnConnect ( Client , * Session ) ( interface { } , error )
OnDisconnect ( Client , * Session )
OnIncoming ( Response Sender, * Session , * DataIncoming )
OnIncoming ( Sender , * Session , * DataIncoming ) ( interface { } , error )
}
type channellingAPI struct {
@ -60,169 +60,113 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco
@@ -60,169 +60,113 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco
}
}
func ( api * channellingAPI ) OnConnect ( client Client , session * Session ) {
func ( api * channellingAPI ) OnConnect ( client Client , session * Session ) ( interface { } , error ) {
api . Unicaster . OnConnect ( client , session )
api . SendSelf ( client , session )
return api . MakeSelf ( session )
}
func ( api * channellingAPI ) OnDisconnect ( client Client , session * Session ) {
api . Unicaster . OnDisconnect ( client , session )
}
func ( api * channellingAPI ) OnIncoming ( c Response Sender, session * Session , msg * DataIncoming ) {
func ( api * channellingAPI ) OnIncoming ( sender Sender , session * Session , msg * DataIncoming ) ( interface { } , error ) {
switch msg . Type {
case "Self" :
api . SendSelf ( c , session )
return api . MakeSelf ( session )
case "Hello" :
//log.Println("Hello", msg.Hello, c.Index())
// TODO(longsleep): Filter room id and user agent.
session . Update ( & SessionUpdate { Types : [ ] string { "Ua" } , Ua : msg . Hello . Ua } )
room , err := session . JoinRoom ( msg . Hello . Id , msg . Hello . Credentials , c )
// NOTE(lcooper): Iid filtered for compatibility's sake.
// Evaluate sending unconditionally when supported by all clients.
if msg . Iid != "" {
if err == nil {
c . Reply ( msg . Iid , & DataWelcome {
Type : "Welcome" ,
Room : room ,
Users : api . RoomUsers ( session ) ,
} )
} else {
c . Reply ( msg . Iid , err )
}
if msg . Hello == nil {
return nil , NewDataError ( "bad_request" , "message did not contain Hello" )
}
return api . HandleHello ( session , msg . Hello , sender )
case "Offer" :
if msg . Offer == nil {
log . Println ( "Received invalid offer message." , msg )
break
}
// TODO(longsleep): Validate offer
session . Unicast ( msg . Offer . To , msg . Offer )
case "Candidate" :
if msg . Candidate == nil {
log . Println ( "Received invalid candidate message." , msg )
break
}
// TODO(longsleep): Validate candidate
session . Unicast ( msg . Candidate . To , msg . Candidate )
case "Answer" :
if msg . Answer == nil {
log . Println ( "Received invalid answer message." , msg )
break
}
// TODO(longsleep): Validate Answer
session . Unicast ( msg . Answer . To , msg . Answer )
case "Users" :
if session . Hello {
sessions := & DataSessions { Type : "Users" , Users : api . RoomUsers ( session ) }
c . Reply ( msg . Iid , sessions )
}
return api . HandleUsers ( session )
case "Authentication" :
st := msg . Authentication . Authentication
if st == nil {
return
if msg . Authentication == nil || msg . Authentication . Authentication == nil {
return nil , NewDataError ( "bad_request" , "message did not contain Authentication" )
}
if err := api . Authenticate ( session , st , "" ) ; err == nil {
log . Println ( "Authentication success" , session . Userid ( ) )
api . SendSelf ( c , session )
session . BroadcastStatus ( )
} else {
log . Println ( "Authentication failed" , err , st . Userid , st . Nonce )
}
return api . HandleAuthentication ( session , msg . Authentication . Authentication )
case "Bye" :
if msg . Bye == nil {
log . Println ( "Received invalid bye message." , msg )
break
}
session . Unicast ( msg . Bye . To , msg . Bye )
case "Status" :
if msg . Status == nil {
log . Println ( "Received invalid status message." , msg )
break
}
//log.Println("Status", msg.Status)
session . Update ( & SessionUpdate { Types : [ ] string { "Status" } , Status : msg . Status . Status } )
session . BroadcastStatus ( )
case "Chat" :
// TODO(longsleep): Limit sent chat messages per incoming connection.
if ! msg . Chat . Chat . NoEcho {
session . Unicast ( session . Id , msg . Chat )
}
msg . Chat . Chat . Time = time . Now ( ) . Format ( time . RFC3339 )
if msg . Chat . To == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session . Hello {
api . CountBroadcastChat ( )
session . Broadcast ( msg . Chat )
}
} else {
if msg . Chat . Chat . Status != nil && msg . Chat . Chat . Status . ContactRequest != nil {
if ! api . Config . WithModule ( "contacts" ) {
return
}
if err := api . contactrequestHandler ( session , msg . Chat . To , msg . Chat . Chat . Status . ContactRequest ) ; err != nil {
log . Println ( "Ignoring invalid contact request." , err )
return
}
msg . Chat . Chat . Status . ContactRequest . Userid = session . Userid ( )
}
if msg . Chat . Chat . Status == nil {
api . CountUnicastChat ( )
}
session . Unicast ( msg . Chat . To , msg . Chat )
if msg . Chat . Chat . Mid != "" {
// Send out delivery confirmation status chat message.
session . Unicast ( session . Id , & DataChat { To : msg . Chat . To , Type : "Chat" , Chat : & DataChatMessage { Mid : msg . Chat . Chat . Mid , Status : & DataChatStatus { State : "sent" } } } )
}
if msg . Chat == nil || msg . Chat . Chat == nil {
log . Println ( "Received invalid chat message." , msg )
break
}
api . HandleChat ( session , msg . Chat . To , msg . Chat . Chat )
case "Conference" :
// Check conference maximum size.
if len ( msg . Conference . Conference ) > maxConferenceSize {
log . Println ( "Refusing to create conference above limit." , len ( msg . Conference . Conference ) )
} else {
// Send conference update to anyone.
for _ , id := range msg . Conference . Conference {
if id != session . Id {
session . Unicast ( id , msg . Conference )
}
}
if msg . Conference == nil {
log . Println ( "Received invalid conference message." , msg )
break
}
api . HandleConference ( session , msg . Conference )
case "Alive" :
c . Reply ( msg . Iid , msg . Alive )
return msg . Alive , nil
case "Sessions" :
var users [ ] * DataSession
switch msg . Sessions . Sessions . Type {
case "contact" :
if api . Config . WithModule ( "contacts" ) {
if userID , err := api . getContactID ( session , msg . Sessions . Sessions . Token ) ; err == nil {
users = api . GetUserSessions ( session , userID )
} else {
log . Printf ( err . Error ( ) )
}
} else {
log . Printf ( "Incoming contacts session request with contacts disabled" )
}
case "session" :
id , err := session . attestation . Decode ( msg . Sessions . Sessions . Token )
if err != nil {
log . Printf ( "Failed to decode incoming attestation" , err , msg . Sessions . Sessions . Token )
break
}
session , ok := api . GetSession ( id )
if ! ok {
log . Printf ( "Cannot retrieve session for id %s" , id )
break
}
users = make ( [ ] * DataSession , 1 , 1 )
users [ 0 ] = session . Data ( )
default :
log . Printf ( "Unkown incoming sessions request type %s" , msg . Sessions . Sessions . Type )
if msg . Sessions == nil || msg . Sessions . Sessions == nil {
return nil , NewDataError ( "bad_request" , "message did not contain Sessions" )
}
// TODO(lcooper): We ought to reply with a *DataError here if failed.
if users != nil {
c . Reply ( msg . Iid , & DataSessions { Type : "Sessions" , Users : users , Sessions : msg . Sessions . Sessions } )
}
return api . HandleSessions ( session , msg . Sessions . Sessions )
case "Room" :
if room , err := api . UpdateRoom ( session , msg . Room ) ; err == nil {
session . Broadcast ( room )
c . Reply ( msg . Iid , room )
} else {
c . Reply ( msg . Iid , err )
if msg . Room == nil {
return nil , NewDataError ( "bad_request" , "message did not contain Room" )
}
return api . HandleRoom ( session , msg . Room )
default :
log . Println ( "OnText unhandled message type" , msg . Type )
}
return nil , nil
}
func ( api * channellingAPI ) SendSelf ( c Responder , session * Session ) {
func ( api * channellingAPI ) MakeSelf ( session * Session ) ( * DataSelf , error ) {
token , err := api . EncodeSessionToken ( session )
if err != nil {
log . Println ( "Error in OnRegister" , err )
return
return nil , err
}
log . Println ( "Created new session token" , len ( token ) , token )
@ -237,5 +181,138 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) {
@@ -237,5 +181,138 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) {
Turn : api . CreateTurnData ( session ) ,
Stun : api . StunURIs ,
}
c . Reply ( "" , self )
return self , nil
}
func ( api * channellingAPI ) HandleHello ( session * Session , hello * DataHello , sender Sender ) ( * DataWelcome , error ) {
// TODO(longsleep): Filter room id and user agent.
session . Update ( & SessionUpdate { Types : [ ] string { "Ua" } , Ua : hello . Ua } )
room , err := session . JoinRoom ( hello . Id , hello . Credentials , sender )
if err != nil {
return nil , err
}
return & DataWelcome {
Type : "Welcome" ,
Room : room ,
Users : api . RoomUsers ( session ) ,
} , nil
}
func ( api * channellingAPI ) HandleUsers ( session * Session ) ( sessions * DataSessions , err error ) {
if session . Hello {
sessions = & DataSessions { Type : "Users" , Users : api . RoomUsers ( session ) }
} else {
err = NewDataError ( "not_in_room" , "Cannot list users without a current room" )
}
return
}
func ( api * channellingAPI ) HandleAuthentication ( session * Session , st * SessionToken ) ( * DataSelf , error ) {
if err := api . Authenticate ( session , st , "" ) ; err != nil {
log . Println ( "Authentication failed" , err , st . Userid , st . Nonce )
return nil , err
}
log . Println ( "Authentication success" , session . Userid ( ) )
self , err := api . MakeSelf ( session )
if err == nil {
session . BroadcastStatus ( )
}
return self , err
}
func ( api * channellingAPI ) HandleChat ( session * Session , to string , chat * DataChatMessage ) {
// TODO(longsleep): Limit sent chat messages per incoming connection.
if ! chat . NoEcho {
session . Unicast ( session . Id , chat )
}
chat . Time = time . Now ( ) . Format ( time . RFC3339 )
if to == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session . Hello {
api . CountBroadcastChat ( )
session . Broadcast ( chat )
}
} else {
if chat . Status != nil {
if chat . Status . ContactRequest != nil {
if ! api . Config . WithModule ( "contacts" ) {
return
}
if err := api . contactrequestHandler ( session , to , chat . Status . ContactRequest ) ; err != nil {
log . Println ( "Ignoring invalid contact request." , err )
return
}
chat . Status . ContactRequest . Userid = session . Userid ( )
}
} else {
api . CountUnicastChat ( )
}
session . Unicast ( to , chat )
if chat . Mid != "" {
// Send out delivery confirmation status chat message.
session . Unicast ( session . Id , & DataChat { To : to , Type : "Chat" , Chat : & DataChatMessage { Mid : chat . Mid , Status : & DataChatStatus { State : "sent" } } } )
}
}
}
func ( api * channellingAPI ) HandleConference ( session * Session , conference * DataConference ) {
// Check conference maximum size.
if len ( conference . Conference ) > maxConferenceSize {
log . Println ( "Refusing to create conference above limit." , len ( conference . Conference ) )
return
}
// Send conference update to anyone.
for _ , id := range conference . Conference {
if id != session . Id {
session . Unicast ( id , conference )
}
}
}
func ( api * channellingAPI ) HandleSessions ( session * Session , sessions * DataSessionsRequest ) ( * DataSessions , error ) {
switch sessions . Type {
case "contact" :
if ! api . Config . WithModule ( "contacts" ) {
return nil , NewDataError ( "contacts_not_enabled" , "incoming contacts session request with contacts disabled" )
}
userID , err := api . getContactID ( session , sessions . Token )
if err != nil {
return nil , err
}
return & DataSessions {
Type : "Sessions" ,
Users : api . GetUserSessions ( session , userID ) ,
Sessions : sessions ,
} , nil
case "session" :
id , err := session . attestation . Decode ( sessions . Token )
if err != nil {
return nil , NewDataError ( "bad_attestation" , err . Error ( ) )
}
session , ok := api . GetSession ( id )
if ! ok {
return nil , NewDataError ( "no_such_session" , "cannot retrieve session" )
}
return & DataSessions {
Type : "Sessions" ,
Users : [ ] * DataSession { session . Data ( ) } ,
Sessions : sessions ,
} , nil
default :
return nil , NewDataError ( "bad_request" , "unknown sessions request type" )
}
}
func ( api * channellingAPI ) HandleRoom ( session * Session , room * DataRoom ) ( * DataRoom , error ) {
room , err := api . UpdateRoom ( session , room )
if err == nil {
session . Broadcast ( room )
}
return room , err
}