summaryrefslogtreecommitdiff
path: root/server/user
diff options
context:
space:
mode:
Diffstat (limited to 'server/user')
-rw-r--r--server/user/command.go76
-rw-r--r--server/user/user.go95
2 files changed, 171 insertions, 0 deletions
diff --git a/server/user/command.go b/server/user/command.go
new file mode 100644
index 0000000..210dede
--- /dev/null
+++ b/server/user/command.go
@@ -0,0 +1,76 @@
+package user
+
+import (
+ "citrons.xyz/talk/server/session"
+ "citrons.xyz/talk/proto"
+)
+
+func (u *User) SendRequest(r session.Request) {
+ switch r.Cmd.Kind {
+
+ case "update":
+ if r.From.UserId != u.Id() {
+ r.Reply(proto.Fail{"forbidden", "", nil}.Cmd())
+ return
+ }
+ if len(r.Cmd.Args) != 1 {
+ r.ReplyInvalid()
+ return
+ }
+ upd := r.Cmd.Args[0]
+ if upd.Kind != "u" {
+ r.ReplyInvalid()
+ return
+ }
+ name := u.name
+ for k, v := range upd.Fields {
+ switch k {
+ case "":
+ name = v
+ default:
+ r.ReplyInvalid()
+ return
+ }
+ }
+ if name != u.name {
+ err := u.Rename(name)
+ if err != nil {
+ r.Reply(err.Cmd())
+ return
+ }
+ }
+ u.Stream.Event(r.Cmd)
+ r.Reply(proto.NewCmd("ok", ""))
+
+ case "i":
+ r.Reply(proto.NewCmd("i", "", u.GetInfo()))
+
+ case "s":
+ r.From.Subscribe(&u.Stream)
+ r.Reply(proto.NewCmd("i", "", u.GetInfo()))
+
+ case "u":
+ r.From.Unsubscribe(&u.Stream)
+ r.Reply(proto.NewCmd("ok", ""))
+
+ default:
+ r.ReplyInvalid()
+ }
+}
+
+func (t Tombstone) SendRequest(r session.Request) {
+ switch r.Cmd.Kind {
+
+ case "update":
+ r.Reply(proto.Fail{"bad-target", "", nil}.Cmd())
+
+ case "i", "s":
+ r.Reply(proto.NewCmd("i", "", t.GetInfo()))
+
+ case "u":
+ r.Reply(proto.NewCmd("ok", ""))
+
+ default:
+ r.ReplyInvalid()
+ }
+}
diff --git a/server/user/user.go b/server/user/user.go
new file mode 100644
index 0000000..41a5c4f
--- /dev/null
+++ b/server/user/user.go
@@ -0,0 +1,95 @@
+package user
+
+import (
+ "citrons.xyz/talk/server/object"
+ "citrons.xyz/talk/server/session"
+ "citrons.xyz/talk/proto"
+)
+
+type UserStore struct {
+ world *object.World
+ byName map[string]*User
+ gone map[string]Tombstone
+}
+
+type User struct {
+ store *UserStore
+ name string
+ id string
+ Stream session.Stream
+ Channels map[string]bool
+ Anonymous bool
+}
+
+type Tombstone struct {
+ name string
+}
+
+func NewStore(world *object.World) *UserStore {
+ return &UserStore {
+ world, make(map[string]*User), make(map[string]Tombstone),
+ }
+}
+
+func (us *UserStore) CreateUser(name string) (*User, *proto.Fail) {
+ if us.byName[name] != nil {
+ return nil, &proto.Fail {
+ "name-taken", "", map[string]string {"": name},
+ }
+ }
+ var u User
+ u.store = us
+ u.name = name
+ us.byName[name] = &u
+ u.id = us.world.NewObject(&u)
+ u.Channels = make(map[string]bool)
+ return &u, nil
+}
+
+func (us *UserStore) ByName(name string) *User {
+ return us.byName[name]
+}
+
+func (u *User) Name() string {
+ return u.name
+}
+
+func (u *User) Id() string {
+ return u.id
+}
+
+func (u *User) Rename(name string) *proto.Fail {
+ if u.store.byName[name] != nil {
+ return &proto.Fail {
+ "name-taken", "", map[string]string {"": name},
+ }
+ }
+ u.store.byName[u.name] = nil
+ u.store.byName[name] = u
+ u.name = name
+ return nil
+}
+
+func (u *User) Delete() {
+ u.Stream.Event(proto.NewCmd("delete", u.id))
+ u.Stream.UnsubscribeAll()
+
+ delete(u.store.byName, u.name)
+ u.store.world.RemoveObject(u.id)
+
+ gone := Tombstone {u.name}
+ u.store.gone[u.id] = gone
+ u.store.world.PutObject(u.id, gone)
+}
+
+func (u *User) GetInfo() proto.Object {
+ return proto.Object {
+ "u", u.id, map[string]string {"": u.name},
+ }
+}
+
+func (t Tombstone) GetInfo() proto.Object {
+ return proto.Object {
+ "gone", "", map[string]string {"": t.name},
+ }
+}