func (rb *RoomBinding) RenameUser(ctx scope.Context, session proto.Session, formerName string) ( *proto.NickEvent, error) { presence := &Presence{ Room: rb.Name, ServerID: rb.desc.ID, ServerEra: rb.desc.Era, SessionID: session.ID(), Updated: time.Now(), } err := presence.SetFact(&proto.Presence{ SessionView: *session.View(), LastInteracted: presence.Updated, }) if err != nil { return nil, fmt.Errorf("presence marshal error: %s", err) } if _, err := rb.DbMap.Update(presence); err != nil { return nil, fmt.Errorf("presence update error: %s", err) } event := &proto.NickEvent{ SessionID: session.ID(), ID: session.Identity().ID(), From: formerName, To: session.Identity().Name(), } return event, rb.Backend.broadcast(ctx, rb.Room, proto.NickEventType, event, session) }
func (b *Backend) part(ctx scope.Context, rb *RoomBinding, session proto.Session) error { t, err := b.DbMap.Begin() if err != nil { return err } _, err = t.Exec( "DELETE FROM presence WHERE room = $1 AND server_id = $2 AND server_era = $3 AND session_id = $4", rb.RoomName, b.desc.ID, b.desc.Era, session.ID()) if err != nil { rollback(ctx, t) logging.Logger(ctx).Printf("failed to persist departure: %s", err) return err } // Broadcast a presence event. // TODO: make this an explicit action via the Room protocol, to support encryption event := proto.PresenceEvent(session.View(proto.Staff)) if err := rb.broadcast(ctx, t, proto.PartEventType, event, session); err != nil { rollback(ctx, t) return err } if err := t.Commit(); err != nil { return err } b.Lock() if lm, ok := b.listeners[rb.RoomName]; ok { delete(lm, session.ID()) } b.Unlock() return nil }
func (r *RoomBase) Join(ctx scope.Context, session proto.Session) (string, error) { client := &proto.Client{} if !client.FromContext(ctx) { return "", fmt.Errorf("client data not found in scope") } r.m.Lock() defer r.m.Unlock() if r.identities == nil { r.identities = map[proto.UserID]proto.Identity{} } if r.nicks == nil { r.nicks = map[proto.UserID]string{} } if r.live == nil { r.live = map[proto.UserID][]proto.Session{} } if r.clients == nil { r.clients = map[string]*proto.Client{} } ident := session.Identity() id := ident.ID() if banned, ok := r.agentBans[ident.ID()]; ok && banned.After(time.Now()) { return "", proto.ErrAccessDenied } if banned, ok := r.ipBans[client.IP]; ok && banned.After(time.Now()) { return "", proto.ErrAccessDenied } if _, ok := r.identities[id]; !ok { r.identities[id] = ident } r.live[id] = append(r.live[id], session) r.clients[session.ID()] = client event := proto.PresenceEvent(session.View(proto.Staff)) return "virt:" + event.RealClientAddress, r.broadcast(ctx, proto.JoinType, &event, session) }
func (r *RoomBase) Part(ctx scope.Context, session proto.Session) error { r.m.Lock() defer r.m.Unlock() ident := session.Identity() id := ident.ID() live := r.live[id] for i, s := range live { if s == session { copy(live[i:], live[i+1:]) r.live[id] = live[:len(live)-1] } } if len(r.live[id]) == 0 { delete(r.live, id) delete(r.identities, id) } delete(r.clients, session.ID()) event := proto.PresenceEvent(session.View(proto.Staff)) return r.broadcast(ctx, proto.PartEventType, &event, session) }
func (b *Backend) part(ctx scope.Context, room *Room, session proto.Session) error { b.Lock() defer b.Unlock() if lm, ok := b.listeners[room.Name]; ok { delete(lm, session.ID()) } _, err := b.DbMap.Exec( "DELETE FROM presence"+ " WHERE room = $1 AND server_id = $2 AND server_era = $3 AND session_id = $4", room.Name, b.desc.ID, b.desc.Era, session.ID()) if err != nil { backend.Logger(ctx).Printf("failed to persist departure: %s", err) } // Broadcast a presence event. // TODO: make this an explicit action via the Room protocol, to support encryption return b.broadcast(ctx, room, proto.PartEventType, proto.PresenceEvent(*session.View()), session) }
func (r *memRoom) Join(ctx scope.Context, session proto.Session) error { client := &proto.Client{} if !client.FromContext(ctx) { return fmt.Errorf("client data not found in scope") } r.m.Lock() defer r.m.Unlock() if r.identities == nil { r.identities = map[proto.UserID]proto.Identity{} } if r.live == nil { r.live = map[proto.UserID][]proto.Session{} } if r.clients == nil { r.clients = map[string]*proto.Client{} } ident := session.Identity() id := ident.ID() if banned, ok := r.agentBans[ident.ID()]; ok && banned.After(time.Now()) { return proto.ErrAccessDenied } if banned, ok := r.ipBans[client.IP]; ok && banned.After(time.Now()) { return proto.ErrAccessDenied } if _, ok := r.identities[id]; !ok { r.identities[id] = ident } r.live[id] = append(r.live[id], session) r.clients[session.ID()] = client return r.broadcast(ctx, proto.JoinType, proto.PresenceEvent(*session.View()), session) }
func (rb *RoomBinding) RenameUser(ctx scope.Context, session proto.Session, formerName string) ( *proto.NickEvent, error) { presence := &Presence{ Room: rb.RoomName, ServerID: rb.desc.ID, ServerEra: rb.desc.Era, SessionID: session.ID(), Updated: time.Now(), } err := presence.SetFact(&proto.Presence{ SessionView: session.View(proto.Staff), LastInteracted: presence.Updated, }) if err != nil { return nil, fmt.Errorf("presence marshal error: %s", err) } t, err := rb.DbMap.Begin() if err != nil { return nil, err } if _, err := t.Update(presence); err != nil { rollback(ctx, t) return nil, fmt.Errorf("presence update error: %s", err) } // Store latest nick. // Loop in read-committed mode to simulate UPSERT. for { // Try to insert; if this fails due to duplicate key value, try to update. nick := &Nick{ Room: rb.RoomName, UserID: string(session.Identity().ID()), Nick: session.Identity().Name(), } if err := rb.DbMap.Insert(nick); err != nil { if !strings.HasPrefix(err.Error(), "pq: duplicate key value") { rollback(ctx, t) return nil, err } } else { break } n, err := rb.DbMap.Update(nick) if err != nil { rollback(ctx, t) return nil, err } if n > 0 { break } } event := &proto.NickEvent{ SessionID: session.ID(), ID: session.Identity().ID(), From: formerName, To: session.Identity().Name(), } if err := rb.broadcast(ctx, t, proto.NickEventType, event, session); err != nil { rollback(ctx, t) return nil, err } if err := t.Commit(); err != nil { return nil, err } return event, nil }
func (b *Backend) join(ctx scope.Context, rb *RoomBinding, session proto.Session) (string, error) { client := &proto.Client{} if !client.FromContext(ctx) { return "", fmt.Errorf("client data not found in scope") } bannedAgentCols, err := allColumns(b.DbMap, BannedAgent{}, "") if err != nil { return "", err } bannedIPCols, err := allColumns(b.DbMap, BannedIP{}, "") if err != nil { return "", err } t, err := b.DbMap.Begin() if err != nil { return "", err } // Check for agent ID bans. agentBans, err := t.Select( BannedAgent{}, fmt.Sprintf( "SELECT %s FROM banned_agent WHERE agent_id = $1 AND (room IS NULL OR room = $2) AND (expires IS NULL OR expires > NOW())", bannedAgentCols), session.Identity().ID().String(), rb.RoomName) if err != nil { rollback(ctx, t) return "", err } if len(agentBans) > 0 { logging.Logger(ctx).Printf("access denied to %s: %#v", session.Identity().ID(), agentBans) if err := t.Rollback(); err != nil { return "", err } return "", proto.ErrAccessDenied } // Check for IP bans. ipBans, err := t.Select( BannedIP{}, fmt.Sprintf( "SELECT %s FROM banned_ip WHERE ip = $1 AND (room IS NULL OR room = $2) AND (expires IS NULL OR expires > NOW())", bannedIPCols), client.IP, rb.RoomName) if err != nil { rollback(ctx, t) return "", err } if len(ipBans) > 0 { logging.Logger(ctx).Printf("access denied to %s: %#v", client.IP, ipBans) if err := t.Rollback(); err != nil { return "", err } return "", proto.ErrAccessDenied } // Virtualize the session's client address. var row struct { Address string `db:"address"` } if err := t.SelectOne(&row, "SELECT virtualize_address($1, $2::inet) AS address", rb.RoomName, client.IP); err != nil { return "", err } virtualAddress := row.Address // Look up session's nick. nickRow, err := t.Get(Nick{}, string(session.Identity().ID()), rb.RoomName) if err != nil && err != sql.ErrNoRows { return "", err } if nickRow != nil { session.SetName(nickRow.(*Nick).Nick) } // Write to session log. // TODO: do proper upsert simulation entry := &SessionLog{ SessionID: session.ID(), IP: client.IP, Room: rb.RoomName, UserAgent: client.UserAgent, Connected: client.Connected, } if _, err := t.Delete(entry); err != nil { if rerr := t.Rollback(); rerr != nil { logging.Logger(ctx).Printf("rollback error: %s", rerr) } return "", err } if err := t.Insert(entry); err != nil { if rerr := t.Rollback(); rerr != nil { logging.Logger(ctx).Printf("rollback error: %s", rerr) } return "", err } // Broadcast a presence event. // TODO: make this an explicit action via the Room protocol, to support encryption presence := &Presence{ Room: rb.RoomName, ServerID: b.desc.ID, ServerEra: b.desc.Era, SessionID: session.ID(), Updated: time.Now(), } sessionView := session.View(proto.Staff) sessionView.ClientAddress = virtualAddress err = presence.SetFact(&proto.Presence{ SessionView: sessionView, LastInteracted: presence.Updated, }) if err != nil { rollback(ctx, t) return "", fmt.Errorf("presence marshal error: %s", err) } if err := t.Insert(presence); err != nil { return "", fmt.Errorf("presence insert error: %s", err) } b.Lock() // Add session to listeners. lm, ok := b.listeners[rb.RoomName] if !ok { lm = ListenerMap{} b.listeners[rb.RoomName] = lm } lm[session.ID()] = Listener{Session: session, Client: client} b.Unlock() event := proto.PresenceEvent(session.View(proto.Staff)) event.ClientAddress = virtualAddress if err := rb.broadcast(ctx, t, proto.JoinEventType, event, session); err != nil { rollback(ctx, t) return "", err } if err := t.Commit(); err != nil { return "", err } return virtualAddress, nil }
func (b *Backend) join(ctx scope.Context, room *Room, session proto.Session) error { client := &proto.Client{} if !client.FromContext(ctx) { return fmt.Errorf("client data not found in scope") } bannedAgentCols, err := allColumns(b.DbMap, BannedAgent{}, "") if err != nil { return err } bannedIPCols, err := allColumns(b.DbMap, BannedIP{}, "") if err != nil { return err } t, err := b.DbMap.Begin() if err != nil { return err } rb := func() { rollback(ctx, t) } // Check for agent ID bans. agentBans, err := t.Select( BannedAgent{}, fmt.Sprintf( "SELECT %s FROM banned_agent WHERE agent_id = $1 AND (room IS NULL OR room = $2) AND (expires IS NULL OR expires > NOW())", bannedAgentCols), session.Identity().ID().String(), room.Name) if err != nil { rb() return err } if len(agentBans) > 0 { logging.Logger(ctx).Printf("access denied to %s: %#v", session.Identity().ID(), agentBans) if err := t.Rollback(); err != nil { return err } return proto.ErrAccessDenied } // Check for IP bans. ipBans, err := t.Select( BannedIP{}, fmt.Sprintf( "SELECT %s FROM banned_ip WHERE ip = $1 AND (room IS NULL OR room = $2) AND (expires IS NULL OR expires > NOW())", bannedIPCols), client.IP, room.Name) if err != nil { rb() return err } if len(ipBans) > 0 { logging.Logger(ctx).Printf("access denied to %s: %#v", client.IP, ipBans) if err := t.Rollback(); err != nil { return err } return proto.ErrAccessDenied } // Write to session log. // TODO: do proper upsert simulation entry := &SessionLog{ SessionID: session.ID(), IP: client.IP, Room: room.Name, UserAgent: client.UserAgent, Connected: client.Connected, } if _, err := t.Delete(entry); err != nil { if rerr := t.Rollback(); rerr != nil { logging.Logger(ctx).Printf("rollback error: %s", rerr) } return err } if err := t.Insert(entry); err != nil { if rerr := t.Rollback(); rerr != nil { logging.Logger(ctx).Printf("rollback error: %s", rerr) } return err } // Broadcast a presence event. // TODO: make this an explicit action via the Room protocol, to support encryption presence := &Presence{ Room: room.Name, ServerID: b.desc.ID, ServerEra: b.desc.Era, SessionID: session.ID(), Updated: time.Now(), } err = presence.SetFact(&proto.Presence{ SessionView: *session.View(), LastInteracted: presence.Updated, }) if err != nil { rb() return fmt.Errorf("presence marshal error: %s", err) } if err := t.Insert(presence); err != nil { return fmt.Errorf("presence insert error: %s", err) } b.Lock() // Add session to listeners. lm, ok := b.listeners[room.Name] if !ok { lm = ListenerMap{} b.listeners[room.Name] = lm } lm[session.ID()] = Listener{Session: session, Client: client} b.Unlock() if err := room.broadcast(ctx, t, proto.JoinEventType, proto.PresenceEvent(*session.View()), session); err != nil { rb() return err } if err := t.Commit(); err != nil { return err } return nil }
func (b *Backend) join(ctx scope.Context, room *Room, session proto.Session) error { client := &proto.Client{} if !client.FromContext(ctx) { return fmt.Errorf("client data not found in scope") } // Check for agent ID bans. agentBans, err := b.DbMap.Select( BannedAgent{}, "SELECT agent_id, room, created, expires, room_reason, agent_reason, private_reason"+ " FROM banned_agent"+ " WHERE agent_id = $1 AND (room IS NULL OR room = $2)"+ " AND (expires IS NULL OR expires > NOW())", session.Identity().ID().String(), room.Name) if err != nil { return err } if len(agentBans) > 0 { backend.Logger(ctx).Printf("access denied to %s: %#v", session.Identity().ID(), agentBans) return proto.ErrAccessDenied } // Check for IP bans. ipBans, err := b.DbMap.Select( BannedIP{}, "SELECT ip, room, created, expires, reason FROM banned_ip"+ " WHERE ip = $1 AND (room IS NULL OR room = $2)"+ " AND (expires IS NULL OR expires > NOW())", client.IP, room.Name) if err != nil { return err } if len(ipBans) > 0 { backend.Logger(ctx).Printf("access denied to %s: %#v", client.IP, ipBans) return proto.ErrAccessDenied } // Write to session log. entry := &SessionLog{ SessionID: session.ID(), IP: client.IP, Room: room.Name, UserAgent: client.UserAgent, Connected: client.Connected, } t, err := b.DbMap.Begin() if err != nil { return err } if _, err := t.Delete(entry); err != nil { if rerr := t.Rollback(); rerr != nil { backend.Logger(ctx).Printf("rollback error: %s", rerr) } return err } if err := t.Insert(entry); err != nil { if rerr := t.Rollback(); rerr != nil { backend.Logger(ctx).Printf("rollback error: %s", rerr) } return err } if err := t.Commit(); err != nil { return err } b.Lock() defer b.Unlock() // Add session to listeners. lm, ok := b.listeners[room.Name] if !ok { b.debug("registering listener for %s", room.Name) lm = ListenerMap{} b.listeners[room.Name] = lm } lm[session.ID()] = Listener{Session: session, Client: client} // Broadcast a presence event. // TODO: make this an explicit action via the Room protocol, to support encryption presence := &Presence{ Room: room.Name, ServerID: b.desc.ID, ServerEra: b.desc.Era, SessionID: session.ID(), Updated: time.Now(), } err = presence.SetFact(&proto.Presence{ SessionView: *session.View(), LastInteracted: presence.Updated, }) if err != nil { return fmt.Errorf("presence marshal error: %s", err) } if err := b.DbMap.Insert(presence); err != nil { return fmt.Errorf("presence insert error: %s", err) } b.debug("joining session: %#v", session) b.debug(" -> %#v", session.View()) return b.broadcast(ctx, room, proto.JoinEventType, proto.PresenceEvent(*session.View()), session) }