// loadContacts initializes topic.perSubs to support presence notifications // Case 1.a.i, 1.a.ii func (t *Topic) loadContacts(uid types.Uid) error { subs, err := store.Users.GetSubs(uid) if err != nil { return err } t.perSubs = make(map[string]perSubsData, len(subs)) for _, sub := range subs { //log.Printf("Pres 1.a.i-ii: topic[%s]: processing sub '%s'", t.name, sub.Topic) topic := sub.Topic if strings.HasPrefix(topic, "p2p") { if uid1, uid2, err := types.ParseP2P(topic); err == nil { if uid1 == uid { topic = uid2.UserId() } else { topic = uid1.UserId() } } else { continue } } else if topic == t.name { // No need to push updates to self continue } //log.Printf("Pres 1.a.i-ii: topic[%s]: caching as '%s'", t.name, topic) t.perSubs[topic] = perSubsData{} } //log.Printf("Pres 1.a.i-ii: topic[%s]: total cached %d", t.name, len(t.perSubs)) return nil }
// replyTopicDescBasic loads minimal topic Desc when the requester is not subscribed to the topic func replyTopicDescBasic(sess *Session, topic string, get *MsgClientGet) { log.Printf("hub.replyTopicDescBasic: topic %s", topic) now := time.Now().UTC().Round(time.Millisecond) desc := &MsgTopicDesc{} if strings.HasPrefix(topic, "grp") { stopic, err := store.Topics.Get(topic) if err == nil { desc.CreatedAt = &stopic.CreatedAt desc.UpdatedAt = &stopic.UpdatedAt desc.Public = stopic.Public } else { sess.queueOut(ErrUnknown(get.Id, get.Topic, now)) return } } else { // 'me' and p2p topics var uid types.Uid if strings.HasPrefix(topic, "usr") { // User specified as usrXXX uid = types.ParseUserId(topic) } else if strings.HasPrefix(topic, "p2p") { // User specified as p2pXXXYYY uid1, uid2, _ := types.ParseP2P(topic) if uid1 == sess.uid { uid = uid2 } else if uid2 == sess.uid { uid = uid1 } } if uid.IsZero() { sess.queueOut(ErrMalformed(get.Id, get.Topic, now)) return } suser, err := store.Users.Get(uid) if err == nil { desc.CreatedAt = &suser.CreatedAt desc.UpdatedAt = &suser.UpdatedAt desc.Public = suser.Public } else { log.Printf("hub.replyTopicInfoBasic: sending error 3") sess.queueOut(ErrUnknown(get.Id, get.Topic, now)) return } } log.Printf("hub.replyTopicDescBasic: sending desc -- OK") sess.queueOut(&ServerComMessage{ Meta: &MsgServerMeta{Id: get.Id, Topic: get.Topic, Timestamp: &now, Desc: desc}}) }
// validateTopicName expands session specific topic name to global name // Returns // topic: session-specific topic name the message recepient should see // routeTo: routable global topic name // err: *ServerComMessage with an error to return to the sender func (s *Session) validateTopicName(msgId, topic string, timestamp time.Time) (string, string, *ServerComMessage) { if topic == "" { return "", "", ErrMalformed(msgId, "", timestamp) } if !strings.HasPrefix(topic, "grp") && s.uid.IsZero() { // me and p2p topics require authentication return "", "", ErrAuthRequired(msgId, topic, timestamp) } // Topic to route to i.e. rcptto: or s.subs[routeTo] routeTo := topic if topic == "me" { routeTo = s.uid.UserId() } else if topic == "fnd" { routeTo = s.uid.FndName() } else if strings.HasPrefix(topic, "usr") { // Initiating a p2p topic uid2 := types.ParseUserId(topic) if uid2.IsZero() { // Ensure the user id is valid return "", "", ErrMalformed(msgId, topic, timestamp) } else if uid2 == s.uid { // Use 'me' to access self-topic return "", "", ErrPermissionDenied(msgId, topic, timestamp) } routeTo = s.uid.P2PName(uid2) topic = routeTo } else if strings.HasPrefix(topic, "p2p") { uid1, uid2, err := types.ParseP2P(topic) if err != nil || uid1.IsZero() || uid2.IsZero() || uid1 == uid2 { // Ensure the user ids are valid return "", "", ErrMalformed(msgId, topic, timestamp) } else if uid1 != s.uid && uid2 != s.uid { // One can't access someone else's p2p topic return "", "", ErrPermissionDenied(msgId, topic, timestamp) } } return topic, routeTo, nil }
// SubsForTopic fetches all subsciptions for a topic. func (a *RethinkDbAdapter) SubsForTopic(topic string) ([]t.Subscription, error) { //log.Println("Loading subscriptions for topic ", topic) // must load User.Public for p2p topics var p2p []t.User var err error if t.GetTopicCat(topic) == t.TopicCat_P2P { uid1, uid2, _ := t.ParseP2P(topic) if p2p, err = a.UserGetAll(uid1, uid2); err != nil { return nil, err } else if len(p2p) != 2 { return nil, errors.New("failed to load two p2p users") } } q := rdb.DB(a.dbName).Table("subscriptions").GetAllByIndex("Topic", topic).Limit(MAX_RESULTS) //log.Println("Loading subscription q=", q) rows, err := q.Run(a.conn) if err != nil { return nil, err } var subs []t.Subscription var ss t.Subscription for rows.Next(&ss) { if p2p != nil { if p2p[0].Id == ss.User { ss.SetPublic(p2p[1].Public) ss.SetWith(p2p[1].Id) } else { ss.SetPublic(p2p[0].Public) ss.SetWith(p2p[0].Id) } } subs = append(subs, ss) //log.Printf("SubsForTopic: loaded sub %#+v", ss) } return subs, rows.Err() }
// TopicsForUser loads user's contact list: p2p and grp topics, except for 'me' subscription. func (a *RethinkDbAdapter) TopicsForUser(uid t.Uid) ([]t.Subscription, error) { // Fetch user's subscriptions // Subscription have Topic.UpdatedAt denormalized into Subscription.UpdatedAt q := rdb.DB(a.dbName).Table("subscriptions").GetAllByIndex("User", uid.String()).Limit(MAX_RESULTS) //log.Printf("RethinkDbAdapter.TopicsForUser q: %+v", q) rows, err := q.Run(a.conn) if err != nil { return nil, err } // Fetch subscriptions. Two queries are needed: users table (me & p2p) and topics table (p2p and grp). // Prepare a list of Separate subscriptions to users vs topics var sub t.Subscription join := make(map[string]t.Subscription) // Keeping these to make a join with table for .private and .access topq := make([]interface{}, 0, 16) usrq := make([]interface{}, 0, 16) for rows.Next(&sub) { tcat := t.GetTopicCat(sub.Topic) // 'me' subscription, skip if tcat == t.TopicCat_Me || tcat == t.TopicCat_Fnd { continue // p2p subscription, find the other user to get user.Public } else if tcat == t.TopicCat_P2P { uid1, uid2, _ := t.ParseP2P(sub.Topic) if uid1 == uid { usrq = append(usrq, uid2.String()) } else { usrq = append(usrq, uid1.String()) } topq = append(topq, sub.Topic) // grp subscription } else { topq = append(topq, sub.Topic) } join[sub.Topic] = sub } //log.Printf("RethinkDbAdapter.TopicsForUser topq, usrq: %+v, %+v", topq, usrq) var subs []t.Subscription if len(topq) > 0 || len(usrq) > 0 { subs = make([]t.Subscription, 0, len(join)) } if len(topq) > 0 { // Fetch grp & p2p topics rows, err = rdb.DB(a.dbName).Table("topics").GetAll(topq...).Run(a.conn) if err != nil { return nil, err } var top t.Topic for rows.Next(&top) { sub = join[top.Id] sub.ObjHeader.MergeTimes(&top.ObjHeader) sub.SetSeqId(top.SeqId) sub.SetHardClearId(top.ClearId) if t.GetTopicCat(sub.Topic) == t.TopicCat_Grp { // all done with a grp topic sub.SetPublic(top.Public) subs = append(subs, sub) } else { // put back the updated value of a p2p subsription, will process further below join[top.Id] = sub } } //log.Printf("RethinkDbAdapter.TopicsForUser 1: %#+v", subs) } // Fetch p2p users and join to p2p tables if len(usrq) > 0 { rows, err = rdb.DB(a.dbName).Table("users").GetAll(usrq...).Run(a.conn) if err != nil { return nil, err } var usr t.User for rows.Next(&usr) { uid2 := t.ParseUid(usr.Id) topic := uid.P2PName(uid2) if sub, ok := join[topic]; ok { sub.ObjHeader.MergeTimes(&usr.ObjHeader) sub.SetWith(uid2.UserId()) sub.SetPublic(usr.Public) sub.SetLastSeenAndUA(usr.LastSeen, usr.UserAgent) subs = append(subs, sub) } } //log.Printf("RethinkDbAdapter.TopicsForUser 2: %#+v", subs) } return subs, nil }