diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fa587d1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +custom: https://aceberg.github.io/MyDocs/sponsor diff --git a/internal/db/db-connect.go b/internal/db/db-connect.go index 242c2fa..484164d 100644 --- a/internal/db/db-connect.go +++ b/internal/db/db-connect.go @@ -20,6 +20,7 @@ func dbExec(path, sqlStatement string) { check.IfError(err) } +// Select - select all hosts func Select(path string) (dbHosts []models.Host) { db, _ := sql.Open("sqlite", path) defer db.Close() @@ -38,8 +39,8 @@ func Select(path string) (dbHosts []models.Host) { if err != nil { log.Fatal(err) } - // oneHost.Name = unquote_str(oneHost.Name) - // oneHost.Hw = unquote_str(oneHost.Hw) + oneHost.Name = unquoteStr(oneHost.Name) + oneHost.Hw = unquoteStr(oneHost.Hw) dbHosts = append(dbHosts, oneHost) } diff --git a/internal/db/db-edit.go b/internal/db/db-edit.go index 1f9084e..1756094 100644 --- a/internal/db/db-edit.go +++ b/internal/db/db-edit.go @@ -1,9 +1,11 @@ package db import ( + "fmt" "log" "os" - // "fmt" + + "github.com/aceberg/WatchYourLAN/internal/models" ) // Create - create DB if not exists @@ -26,7 +28,32 @@ func Create(path string) { } } +// SetNow - mark all hosts as offline func SetNow(path string) { sqlStatement := `UPDATE "now" set NOW = '0';` dbExec(path, sqlStatement) } + +// Insert - insert host into table +func Insert(path string, oneHost models.Host) { + oneHost.Name = quoteStr(oneHost.Name) + oneHost.Hw = quoteStr(oneHost.Hw) + sqlStatement := `INSERT INTO "now" (NAME, IP, MAC, HW, DATE, KNOWN, NOW) + VALUES ('%s','%s','%s','%s','%s','%d','%d');` + sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now) + //fmt.Println("Insert statement:", sqlStatement) + dbExec(path, sqlStatement) +} + +// Update - update host +func Update(path string, oneHost models.Host) { + oneHost.Name = quoteStr(oneHost.Name) + oneHost.Hw = quoteStr(oneHost.Hw) + sqlStatement := `UPDATE "now" set + NAME = '%s', IP = '%s', MAC = '%s', HW = '%s', DATE = '%s', + KNOWN = '%d', NOW = '%d' + WHERE ID = '%d';` + sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now, oneHost.ID) + //fmt.Println("Update statement:", sqlStatement) + dbExec(path, sqlStatement) +} diff --git a/internal/db/quote_str.go b/internal/db/quote_str.go new file mode 100644 index 0000000..d577812 --- /dev/null +++ b/internal/db/quote_str.go @@ -0,0 +1,11 @@ +package db + +import "strings" + +func quoteStr(str string) string { + return strings.ReplaceAll(str, "'", "''") +} + +func unquoteStr(str string) string { + return strings.ReplaceAll(str, "''", "'") +} diff --git a/internal/scan/compare.go b/internal/scan/compare.go index 73da000..9fa4dcd 100644 --- a/internal/scan/compare.go +++ b/internal/scan/compare.go @@ -1,33 +1,33 @@ package scan import ( - // "fmt" + "fmt" "log" + "github.com/aceberg/WatchYourLAN/internal/db" "github.com/aceberg/WatchYourLAN/internal/models" ) -// func host_in_db(host Host, dbHosts []Host) bool { // Check if host is already in DB -// for _, oneHost := range dbHosts { -// if host.Ip == oneHost.Ip && host.Mac == oneHost.Mac && host.Hw == oneHost.Hw { -// oneHost.Date = host.Date -// oneHost.Now = 1 -// db_update(oneHost) -// return true -// } -// } -// return false -// } +func hostInDB(path string, host models.Host, dbHosts []models.Host) bool { // Check if host is already in DB + for _, oneHost := range dbHosts { + if host.IP == oneHost.IP && host.Mac == oneHost.Mac && host.Hw == oneHost.Hw { + oneHost.Date = host.Date + oneHost.Now = 1 + db.Update(path, oneHost) + return true + } + } + return false +} -func hostsCompare(foundHosts []models.Host, dbHosts []models.Host) { +func hostsCompare(path string, foundHosts, dbHosts []models.Host) { for _, oneHost := range foundHosts { - // if !(host_in_db(oneHost, dbHosts)) { - // oneHost.Now = 1 // Mark host online - // msg := fmt.Sprintf("UNKNOWN HOST IP: '%s', MAC: '%s', Hw: '%s'", oneHost.Ip, oneHost.Mac, oneHost.Hw) - // log.Println("WARN:", msg) - // shoutr_notify(msg) // Notify through Shoutrrr - // db_insert(oneHost) - // } - log.Println("FOUND:", oneHost) + if !(hostInDB(path, oneHost, dbHosts)) { + oneHost.Now = 1 // Mark host online + msg := fmt.Sprintf("UNKNOWN HOST IP: '%s', MAC: '%s', Hw: '%s'", oneHost.IP, oneHost.Mac, oneHost.Hw) + log.Println("WARN:", msg) + // shoutr_notify(msg) // Notify through Shoutrrr + db.Insert(path, oneHost) + } } } diff --git a/internal/scan/start.go b/internal/scan/start.go index f962291..7593f26 100644 --- a/internal/scan/start.go +++ b/internal/scan/start.go @@ -7,15 +7,15 @@ import ( "github.com/aceberg/WatchYourLAN/internal/models" ) +// Start - start arp-scan goroutine func Start(appConfig models.Conf) { var foundHosts []models.Host var dbHosts []models.Host for { // Endless - foundHosts = arpScan(appConfig.Iface) // Scan interfaces - dbHosts = db.Select(appConfig.DbPath) // Select everything from DB - db.SetNow(appConfig.DbPath) // Mark hosts in DB as offline - hostsCompare(foundHosts, dbHosts) // Compare hosts online and in DB - // and add them to DB + foundHosts = arpScan(appConfig.Iface) // Scan interfaces + dbHosts = db.Select(appConfig.DbPath) // Select everything from DB + db.SetNow(appConfig.DbPath) // Mark hosts in DB as offline + hostsCompare(appConfig.DbPath, foundHosts, dbHosts) // Compare hosts online and in DB and add them to DB time.Sleep(time.Duration(appConfig.Timeout) * time.Second) // Timeout } } diff --git a/internal/web/index.go b/internal/web/index.go index 48a1037..f77060e 100644 --- a/internal/web/index.go +++ b/internal/web/index.go @@ -6,12 +6,15 @@ import ( "net/http" "github.com/aceberg/WatchYourLAN/internal/check" + "github.com/aceberg/WatchYourLAN/internal/db" "github.com/aceberg/WatchYourLAN/internal/models" ) -func index(w http.ResponseWriter, r *http.Request) { +func indexHandler(w http.ResponseWriter, r *http.Request) { var guiData models.GuiData + AllHosts = db.Select(AppConfig.DbPath) + guiData.Config = AppConfig guiData.Hosts = AllHosts guiData.Icon = Icon diff --git a/internal/web/on-off-line.go b/internal/web/on-off-line.go new file mode 100644 index 0000000..f5147e2 --- /dev/null +++ b/internal/web/on-off-line.go @@ -0,0 +1,49 @@ +package web + +import ( + "html/template" + "net/http" + + "github.com/aceberg/WatchYourLAN/internal/check" + "github.com/aceberg/WatchYourLAN/internal/models" +) + +func offlineHandler(w http.ResponseWriter, r *http.Request) { + var guiData models.GuiData + + guiData.Config = AppConfig + guiData.Hosts = []models.Host{} + guiData.Icon = Icon + + for _, oneHost := range AllHosts { + if oneHost.Now == 0 { + guiData.Hosts = append(guiData.Hosts, oneHost) + } + } + + tmpl, _ := template.ParseFiles(TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html") + err := tmpl.ExecuteTemplate(w, "header", guiData) + check.IfError(err) + err = tmpl.ExecuteTemplate(w, "offline", guiData) + check.IfError(err) +} + +func onlineHandler(w http.ResponseWriter, r *http.Request) { + var guiData models.GuiData + + guiData.Config = AppConfig + guiData.Hosts = []models.Host{} + guiData.Icon = Icon + + for _, oneHost := range AllHosts { + if oneHost.Now == 1 { + guiData.Hosts = append(guiData.Hosts, oneHost) + } + } + + tmpl, _ := template.ParseFiles(TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html") + err := tmpl.ExecuteTemplate(w, "header", guiData) + check.IfError(err) + err = tmpl.ExecuteTemplate(w, "offline", guiData) + check.IfError(err) +} diff --git a/internal/web/sort.go b/internal/web/sort.go new file mode 100644 index 0000000..872d1df --- /dev/null +++ b/internal/web/sort.go @@ -0,0 +1,82 @@ +package web + +import ( + "bytes" + "net" + "net/http" + "sort" + + "github.com/aceberg/WatchYourLAN/internal/db" + "github.com/aceberg/WatchYourLAN/internal/models" +) + +func sortByIPs(method string) { + type ipHost struct { + Host models.Host + IP net.IP + } + toSort := []ipHost{} + var oneSort ipHost + + for _, oneHost := range AllHosts { + oneSort.Host = oneHost + oneSort.IP = net.ParseIP(oneHost.IP) + toSort = append(toSort, oneSort) + } + + switch method { + case "asc": + sort.Slice(toSort, func(i, j int) bool { + return bytes.Compare(toSort[i].IP, toSort[j].IP) < 0 + }) + case "desc": + sort.Slice(toSort, func(i, j int) bool { + return bytes.Compare(toSort[i].IP, toSort[j].IP) > 0 + }) + } + + AllHosts = []models.Host{} + for _, oneSort := range toSort { + AllHosts = append(AllHosts, oneSort.Host) + } +} + +func sortHandler(w http.ResponseWriter, r *http.Request) { + + sortMethod := r.FormValue("sort_method") + + switch sortMethod { + case "name-up": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Name < AllHosts[j].Name + }) + case "name-down": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Name > AllHosts[j].Name + }) + case "ip-up": + sortByIPs("asc") + case "ip-down": + sortByIPs("desc") + case "date-up": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Date < AllHosts[j].Date + }) + case "date-down": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Date > AllHosts[j].Date + }) + case "known-up": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Known < AllHosts[j].Known + }) + case "known-down": + sort.SliceStable(AllHosts, func(i, j int) bool { + return AllHosts[i].Known > AllHosts[j].Known + }) + default: + AllHosts = db.Select(AppConfig.DbPath) + } + + http.Redirect(w, r, r.Header.Get("Referer"), 302) +} diff --git a/internal/web/templates/header.html b/internal/web/templates/header.html index 2fd27fd..86df39b 100644 --- a/internal/web/templates/header.html +++ b/internal/web/templates/header.html @@ -5,14 +5,10 @@ - - - -