diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 8a0c4f1..4cca171 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -94,7 +94,121 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n//# sourceURL=webpack:///./src/index.jsx?"); + + +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BathroomComponent extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + doors: {} + }; + } + + componentDidMount() { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + + componentWillUnmount() { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + + async updateDoors(msg) { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } + + render() { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = Math.floor(100 / columns) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + + for (var i = 0; i < keys.length + /*(Math.floor(keys.length / 6) + 1) * 6*/ + ; i++) { + var img = null; + + if (i < keys.length) { + var door = this.state.doors[keys[i]]; + var imgFile = null; + + switch (door) { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + + img = _react.default.createElement("img", { + src: imgFile, + width: "100%", + style: { + "padding": "2px" + } + }); + } + + row.push(_react.default.createElement("td", { + width: widthPx + }, img)); + + if (i % columns == columns - 1 || i == keys.length - 1) { + elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + row.length = 0; + } + } + + return (// + _react.default.createElement("div", { + style: { + "text-align": "center", + width: "100%", + "padding-top": "10px" + } + }, _react.default.createElement("table", { + style: { + "margin-left": "auto", + "margin-right": "auto" + } + }, elems)) + ); + } + +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin { + initialize(registry, store) { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } + +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); /***/ }), @@ -105,8 +219,21 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! ./src/index.jsx */\"./src/index.jsx\");\n\n\n//# sourceURL=webpack:///multi_./src/index.jsx?"); +module.exports = __webpack_require__(/*! ./src/index.jsx */"./src/index.jsx"); + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = React; /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 8a0c4f1..4cca171 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -94,7 +94,121 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n//# sourceURL=webpack:///./src/index.jsx?"); + + +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BathroomComponent extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + doors: {} + }; + } + + componentDidMount() { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + + componentWillUnmount() { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + + async updateDoors(msg) { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } + + render() { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = Math.floor(100 / columns) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + + for (var i = 0; i < keys.length + /*(Math.floor(keys.length / 6) + 1) * 6*/ + ; i++) { + var img = null; + + if (i < keys.length) { + var door = this.state.doors[keys[i]]; + var imgFile = null; + + switch (door) { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + + img = _react.default.createElement("img", { + src: imgFile, + width: "100%", + style: { + "padding": "2px" + } + }); + } + + row.push(_react.default.createElement("td", { + width: widthPx + }, img)); + + if (i % columns == columns - 1 || i == keys.length - 1) { + elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + row.length = 0; + } + } + + return (//
+ _react.default.createElement("div", { + style: { + "text-align": "center", + width: "100%", + "padding-top": "10px" + } + }, _react.default.createElement("table", { + style: { + "margin-left": "auto", + "margin-right": "auto" + } + }, elems)) + ); + } + +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin { + initialize(registry, store) { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } + +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); /***/ }), @@ -105,8 +219,21 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! ./src/index.jsx */\"./src/index.jsx\");\n\n\n//# sourceURL=webpack:///multi_./src/index.jsx?"); +module.exports = __webpack_require__(/*! ./src/index.jsx */"./src/index.jsx"); + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = React; /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map new file mode 100644 index 0000000..26114e4 --- /dev/null +++ b/mattermost/webapp/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;;;AClFA;;;;AAEA,MAAMA,iBAAN,SAAgCC,eAAMC,SAAtC,CACA;AACIC,aAAW,CAACC,KAAD,EACX;AACI,UAAMA,KAAN;AACA,SAAKC,KAAL,GAAa;AAACC,WAAK,EAAC;AAAP,KAAb;AACH;;AACDC,mBAAiB,GACjB;AACIC,YAAQ,CAACC,6BAAT,CAAuC,wCAAvC,EAAiF,KAAKC,WAAL,CAAiBC,IAAjB,CAAsB,IAAtB,CAAjF;AACA,SAAKD,WAAL;AACH;;AACDE,sBAAoB,GACpB;AACIJ,YAAQ,CAACK,+BAAT,CAAyC,wCAAzC;AACH;;AACD,QAAMH,WAAN,CAAkBI,GAAlB,EACA;AACI,QAAIC,GAAG,GAAGC,KAAK,CAACC,QAAN,GAAiBC,QAAjB,CAA0BC,OAA1B,CAAkCC,MAAlC,CAAyCC,OAAzC,GAAmD,yCAA7D;AACA,QAAIC,GAAG,GAAG,MAAMC,KAAK,CAACR,GAAD,CAArB;AACA,QAAIS,IAAI,GAAG,MAAMF,GAAG,CAACE,IAAJ,EAAjB;AACA,SAAKC,QAAL,CAAc;AAAC,eAAQD;AAAT,KAAd;AACH;;AACDE,QAAM,GACN;AACI,QAAI,CAAC,KAAKrB,KAAL,CAAWC,KAAhB,EAAuB;AACvB,QAAIqB,OAAO,GAAG,CAAd;AACA,QAAIC,IAAI,GAAGC,MAAM,CAACD,IAAP,CAAY,KAAKvB,KAAL,CAAWC,KAAvB,EAA8BwB,IAA9B,EAAX;AACA,QAAIC,KAAK,GAAIC,IAAI,CAACC,KAAL,CAAW,MAAMN,OAAjB,CAAD,GAA8B,GAA1C;AACA,QAAIO,OAAO,GAAG,MAAd;AACA,QAAIC,KAAK,GAAG,EAAZ;AACA,QAAIC,GAAG,GAAG,EAAV;;AACA,SAAK,IAAIC,CAAC,GAAG,CAAb,EAAgBA,CAAC,GAAGT,IAAI,CAACU;AAAO;AAAhC,MAA2ED,CAAC,EAA5E,EACA;AACI,UAAIE,GAAG,GAAG,IAAV;;AACA,UAAIF,CAAC,GAAGT,IAAI,CAACU,MAAb,EACA;AACI,YAAIE,IAAI,GAAG,KAAKnC,KAAL,CAAWC,KAAX,CAAiBsB,IAAI,CAACS,CAAD,CAArB,CAAX;AACA,YAAII,OAAO,GAAG,IAAd;;AACA,gBAAQD,IAAR;AAEI;AACA,eAAK,SAAL;AACIC,mBAAO,GAAG,wBAAV;AACA;;AACJ,eAAK,MAAL;AACIA,mBAAO,GAAG,yBAAV;AACA;;AACJ,eAAK,QAAL;AACIA,mBAAO,GAAG,6BAAV;AACA;AAXR;;AAaAF,WAAG,GAAG;AAAK,aAAG,EAAEE,OAAV;AAAmB,eAAK,EAAC,MAAzB;AAAgC,eAAK,EAAE;AAAC,uBAAU;AAAX;AAAvC,UAAN;AACH;;AACDL,SAAG,CAACM,IAAJ,CAAS;AAAI,aAAK,EAAER;AAAX,SAAqBK,GAArB,CAAT;;AACA,UAAIF,CAAC,GAAGV,OAAJ,IAAeA,OAAO,GAAG,CAAzB,IAA8BU,CAAC,IAAIT,IAAI,CAACU,MAAL,GAAc,CAArD,EACA;AACIH,aAAK,CAACO,IAAN,CAAW,6BAAC,cAAD,CAAO,QAAP,QAAiB,CAAC,GAAGN,GAAJ,CAAjB,CAAX;AACAA,WAAG,CAACE,MAAJ,GAAa,CAAb;AACH;AACJ;;AACD,WACI;AACA;AAAK,aAAK,EAAE;AAAC,wBAAc,QAAf;AAAyBP,eAAK,EAAC,MAA/B;AAAuC,yBAAc;AAArD;AAAZ,SACA;AAAO,aAAK,EAAE;AAAC,yBAAc,MAAf;AAAuB,0BAAe;AAAtC;AAAd,SACCI,KADD,CADA;AAFJ;AAQH;;AApEL;;AAuEA,IAAI3B,QAAQ,GAAG,IAAf;AACA,IAAIQ,KAAK,GAAG,IAAZ;;AAEA,MAAM2B,qBAAN,CACA;AACIC,YAAU,CAACC,QAAD,EAAWC,KAAX,EACV;AACItC,YAAQ,GAAGqC,QAAX;AACA7B,SAAK,GAAG8B,KAAR;AACAD,YAAQ,CAACE,kCAAT,CAA4C/C,iBAA5C;AACH;;AANL;;AASAgD,MAAM,CAACC,cAAP,CAAsB,yBAAtB,EAAiD,IAAIN,qBAAJ,EAAjD,E;;;;;;;;;;;;;;;;;;;;;;;ACvFA,uB","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push();\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //
{img}
\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 8a0c4f1..4cca171 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -94,7 +94,121 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n//# sourceURL=webpack:///./src/index.jsx?"); + + +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BathroomComponent extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + doors: {} + }; + } + + componentDidMount() { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + + componentWillUnmount() { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + + async updateDoors(msg) { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } + + render() { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = Math.floor(100 / columns) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + + for (var i = 0; i < keys.length + /*(Math.floor(keys.length / 6) + 1) * 6*/ + ; i++) { + var img = null; + + if (i < keys.length) { + var door = this.state.doors[keys[i]]; + var imgFile = null; + + switch (door) { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + + img = _react.default.createElement("img", { + src: imgFile, + width: "100%", + style: { + "padding": "2px" + } + }); + } + + row.push(_react.default.createElement("td", { + width: widthPx + }, img)); + + if (i % columns == columns - 1 || i == keys.length - 1) { + elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + row.length = 0; + } + } + + return (// + _react.default.createElement("div", { + style: { + "text-align": "center", + width: "100%", + "padding-top": "10px" + } + }, _react.default.createElement("table", { + style: { + "margin-left": "auto", + "margin-right": "auto" + } + }, elems)) + ); + } + +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin { + initialize(registry, store) { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } + +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); /***/ }), @@ -105,8 +219,21 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! ./src/index.jsx */\"./src/index.jsx\");\n\n\n//# sourceURL=webpack:///multi_./src/index.jsx?"); +module.exports = __webpack_require__(/*! ./src/index.jsx */"./src/index.jsx"); + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = React; /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map new file mode 100644 index 0000000..26114e4 --- /dev/null +++ b/mattermost/webapp/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push();\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //
{img}
\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/webapp/src/index.jsx b/mattermost/webapp/src/index.jsx index e69de29..0868e33 100644 --- a/mattermost/webapp/src/index.jsx +++ b/mattermost/webapp/src/index.jsx @@ -0,0 +1,88 @@ +import React from 'react'; + +class BathroomComponent extends React.Component +{ + constructor(props) + { + super(props); + this.state = {doors:{}}; + } + componentDidMount() + { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + componentWillUnmount() + { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + async updateDoors(msg) + { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({"doors":json}); + } + render() + { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = (Math.floor(100 / columns)) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++) + { + var img = null; + if (i < keys.length) + { + var door = this.state.doors[keys[i]]; + var imgFile = null; + switch (door) + { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + img = + } + row.push({img}); + if (i % columns == columns - 1 || i == keys.length - 1) + { + elems.push({[...row]}); + row.length = 0; + } + } + return ( + // +
+
+ {elems} +
+ + ); + } +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin +{ + initialize(registry, store) + { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 8a0c4f1..4cca171 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -94,7 +94,121 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n//# sourceURL=webpack:///./src/index.jsx?"); + + +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BathroomComponent extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + doors: {} + }; + } + + componentDidMount() { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + + componentWillUnmount() { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + + async updateDoors(msg) { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } + + render() { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = Math.floor(100 / columns) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + + for (var i = 0; i < keys.length + /*(Math.floor(keys.length / 6) + 1) * 6*/ + ; i++) { + var img = null; + + if (i < keys.length) { + var door = this.state.doors[keys[i]]; + var imgFile = null; + + switch (door) { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + + img = _react.default.createElement("img", { + src: imgFile, + width: "100%", + style: { + "padding": "2px" + } + }); + } + + row.push(_react.default.createElement("td", { + width: widthPx + }, img)); + + if (i % columns == columns - 1 || i == keys.length - 1) { + elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + row.length = 0; + } + } + + return (// + _react.default.createElement("div", { + style: { + "text-align": "center", + width: "100%", + "padding-top": "10px" + } + }, _react.default.createElement("table", { + style: { + "margin-left": "auto", + "margin-right": "auto" + } + }, elems)) + ); + } + +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin { + initialize(registry, store) { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } + +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); /***/ }), @@ -105,8 +219,21 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! ./src/index.jsx */\"./src/index.jsx\");\n\n\n//# sourceURL=webpack:///multi_./src/index.jsx?"); +module.exports = __webpack_require__(/*! ./src/index.jsx */"./src/index.jsx"); + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = React; /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map new file mode 100644 index 0000000..26114e4 --- /dev/null +++ b/mattermost/webapp/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push();\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //
{img}
\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/webapp/src/index.jsx b/mattermost/webapp/src/index.jsx index e69de29..0868e33 100644 --- a/mattermost/webapp/src/index.jsx +++ b/mattermost/webapp/src/index.jsx @@ -0,0 +1,88 @@ +import React from 'react'; + +class BathroomComponent extends React.Component +{ + constructor(props) + { + super(props); + this.state = {doors:{}}; + } + componentDidMount() + { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + componentWillUnmount() + { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + async updateDoors(msg) + { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({"doors":json}); + } + render() + { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = (Math.floor(100 / columns)) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++) + { + var img = null; + if (i < keys.length) + { + var door = this.state.doors[keys[i]]; + var imgFile = null; + switch (door) + { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + img = + } + row.push({img}); + if (i % columns == columns - 1 || i == keys.length - 1) + { + elems.push({[...row]}); + row.length = 0; + } + } + return ( + // +
+
+ {elems} +
+ + ); + } +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin +{ + initialize(registry, store) + { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); diff --git a/mattermost/webapp/webpack.config.js b/mattermost/webapp/webpack.config.js index 57121e1..b7e2419 100644 --- a/mattermost/webapp/webpack.config.js +++ b/mattermost/webapp/webpack.config.js @@ -4,6 +4,7 @@ entry: [ './src/index.jsx', ], + devtool: 'source-map', resolve: { modules: [ 'src', diff --git a/.gitignore b/.gitignore index 1377554..3819313 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +*.swo diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz new file mode 100644 index 0000000..246e3c9 --- /dev/null +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/install_deps.sh b/mattermost/install_deps.sh new file mode 100644 index 0000000..6a3cec5 --- /dev/null +++ b/mattermost/install_deps.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash + +sudo apt-get install filepp diff --git a/mattermost/package.sh b/mattermost/package.sh new file mode 100644 index 0000000..7253322 --- /dev/null +++ b/mattermost/package.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +cd "$( dirname "${BASH_SOURCE[0]}" )" +tar -czvf bathroom.tar.gz plugin.json -C server bathroom-linux-amd64 -C ../webapp/dist main.js diff --git a/mattermost/plugin.json b/mattermost/plugin.json new file mode 100644 index 0000000..ba3a186 --- /dev/null +++ b/mattermost/plugin.json @@ -0,0 +1,17 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json new file mode 100644 index 0000000..634b204 --- /dev/null +++ b/mattermost/plugin_templ.json @@ -0,0 +1,21 @@ +{ + "id": "com.mattermost.bathroom", + "name": "Bathroom Monitor", + "server": { + "executable": "bathroom-linux-amd64" + }, + "webapp": { + "bundle_path": "main.js" + }, + "settings_schema": { + "settings": [ + {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, +#ifdef fsnotify + {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#else + {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, +#endif + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + ] + } +} diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 681e00f..c478ac9 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 225a000..b3512ab 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -30,6 +30,8 @@ "encoding/base64" ) +const DO_LOGGING = false + var userSplit *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) const ( @@ -139,6 +141,12 @@ } } +func (p *BathroomMonitorPlugin) log(log string) { + if DO_LOGGING { + p.API.LogInfo(log) + } +} + func (p *BathroomMonitorPlugin) setDoorStatus(id uint8, status uint, report bool) error { if id < 1 || id > p.config.numDoors { return errors.New(fmt.Sprintf("Invalid door id %d", id)) @@ -156,8 +164,12 @@ p.doors[id - 1].status = status if report { statusStr, _ := statusName(status) - p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + _ = statusStr + //p.postAdminChannel(fmt.Sprintf("STATUS: door %d = %s", id, statusStr)) + p.API.PublishWebSocketEvent("updated", map[string]interface{}{}, &model.WebsocketBroadcast{}) } + } else { + p.log(fmt.Sprintf("Asked to change status from %d to %d ?", p.doors[id - 1].status, status)) } return nil @@ -186,12 +198,12 @@ p.configLock.Unlock() - doorVal, ok := r.Form["door_id"] - if !ok || len(doorVal) == 0 { + doorStr, ok := r.Form["door_id"] + if !ok || len(doorStr) == 0 { return 0, errors.New("Please send door id") } - doorId, err := strconv.ParseUint(doorVal[0], 10, 8) + doorId, err := strconv.ParseUint(doorStr[0], 10, 8) if err != nil { return 0, errors.New(fmt.Sprintf("Couldn't parse door_id: %s", err.Error())) } @@ -205,7 +217,7 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.API.LogInfo(fmt.Sprintf("Requested path: %s", r.URL.Path)) + p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -224,7 +236,7 @@ } if r.URL.Path == "/status-update" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { @@ -232,13 +244,13 @@ return } - statusVal, ok := r.Form["status"] - if !ok || len(statusVal) == 0 { + statusStr, ok := r.Form["status"] + if !ok || len(statusStr) == 0 { fmt.Fprint(w, "Please send door status") return } - status, err := strconv.ParseUint(statusVal[0], 10, 8) + status, err := strconv.ParseUint(statusStr[0], 10, 8) if err != nil { fmt.Fprintf(w, "Couldn't parse status: %s", err.Error()) return @@ -249,6 +261,11 @@ return } + statusVal := Open + if status == 1 { + statusVal = Closed + } + p.doorLock.Lock() defer p.doorLock.Unlock() @@ -257,34 +274,34 @@ return } - p.API.LogInfo("Getting random bytes") + p.log("Getting random bytes") var verifyBytes [50]byte rand.Read(verifyBytes[:]) - p.API.LogInfo("Encoding random bytes") + p.log("Encoding random bytes") verifyB64 := base64.StdEncoding.EncodeToString(verifyBytes[:]) - p.API.LogInfo("Encryping random bytes") + p.log("Encryping random bytes") encrypted, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, p.doors[doorId8 - 1].pubKey, verifyBytes[:], []byte{}) if err != nil { fmt.Fprintf(w, "Couldn't encrypt verification: %s", err.Error()) return } - p.API.LogInfo("Encoding encrypted bytes") + p.log("Encoding encrypted bytes") encryptedB64 := base64.StdEncoding.EncodeToString(encrypted) req := &DoorRequest { - ip:r.RemoteAddr, + ip:c.IpAddress, time:time.Now().Unix(), verify:verifyB64, - status: uint(status), + status: statusVal, } p.doors[doorId8 - 1].lastRequest = req - p.API.LogInfo(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) + p.log(fmt.Sprintf("NEW REQUEST SAVED: %s %d %s", req.ip, req.time, req.verify)) fmt.Fprint(w, encryptedB64) @@ -293,7 +310,7 @@ } if r.URL.Path == "/verify-status" { r.ParseForm() - p.API.LogInfo("Contacted by " + r.RemoteAddr) + p.log("Contacted by " + r.RemoteAddr) doorId8, err := p.validateRequestDoorId(r) if err != nil { fmt.Fprint(w, err.Error()) @@ -306,8 +323,10 @@ return } + p.log("Verify locking") p.doorLock.Lock() defer p.doorLock.Unlock() + p.log("Verify done locking") if p.doors[doorId8 - 1].lastRequest == nil { fmt.Fprint(w, "Invalid request") @@ -315,7 +334,7 @@ } req := p.doors[doorId8 - 1].lastRequest - if req.ip != r.RemoteAddr { + if req.ip != c.IpAddress { fmt.Fprintf(w, "Not your request %s %s", req.ip, r.RemoteAddr) return } @@ -328,17 +347,20 @@ if req.verify != verifyB64[0] { + p.log(fmt.Sprintf("Failed verification %s %s", req.verify, verifyB64[0])) fmt.Fprint(w, "Unauthorized request") return } - p.doorLock.Lock() - defer p.doorLock.Unlock() - p.doors[doorId8 - 1].lastRequest = nil - p.setDoorStatus(doorId8, req.status, true) + p.log("Changing") + err = p.setDoorStatus(doorId8, req.status, true) + p.log("Changed") + if err != nil { + fmt.Fprintf(w, "Couldn't set status %d %d: %s", doorId8, req.status, err.Error()) + } return @@ -463,7 +485,7 @@ p.initDoors() - p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) + p.log(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.numDoors, p.config.WatchPath)) if configErr != nil { p.postAdminChannel(configErr.Error()) @@ -562,7 +584,7 @@ } } case <- p.configChanged: - p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) + p.log(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) run = false } } diff --git a/mattermost/server/bathroom.tar.gz b/mattermost/server/bathroom.tar.gz deleted file mode 100644 index f2298e9..0000000 --- a/mattermost/server/bathroom.tar.gz +++ /dev/null Binary files differ diff --git a/mattermost/server/build.sh b/mattermost/server/build.sh index 916a360..c2c1a0e 100755 --- a/mattermost/server/build.sh +++ b/mattermost/server/build.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash -filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) plugin_templ.json > plugin.json +filepp $( [[ ! -z "$DEFS" ]] && echo -D$DEFS ) ../plugin_templ.json > ../plugin.json GOOS=linux GOARCH=amd64 go build $( [[ ! -z "$DEFS" ]] && echo -tags $DEFS ) -o bathroom-linux-amd64 bathroom.go $FILES diff --git a/mattermost/server/package.sh b/mattermost/server/package.sh deleted file mode 100755 index c8ce874..0000000 --- a/mattermost/server/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /usr/bin/env bash - -tar -czvf bathroom.tar.gz bathroom-linux-amd64 plugin.json diff --git a/mattermost/server/plugin.json b/mattermost/server/plugin.json deleted file mode 100644 index e4d22cb..0000000 --- a/mattermost/server/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/server/plugin_templ.json b/mattermost/server/plugin_templ.json deleted file mode 100644 index 1de221a..0000000 --- a/mattermost/server/plugin_templ.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "com.mattermost.bathroom", - "name": "Bathroom Monitor", - "server": { - "executable": "bathroom-linux-amd64" - }, - "settings_schema": { - "settings": [ - {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, -#ifdef fsnotify - {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, -#endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} - ] - } -} diff --git a/mattermost/webapp/bathroom.tar.gz b/mattermost/webapp/bathroom.tar.gz new file mode 100644 index 0000000..48d8426 --- /dev/null +++ b/mattermost/webapp/bathroom.tar.gz Binary files differ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 8a0c4f1..4cca171 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -94,7 +94,121 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n//# sourceURL=webpack:///./src/index.jsx?"); + + +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BathroomComponent extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + doors: {} + }; + } + + componentDidMount() { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + + componentWillUnmount() { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + + async updateDoors(msg) { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } + + render() { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = Math.floor(100 / columns) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + + for (var i = 0; i < keys.length + /*(Math.floor(keys.length / 6) + 1) * 6*/ + ; i++) { + var img = null; + + if (i < keys.length) { + var door = this.state.doors[keys[i]]; + var imgFile = null; + + switch (door) { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + + img = _react.default.createElement("img", { + src: imgFile, + width: "100%", + style: { + "padding": "2px" + } + }); + } + + row.push(_react.default.createElement("td", { + width: widthPx + }, img)); + + if (i % columns == columns - 1 || i == keys.length - 1) { + elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + row.length = 0; + } + } + + return (// + _react.default.createElement("div", { + style: { + "text-align": "center", + width: "100%", + "padding-top": "10px" + } + }, _react.default.createElement("table", { + style: { + "margin-left": "auto", + "margin-right": "auto" + } + }, elems)) + ); + } + +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin { + initialize(registry, store) { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } + +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); /***/ }), @@ -105,8 +219,21 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! ./src/index.jsx */\"./src/index.jsx\");\n\n\n//# sourceURL=webpack:///multi_./src/index.jsx?"); +module.exports = __webpack_require__(/*! ./src/index.jsx */"./src/index.jsx"); + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = React; /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map new file mode 100644 index 0000000..26114e4 --- /dev/null +++ b/mattermost/webapp/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push();\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //
{img}
\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/webapp/src/index.jsx b/mattermost/webapp/src/index.jsx index e69de29..0868e33 100644 --- a/mattermost/webapp/src/index.jsx +++ b/mattermost/webapp/src/index.jsx @@ -0,0 +1,88 @@ +import React from 'react'; + +class BathroomComponent extends React.Component +{ + constructor(props) + { + super(props); + this.state = {doors:{}}; + } + componentDidMount() + { + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); + this.updateDoors(); + } + componentWillUnmount() + { + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); + } + async updateDoors(msg) + { + var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({"doors":json}); + } + render() + { + if (!this.state.doors) return; + var columns = 6; + var keys = Object.keys(this.state.doors).sort(); + var width = (Math.floor(100 / columns)) + "%"; + var widthPx = "23px"; + var elems = []; + var row = []; + for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++) + { + var img = null; + if (i < keys.length) + { + var door = this.state.doors[keys[i]]; + var imgFile = null; + switch (door) + { + default: + case "unknown": + imgFile = "/static/emoji/2753.png"; + break; + case "open": + imgFile = "/static/emoji/1f6bd.png"; + break; + case "closed": + imgFile = "/static/emoji/26d4-fe0f.png"; + break; + } + img = + } + row.push({img}); + if (i % columns == columns - 1 || i == keys.length - 1) + { + elems.push({[...row]}); + row.length = 0; + } + } + return ( + // +
+
+ {elems} +
+ + ); + } +} + +var Registry = null; +var Store = null; + +class BathroomMonitorPlugin +{ + initialize(registry, store) + { + Registry = registry; + Store = store; + registry.registerLeftSidebarHeaderComponent(BathroomComponent); + } +} + +window.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin()); diff --git a/mattermost/webapp/webpack.config.js b/mattermost/webapp/webpack.config.js index 57121e1..b7e2419 100644 --- a/mattermost/webapp/webpack.config.js +++ b/mattermost/webapp/webpack.config.js @@ -4,6 +4,7 @@ entry: [ './src/index.jsx', ], + devtool: 'source-map', resolve: { modules: [ 'src', diff --git a/pi/monitor.py b/pi/monitor.py index 5d37f82..18d9312 100644 --- a/pi/monitor.py +++ b/pi/monitor.py @@ -38,6 +38,32 @@ private_key = RSA.import_key(open("private.pem").read()) rsa = PKCS1_OAEP.new(private_key); +def report(val): + try: + url = url_base + "status-update" + ext + "?door_id=" + str(id) + "&status=" + ("1" if val else "0") + print " open " + url + response = urllib2.urlopen(url) + + responseStr = response.read() + print "Response: " + responseStr + enc_verify = base64.b64decode(responseStr) + + verify = rsa.decrypt(enc_verify) + + if ext == "": + verify = base64.b64encode(verify) + + print " verifying with: " + verify + + url = url_base + "verify-status" + ext + "?door_id=" + str(id) + "&verify=" + verify + print " open " + url + response = urllib2.urlopen(url) + print " returned: " + response.read() + except Exception as e: + print str(e) + traceback.print_tb(sys.exc_info()[2]) + +report(False) while True: now_pressed = b.is_pressed if now_pressed == status: @@ -48,31 +74,8 @@ now_pressed = b.is_pressed if now_pressed != status: status = now_pressed - print "status now: " + str(status) - try: - url = url_base + "status-update" + ext + "?door_id=" + str(id) + "&status=" + ("1" if status else "0") - print " open " + url - response = urllib2.urlopen(url) - - responseStr = response.read() - print "Response: " + responseStr - enc_verify = base64.b64decode(responseStr) - - verify = rsa.decrypt(enc_verify) - - if ext == "": - verify = base64.b64encode(verify) - - print " verifying with: " + verify - - url = url_base + "verify-status" + ext + "?door_id=" + str(id) + "&verify=" + urllib.quote(verify) - print " open " + url - response = urllib2.urlopen(url) - print " returned: " + response.read() - except Exception as e: - print str(e) - traceback.print_tb(sys.exc_info()[2]) + report(status) sleep(1)