// Checks for "attach <id>" entries attaching this node ID to sessions we lack // a connection to. Starts a timer to delete them if present. // Must be called during a transaction, holding the session lock. func checkOrphanAttaches() { attachedTo := store.AllAttachedTo(uint64(config.Id())) for _, session := range attachedTo { if sessions[session] == nil { startOrphanTimeout(session) } } }
// Must be called in a transaction, holding session and waiting locks. func handleAuthComplete(conn *userConn, idemChanges []store.Change) { // We're no longer waiting for authentication to complete. delete(waiting, conn.waitingAuth.requestId) waitingAuth := conn.waitingAuth conn.waitingAuth = nil // Find the created session entity. newSessionId := uint64(0) for i := range idemChanges { if idemChanges[i].Key == "kind" && idemChanges[i].Value == "session" { newSessionId = idemChanges[i].TargetEntity break } } // Check session exists. session := store.GetEntity(newSessionId) if session == nil || session.Value("kind") != "session" { sendAuthFail(conn, "Session Deleted") return } // Check it is attached to a user. attachedTo := store.AllAttachedTo(newSessionId) if len(attachedTo) == 0 { sendAuthFail(conn, "User Deleted") return } // Check that user has a username. user := store.GetEntity(attachedTo[0]) if user.Value("name username") == "" { sendAuthFail(conn, "Username Already In Use") // TODO: Should request deletion of user, leaning on timeout. return } // This connection is successfully authenticated with that session. conn.session = newSessionId // If there is an existing connection associated with that session, // drop it. if existingConn := sessions[newSessionId]; existingConn != nil { existingConn.conn.Close() } sessions[conn.session] = conn sendAuthSuccess(conn, waitingAuth.password) }
// Main function for the handling goroutine for conn. func handleConn(conn *userConn) { // Do cleanup in a defer, so if they crash us we still tidy up here. // A small nod towards tolerating bad input, with much more needed. defer func() { // Remove the connection from our connection set if present. // It will not be present if we are degraded or similar. connectionsLock.Lock() if _, exists := connections[conn]; exists { delete(connections, conn) } // Remove the connection from our waiting map if present. // This must happen before session removal, as otherwise // auth could complete between the session check and this one. waitingLock.Lock() if conn.waitingAuth != nil { waitingConn := waiting[conn.waitingAuth.requestId] if waitingConn == conn { delete(waiting, conn.waitingAuth.requestId) } } waitingLock.Unlock() // Remove the connection from our sessions map if present. // It will not be present if we are degraded, // or have replaced this connection. sessionsLock.Lock() if conn.session != 0 { sessionConn := sessions[conn.session] if sessionConn == conn { delete(sessions, conn.session) // Send a change detaching this node // from that session. idStr := strconv.FormatUint(uint64(config.Id()), 10) ourAttachStr := "attach " + idStr chset := make([]store.Change, 1) chset[0].TargetEntity = conn.session chset[0].Key = ourAttachStr chset[0].Value = "" req := makeRequest(chset) go chrequest.Request(req) } } sessionsLock.Unlock() connectionsLock.Unlock() }() for { select { case msg, ok := <-conn.conn.Received: if !ok { return } switch *msg.MsgType { case 2: handleAuth(conn, msg.Content) case 3: handleFollowUsername(conn, msg.Content) case 4: handleFollowUser(conn, msg.Content) case 5: handleStopFollowingUser(conn, msg.Content) case 6: handleSend(conn, msg.Content) default: conn.conn.Close() } case userMsg := <-conn.deliver: // Get the sender's user ID. store.StartTransaction() attachedTo := store.AllAttachedTo(userMsg.Sender) store.EndTransaction() // We don't know who the sender is. Drop message. if len(attachedTo) == 0 { break } // Deliver any user messages given to us. var deliverMsg cliproto_down.Received deliverMsg.SenderUserId = new(uint64) deliverMsg.SenderSessionId = new(uint64) deliverMsg.Tag = new(string) deliverMsg.Content = new(string) *deliverMsg.SenderUserId = attachedTo[0] *deliverMsg.SenderSessionId = userMsg.Sender *deliverMsg.Tag = userMsg.Tag *deliverMsg.Content = userMsg.Content conn.conn.SendProto(10, &deliverMsg) } } }