package captiveportal import ( "sync" "time" ) type Client struct { LastSeen time.Time 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.LastSeen = time.Now() 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.Captive } func (r *Registry) upsert(id string) *Client { client, exists := r.clients[id] if !exists { client = &Client{ Captive: true, Lying: false, OS: OSUnknown, } r.clients[id] = client } return client } func NewRegistry() *Registry { return &Registry{ clients: make(map[string]*Client), } }