Example #1
0
File: pm.go Project: logan/heim
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
}
Example #2
0
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
}
Example #3
0
func (s *memIdentity) View() *proto.IdentityView {
	return &proto.IdentityView{
		ID:        proto.UserID(s.id),
		Name:      s.name,
		ServerID:  s.serverID,
		ServerEra: s.serverEra,
	}
}
Example #4
0
File: pm.go Project: logan/heim
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
}
Example #5
0
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{}}
}
Example #6
0
File: ban.go Project: logan/heim
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
}
Example #7
0
File: pm.go Project: logan/heim
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
}
Example #8
0
File: ban.go Project: logan/heim
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
}
Example #9
0
func (c *consoleIdentity) ID() proto.UserID { return proto.UserID("console") }
Example #10
0
func (s *session) Identity() proto.Identity { return backend.NewIdentity(proto.UserID(s.id), s.name) }
Example #11
0
func (s *memIdentity) ID() proto.UserID  { return proto.UserID(s.id) }
Example #12
0
File: pm.go Project: logan/heim
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
}
Example #13
0
File: pm.go Project: logan/heim
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
}
Example #14
0
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
}