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 }