func (pm *PM) ToBackend() *proto.PM { bpm := &proto.PM{ InitiatorNick: pm.InitiatorNick, Receiver: proto.UserID(pm.Receiver), ReceiverNick: pm.ReceiverNick, ReceiverMAC: pm.ReceiverMAC, IV: pm.IV, EncryptedSystemKey: &security.ManagedKey{ KeyType: proto.RoomMessageKeyType, Ciphertext: pm.EncryptedSystemKey, ContextKey: "pm", ContextValue: pm.ID, }, EncryptedInitiatorKey: &security.ManagedKey{ KeyType: proto.RoomMessageKeyType, IV: pm.IV, Ciphertext: pm.EncryptedInitiatorKey, }, } if len(pm.EncryptedReceiverKey) > 0 { bpm.EncryptedReceiverKey = &security.ManagedKey{ KeyType: proto.RoomMessageKeyType, IV: pm.IV, Ciphertext: pm.EncryptedReceiverKey, } } // ignore id parsing errors _ = bpm.ID.FromString(pm.ID) _ = bpm.Initiator.FromString(pm.Initiator) return bpm }
func (m *Message) ToBackend() proto.Message { msg := proto.Message{ UnixTime: proto.Time(m.Posted), Sender: &proto.SessionView{ IdentityView: &proto.IdentityView{ ID: proto.UserID(m.SenderID), Name: m.SenderName, ServerID: m.ServerID, ServerEra: m.ServerEra, }, SessionID: m.SessionID, }, Content: m.Content, } // ignore id parsing errors _ = msg.ID.FromString(m.ID) _ = msg.Parent.FromString(m.Parent) if m.PreviousEditID.Valid { _ = msg.PreviousEditID.FromString(m.PreviousEditID.String) } // other optionals if m.EncryptionKeyID.Valid { msg.EncryptionKeyID = m.EncryptionKeyID.String } if m.Deleted.Valid { msg.Deleted = proto.Time(m.Deleted.Time) } if m.Edited.Valid { msg.Edited = proto.Time(m.Edited.Time) } return msg }
func (s *memIdentity) View() *proto.IdentityView { return &proto.IdentityView{ ID: proto.UserID(s.id), Name: s.name, ServerID: s.serverID, ServerEra: s.serverEra, } }
func (pmrb *PMRoomBinding) ResolveNick(ctx scope.Context, userID proto.UserID) (string, bool, error) { if userID == proto.UserID(fmt.Sprintf("account:%s", pmrb.pm.Initiator)) { return pmrb.pm.InitiatorNick, true, nil } if userID == pmrb.pm.Receiver { return pmrb.pm.ReceiverNick, true, nil } return "", false, nil }
func (s *session) handleLogoutCommand() *response { if err := s.backend.AgentTracker().ClearClientKey(s.ctx, s.client.Agent.IDString()); err != nil { return &response{err: err} } err := s.backend.NotifyUser(s.ctx, proto.UserID("agent:"+s.AgentID()), proto.LogoutEventType, proto.LogoutEvent{}, s) if err != nil { return &response{err: err} } return &response{packet: &proto.LogoutReply{}} }
func (ban) run(ctx scope.Context, c *console, args []string) error { roomName := c.String("room", "", "ban only in the given room") agent := c.String("agent", "", "agent ID to ban") ip := c.String("ip", "", "IP to ban") duration := c.Duration("duration", 0, "duration of ban (defaults to forever)") if err := c.Parse(args); err != nil { return err } var until time.Time var untilStr string switch *duration { case 0: until = time.Time{} untilStr = "forever" default: until = time.Now().Add(*duration) untilStr = fmt.Sprintf("until %s", until) } ban := proto.Ban{} switch { case *agent != "": ban.ID = proto.UserID(*agent) case *ip != "": ban.IP = *ip default: return fmt.Errorf("-agent <agent-id> or -ip <ip> is required") } if *roomName == "" { if err := c.backend.Ban(ctx, ban, until); err != nil { return err } c.Printf("banned globally for %s: %#v\n", untilStr, ban) } else { room, err := c.backend.GetRoom(ctx, *roomName) if err != nil { return err } if err := room.Ban(ctx, ban, until); err != nil { return err } c.Printf("banned in room %s for %s: %#v\n", *roomName, untilStr, ban) } return nil }
func (pmrb *PMRoomBinding) Snapshot( ctx scope.Context, session proto.Session, level proto.PrivilegeLevel, numMessages int) (*proto.SnapshotEvent, error) { snapshot, err := pmrb.RoomBinding.Snapshot(ctx, session, level, numMessages) if err != nil { return nil, err } if snapshot.Nick == pmrb.pm.InitiatorNick { snapshot.PMWithNick = pmrb.pm.ReceiverNick snapshot.PMWithUserID = pmrb.pm.Receiver } else { snapshot.PMWithNick = pmrb.pm.InitiatorNick snapshot.PMWithUserID = proto.UserID(fmt.Sprintf("account:%s", pmrb.pm.Initiator)) } return snapshot, nil }
func (unban) run(ctx scope.Context, c *console, args []string) error { roomName := c.String("room", "", "unban only in the given room") agent := c.String("agent", "", "agent ID to unban") ip := c.String("ip", "", "IP to unban") if err := c.Parse(args); err != nil { return err } ban := proto.Ban{} switch { case *agent != "": ban.ID = proto.UserID(*agent) case *ip != "": ban.IP = *ip default: return fmt.Errorf("-agent <agent-id> or -ip <ip> is required") } if *roomName == "" { if err := c.backend.Unban(ctx, ban); err != nil { return err } c.Printf("global unban: %#v\n", ban) } else { room, err := c.backend.GetRoom(ctx, *roomName) if err != nil { return err } if err := room.Unban(ctx, ban); err != nil { return err } c.Printf("unban in room %s: %#v\n", *roomName, ban) } return nil }
func (c *consoleIdentity) ID() proto.UserID { return proto.UserID("console") }
func (s *session) Identity() proto.Identity { return backend.NewIdentity(proto.UserID(s.id), s.name) }
func (s *memIdentity) ID() proto.UserID { return proto.UserID(s.id) }
func (t *PMTracker) Initiate( ctx scope.Context, kms security.KMS, room proto.Room, client *proto.Client, recipient proto.UserID) ( snowflake.Snowflake, error) { initiatorNick, ok, err := room.ResolveNick(ctx, proto.UserID(fmt.Sprintf("account:%s", client.Account.ID()))) if err != nil { return 0, err } if !ok { initiatorNick = fmt.Sprintf("account:%s", client.Account.ID()) } recipientNick, ok, err := room.ResolveNick(ctx, recipient) if err != nil { return 0, err } if !ok { recipientNick = string(recipient) } pm, err := proto.InitiatePM(ctx, t.Backend, kms, client, initiatorNick, recipient, recipientNick) if err != nil { return 0, err } row := &PM{ ID: pm.ID.String(), Initiator: pm.Initiator.String(), InitiatorNick: pm.InitiatorNick, Receiver: string(pm.Receiver), ReceiverNick: pm.ReceiverNick, ReceiverMAC: pm.ReceiverMAC, IV: pm.IV, EncryptedSystemKey: pm.EncryptedSystemKey.Ciphertext, EncryptedInitiatorKey: pm.EncryptedInitiatorKey.Ciphertext, } if pm.EncryptedReceiverKey != nil { row.EncryptedReceiverKey = pm.EncryptedReceiverKey.Ciphertext } // Look for existing PM to reuse. tx, err := t.DbMap.Begin() if err != nil { return 0, err } var existingRow PM err = tx.SelectOne( &existingRow, "SELECT id FROM pm WHERE initiator = $1 AND receiver = $2", client.Account.ID().String(), string(recipient)) if err != nil && err != sql.ErrNoRows { rollback(ctx, tx) return 0, err } if err == nil { rollback(ctx, tx) var pmID snowflake.Snowflake if err := pmID.FromString(existingRow.ID); err != nil { return 0, err } return pmID, nil } kind, id := recipient.Parse() if kind == "account" { var existingRow PM err = tx.SelectOne( &existingRow, "SELECT id FROM pm WHERE initiator = $1 AND receiver = $2", id, string(client.UserID())) if err != nil && err != sql.ErrNoRows { rollback(ctx, tx) return 0, err } if err == nil { rollback(ctx, tx) var pmID snowflake.Snowflake if err := pmID.FromString(existingRow.ID); err != nil { return 0, err } return pmID, nil } } if err := tx.Insert(row); err != nil { rollback(ctx, tx) return 0, err } if err := tx.Commit(); err != nil { return 0, err } return pm.ID, nil }
func (t *PMTracker) Initiate( ctx scope.Context, kms security.KMS, room proto.Room, client *proto.Client, recipient proto.UserID) ( snowflake.Snowflake, error) { t.m.Lock() defer t.m.Unlock() // Look for reusable PM. for pmID, pm := range t.pms { if pm.pm.Initiator == client.Account.ID() && pm.pm.Receiver == recipient { return pmID, nil } if pm.pm.Receiver == client.UserID() { kind, id := pm.pm.Receiver.Parse() if kind == "account" && id == pm.pm.Initiator.String() { return pmID, nil } } } // Create new PM. initiatorNick, ok, err := room.ResolveNick(ctx, proto.UserID(fmt.Sprintf("account:%s", client.Account.ID()))) if err != nil { return 0, err } if !ok { initiatorNick = fmt.Sprintf("account:%s", client.Account.ID()) } recipientNick, ok, err := room.ResolveNick(ctx, recipient) if err != nil { return 0, err } if !ok { recipientNick = string(recipient) } pm, err := proto.InitiatePM(ctx, t.b, kms, client, initiatorNick, recipient, recipientNick) if err != nil { return 0, err } pmKey, _, otherName, err := pm.Access(ctx, t.b, kms, client) if err != nil { return 0, err } if t.pms == nil { t.pms = map[snowflake.Snowflake]*PM{} } t.pms[pm.ID] = &PM{ RoomBase: RoomBase{ name: fmt.Sprintf("private chat with %s", otherName), version: t.b.version, log: newMemLog(), messageKey: &roomMessageKey{ id: fmt.Sprintf("pm:%s", pm.ID), timestamp: time.Now(), key: *pmKey, }, }, pm: pm, } return pm.ID, nil }
func (lm ListenerMap) Broadcast(ctx scope.Context, event *proto.Packet, exclude ...string) error { payload, err := event.Payload() if err != nil { return err } excludeSet := map[string]struct{}{} for _, exc := range exclude { excludeSet[exc] = struct{}{} } // Inspect packet to see if it's a bounce event. If so, we'll deliver it // only to the bounced parties. bounceAgentID := proto.UserID("") bounceIP := "" if event.Type == proto.BounceEventType { if bounceEvent, ok := payload.(*proto.BounceEvent); ok { bounceAgentID = bounceEvent.AgentID bounceIP = bounceEvent.IP } else { logging.Logger(ctx).Printf("wtf? expected *proto.BounceEvent, got %T", payload) } } // Inspect packet to see if it's a join event. If so, we'll enable the excluded // listener, and look for aliased sessions to kick into fast-keepalive mode. fastKeepaliveAgentID := "" if event.Type == proto.JoinEventType { if presence, ok := payload.(*proto.PresenceEvent); ok { if idx := strings.IndexRune(string(presence.ID), '-'); idx >= 1 { fastKeepaliveAgentID = string(presence.ID[:idx]) } } for _, sessionID := range exclude { listener, ok := lm[sessionID] if ok && !listener.enabled { listener.enabled = true lm[sessionID] = listener } } } for sessionID, listener := range lm { if _, ok := excludeSet[sessionID]; !ok { if bounceAgentID != "" { if listener.Session.Identity().ID() == bounceAgentID { logging.Logger(ctx).Printf("sending disconnect to %s: %#v", listener.ID(), payload) discEvent := &proto.DisconnectEvent{Reason: payload.(*proto.BounceEvent).Reason} if err := listener.Send(ctx, proto.DisconnectEventType, discEvent); err != nil { logging.Logger(ctx).Printf("error sending disconnect event to %s: %s", listener.ID(), err) } } continue } if bounceIP != "" { if listener.Client.IP == bounceIP { logging.Logger(ctx).Printf("sending disconnect to %s: %#v", listener.ID(), payload) discEvent := &proto.DisconnectEvent{Reason: payload.(*proto.BounceEvent).Reason} if err := listener.Send(ctx, proto.DisconnectEventType, discEvent); err != nil { logging.Logger(ctx).Printf("error sending disconnect event to %s: %s", listener.ID(), err) } } continue } if fastKeepaliveAgentID != "" && strings.HasPrefix(sessionID, fastKeepaliveAgentID) { if err := listener.CheckAbandoned(); err != nil { fmt.Errorf("fast keepalive to %s: %s", listener.ID(), err) } } if !listener.enabled { // The event occurred before the listener joined, so don't deliver it. logging.Logger(ctx).Printf("not delivering event %s before %s joined", event.Type, listener.ID()) continue } if err := listener.Send(ctx, event.Type, payload); err != nil { // TODO: accumulate errors return fmt.Errorf("send message to %s: %s", listener.ID(), err) } } } return nil }