package captiveportal import ( "sync" ) type Client struct { ID string Captive bool Lying bool OS OS } type Registry struct { mutex sync.RWMutex clients map[string]*Client } func (r *Registry) Touch(id string, os OS) { r.mutex.Lock() defer r.mutex.Unlock() client := r.upsert(id) client.OS = os } func (r *Registry) Lie(id string) { r.mutex.Lock() defer r.mutex.Unlock() client := r.upsert(id) client.Lying = true } func (r *Registry) Release(id string) { r.mutex.Lock() defer r.mutex.Unlock() client := r.upsert(id) client.Captive = false } func (r *Registry) IsCaptive(id string) bool { r.mutex.RLock() defer r.mutex.RUnlock() client, exists := r.clients[id] if !exists { return false } return client.Captive } func (r *Registry) ClientOS(id string) OS { r.mutex.RLock() defer r.mutex.RUnlock() client, exists := r.clients[id] if !exists { return OSUnknown } return client.OS } func (r *Registry) IsLying(id string) bool { r.mutex.RLock() defer r.mutex.RUnlock() client, exists := r.clients[id] if !exists { return false } return client.Lying } func (r *Registry) Delete(id string) { r.mutex.Lock() defer r.mutex.Unlock() delete(r.clients, id) } func (r *Registry) Clients() []Client { r.mutex.RLock() defer r.mutex.RUnlock() clients := make([]Client, 0, len(r.clients)) for _, c := range r.clients { clients = append(clients, *c) } return clients } func (r *Registry) upsert(id string) *Client { client, exists := r.clients[id] if !exists { client = &Client{ ID: id, Captive: true, Lying: false, OS: OSUnknown, } r.clients[id] = client } return client } func NewRegistry() *Registry { return &Registry{ clients: make(map[string]*Client), } }