package main
import (
"time"
"github.com/hrfee/mediabrowser"
)
type userDaemon struct {
Stopped bool
ShutdownChannel chan string
Interval time . Duration
period time . Duration
app * appContext
}
func newUserDaemon ( interval time . Duration , app * appContext ) * userDaemon {
return & userDaemon {
Stopped : false ,
ShutdownChannel : make ( chan string ) ,
Interval : interval ,
period : interval ,
app : app ,
}
}
func ( rt * userDaemon ) run ( ) {
rt . app . info . Println ( "User daemon started" )
for {
select {
case <- rt . ShutdownChannel :
rt . ShutdownChannel <- "Down"
return
case <- time . After ( rt . period ) :
break
}
started := time . Now ( )
rt . app . checkUsers ( )
finished := time . Now ( )
duration := finished . Sub ( started )
rt . period = rt . Interval - duration
}
}
func ( rt * userDaemon ) shutdown ( ) {
rt . Stopped = true
rt . ShutdownChannel <- "Down"
<- rt . ShutdownChannel
close ( rt . ShutdownChannel )
}
func ( app * appContext ) checkUsers ( ) {
if err := app . storage . loadUsers ( ) ; err != nil {
app . err . Printf ( "Failed to load user expiries: %v" , err )
return
}
app . storage . usersLock . Lock ( )
defer app . storage . usersLock . Unlock ( )
if len ( app . storage . users ) == 0 {
return
}
app . info . Println ( "Daemon: Checking for user expiry" )
users , status , err := app . jf . GetUsers ( false )
if err != nil || status != 200 {
app . err . Printf ( "Failed to get users (%d): %s" , status , err )
return
}
mode := "disable"
termPlural := "Disabling"
if app . config . Section ( "user_expiry" ) . Key ( "behaviour" ) . MustString ( "disable_user" ) == "delete_user" {
mode = "delete"
termPlural = "Deleting"
}
contact := false
if messagesEnabled && app . config . Section ( "user_expiry" ) . Key ( "send_email" ) . MustBool ( true ) {
contact = true
}
// Use a map to speed up checking for deleted users later
userExists := map [ string ] bool { }
for _ , user := range users {
userExists [ user . ID ] = true
}
for id , expiry := range app . storage . users {
if _ , ok := userExists [ id ] ; ! ok {
app . info . Printf ( "Deleting expiry for non-existent user \"%s\"" , id )
delete ( app . storage . users , id )
} else if time . Now ( ) . After ( expiry ) {
found := false
var user mediabrowser . User
for _ , u := range users {
if u . ID == id {
found = true
user = u
break
}
}
if ! found {
app . info . Printf ( "Expired user already deleted, ignoring." )
delete ( app . storage . users , id )
continue
}
app . info . Printf ( "%s expired user \"%s\"" , termPlural , user . Name )
if mode == "delete" {
status , err = app . jf . DeleteUser ( id )
} else if mode == "disable" {
user . Policy . IsDisabled = true
// Admins can't be disabled
user . Policy . IsAdministrator = false
status , err = app . jf . SetPolicy ( id , user . Policy )
}
if ! ( status == 200 || status == 204 ) || err != nil {
app . err . Printf ( "Failed to %s \"%s\" (%d): %s" , mode , user . Name , status , err )
continue
}
delete ( app . storage . users , id )
app . jf . CacheExpiry = time . Now ( )
if contact {
if ! ok {
continue
}
name := app . getAddressOrName ( user . ID )
msg , err := app . email . constructUserExpired ( app , false )
if err != nil {
app . err . Printf ( "Failed to construct expiry message for \"%s\": %s" , user . Name , err )
} else if err := app . sendByID ( msg , user . ID ) ; err != nil {
app . err . Printf ( "Failed to send expiry message to \"%s\": %s" , name , err )
} else {
app . info . Printf ( "Sent expiry notification to \"%s\"" , name )
}
}
}
}
err = app . storage . storeUsers ( )
if err != nil {
app . err . Printf ( "Failed to store user expiries: %s" , err )
}
}