package main import ( "APIServer/cnf" "APIServer/ews" "encoding/json" "errors" "flag" "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "strings" "text/template" "time" "github.com/chai2010/winsvc" _ "github.com/go-sql-driver/mysql" "github.com/natefinch/lumberjack" uuid "github.com/satori/go.uuid" yaml "gopkg.in/yaml.v2" ) var cfg = cnf.Cfg{} var svc string = "apisvr" var vsn bool = false var ver string = "" var logger *log.Logger var flagIns bool = false var flagRmv bool = false var flagRun bool = false var flagTer bool = false var flagRst bool = false func init() { flag.BoolVar(&flagIns, "i", false, "Install Service") flag.BoolVar(&flagRmv, "u", false, "Uninstall Service") flag.BoolVar(&flagRun, "s", false, "Start Service") flag.BoolVar(&flagTer, "t", false, "Stop Service") flag.BoolVar(&flagRst, "r", false, "Restart Service") flag.BoolVar(&vsn, "v", false, "Show Program Version") flag.Parse() logger = log.New(&lumberjack.Logger{ Filename: strings.TrimSuffix(os.Args[0], ".exe") + ".log", MaxAge: 30, MaxSize: 10, MaxBackups: 100, LocalTime: true, }, "", log.Ldate|log.Ltime|log.Lmicroseconds) } func main() { if vsn { showVersion() os.Exit(0) } self := strings.TrimSuffix(os.Args[0], ".exe") file := self + ".yml" src, err := os.ReadFile(file) if err != nil { log.Printf("[ERR] %s\r\n", err.Error()) logger.Printf("[ERR] %s\r\n", err.Error()) os.Exit(1) } err = yaml.Unmarshal(src, &cfg) if err != nil { log.Printf("[ERR] %s\r\n", err.Error()) logger.Printf("[ERR] %s\r\n", err.Error()) os.Exit(1) } cwd, err := winsvc.GetAppPath() if err != nil { log.Printf("[ERR] %s\r\n", err.Error()) logger.Printf("[ERR] %s\r\n", err.Error()) os.Exit(1) } err = os.Chdir(filepath.Dir(cwd)) if err != nil { log.Printf("[ERR] %s\r\n", err.Error()) logger.Printf("[ERR] %s\r\n", err.Error()) os.Exit(1) } // Install Service if flagIns { err := winsvc.InstallService(cwd, svc, "API Server") if err != nil { log.Printf("[ERR] Install Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Install Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Install Service Successfully.\r\n") logger.Printf("[MSG] Install Service Successfully.\r\n") return } // Uninstall Service if flagRmv { err := winsvc.RemoveService(svc) if err != nil { log.Printf("[ERR] Uninstall Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Uninstall Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Uninstall Service Successfully.\r\n") logger.Printf("[MSG] Uninstall Service Successfully.\r\n") return } // Start Service if flagRun { err := winsvc.StartService(svc) if err != nil { log.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Service Started.\r\n") logger.Printf("[MSG] Service Started.\r\n") return } // Stop Service if flagTer { err := winsvc.StopService(svc) if err != nil { log.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Service Stop.\r\n") logger.Printf("[MSG] Service Stop.\r\n") return } // Restart Service if flagRst { err := winsvc.StopService(svc) if err != nil { log.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Service Stop.\r\n") logger.Printf("[MSG] Service Stop.\r\n") time.Sleep(time.Second * 1) err = winsvc.StartService(svc) if err != nil { log.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Service Started.\r\n") logger.Printf("[MSG] Service Started.\r\n") return } if !winsvc.InServiceMode() { err := winsvc.RunAsService(svc, startSvc, stopSvc, false) if err != nil { log.Printf("[ERR] Run As Service Failed: %s\r\n", err.Error()) logger.Printf("[ERR] Run As Service Failed: %s\r\n", err.Error()) os.Exit(1) } log.Printf("[MSG] Run As Service ...\r\n") logger.Printf("[MSG] Run As Service ...\r\n") } } func startSvc() { log.Printf("[MSG] Starting Service ...\r\n") logger.Printf("[MSG] Starting Service ...\r\n") http.HandleFunc("/", handleIndex) http.HandleFunc("/request/", handleRequest) http.HandleFunc("/fileinfo/", handleFileInfo) http.HandleFunc("/getuutinfo/", handleGetUutInfo) http.HandleFunc("/setuutinfo/", handleSetUutInfo) logger.Printf("[MSG] Starting HTTP Server On Port: %s\r\n", cfg.Settings.ListenPort) err := http.ListenAndServe(":"+cfg.Settings.ListenPort, nil) if err != nil { logger.Printf("%s\r\n", err.Error()) os.Exit(1) } } func stopSvc() { log.Printf("[MSG] Stopping Service ...\r\n") logger.Printf("[MSG] Stopping Service ...\r\n") os.Exit(0) } func handleIndex(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "MethodNotAllowed: %v\r\n", http.StatusMethodNotAllowed) return } tpl, err := template.ParseFiles("./Index.html") if err != nil { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "ErrMsg: %v\r\n", err) return } err = tpl.Execute(w, nil) if err != nil { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "ErrMsg: %v\r\n", err) return } } func handleRequest(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "MethodNotAllowed: %v\r\n", http.StatusMethodNotAllowed) return } reqID := uuid.NewV4().String() logger.Printf("RequestID: %s; Addr: %s; URI: %s\r\n", reqID, r.RemoteAddr, r.RequestURI) if err := r.ParseForm(); err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", reqID, err.Error()) return } var params []string if len(r.Form) > 0 { logger.Printf("RequestID: %s; Form: %#v\r\n", reqID, r.Form) for k, v := range r.Form { if cfg.Settings.ParamCheck { if !chkParams(k) { continue } if !chkParams(v[0]) { continue } } params = append(params, fmt.Sprintf("%s:%s", k, strings.ReplaceAll(v[0], " ", "_"))) } } logger.Printf("RequestID: %s; Params: %#v\r\n", reqID, params) resp := runCommand(cfg.Settings.HookScript, params, reqID) fmt.Fprintf(w, "%s\r\n", resp) } func handleFileInfo(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } reqID := uuid.NewV4().String() logger.Printf("RequestID: %s; Addr: %s; URI: %s\r\n", reqID, r.RemoteAddr, r.RequestURI) if err := r.ParseForm(); err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", reqID, err.Error()) return } var path string = "" if len(r.Form) > 0 { for k, v := range r.Form { if cfg.Settings.ParamCheck { if !chkParams(k) { continue } if !chkParams(v[0]) { continue } } if k == "path" { path = v[0] } } } if path == "" { fmt.Fprintf(w, "%v", http.StatusBadRequest) return } _, err := os.Stat(path) if err != nil { fmt.Fprintf(w, "%v", http.StatusNotFound) return } type FileInfo struct { FileName string `json:"FileName"` FileSize int64 `json:"FileSize"` LastModified string `json:"LastModified"` //MD5Hash string `json:"MD5Hash"` } type FileList struct { FileList []FileInfo `json:"FileList"` } var infoList []FileInfo err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error { if !info.IsDir() { var fileInfo FileInfo fileInfo.FileName = info.Name() fileInfo.FileSize = info.Size() fileInfo.LastModified = info.ModTime().Format("2006-01-02 15:04:05") //fileInfo.MD5Hash = fileMD5(path) infoList = append(infoList, fileInfo) } return nil }) if err != nil { fmt.Fprintf(w, "%s", err.Error()) return } fileList := FileList{infoList} jsonObj, err := json.MarshalIndent(fileList, "", " ") if err != nil { fmt.Fprintf(w, "%s", err.Error()) return } fmt.Fprintf(w, "%s", string(jsonObj)) } func handleGetUutInfo(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } host := strings.Split(r.Host, ":")[0] addr := strings.Split(r.RemoteAddr, ":")[0] uri := r.RequestURI rst := map[string]string{"RESULT": ""} if err := r.ParseForm(); err != nil { logger.Printf("[ERR] %s; %s; %s\r\n", r.RemoteAddr, r.RequestURI, err.Error()) rst["RESULT"] = "NG" rst["ErrMsg"] = err.Error() w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } params := make(map[string]string, 0) if len(r.Form) > 0 { for k, v := range r.Form { params[strings.ToUpper(strings.TrimSpace(k))] = strings.TrimSpace(v[0]) } } ews := new(ews.EWS) rst = ews.GetUutInfo(cfg, logger, host, addr, uri, params) if rst["RESULT"] != "OK" { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } fmt.Fprintf(w, "%v", rst["ErrMsg"]) } func handleSetUutInfo(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } host := strings.Split(r.Host, ":")[0] addr := strings.Split(r.RemoteAddr, ":")[0] uri := r.RequestURI rst := map[string]string{"RESULT": ""} if r.Method != http.MethodPost { rst["RESULT"] = "NG" rst["ErrMsg"] = fmt.Sprintf("MethodNotAllowed: %v", http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } params, err := parseReqParams(r) if err != nil { logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) rst["RESULT"] = "NG" rst["ErrMsg"] = err.Error() w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } ews := new(ews.EWS) rst = ews.SetUutInfo(cfg, logger, host, addr, uri, params) if rst["RESULT"] != "OK" { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) } func parseReqParams(r *http.Request) (map[string]string, error) { params := make(map[string]string, 0) if err := r.ParseForm(); err != nil { logger.Printf("[ERR] %s; %s; %s\r\n", r.RemoteAddr, r.RequestURI, err.Error()) return params, err } if len(r.Form) > 0 { for k, v := range r.Form { params[strings.ToUpper(strings.TrimSpace(k))] = strings.TrimSpace(v[0]) } } return params, nil } /* func fileMD5(filePath string) string { file, err := os.Open(filePath) if err != nil { return "" } hash := md5.New() _, err = io.Copy(hash, file) if err != nil { return "" } return hex.EncodeToString(hash.Sum(nil)) } */ func chkParams(arg string) bool { if strings.Contains(arg, ";") || strings.Contains(arg, "|") || strings.Contains(arg, "&") || strings.Contains(arg, ">") || strings.Contains(arg, "<") || strings.Contains(arg, "(") || strings.Contains(arg, ")") { return false } return true } func runCommand(cmdStr string, argStr []string, requestID string) string { cmd := exec.Command(cmdStr, argStr...) if errors.Is(cmd.Err, exec.ErrDot) { cmd.Err = nil } std, err := cmd.StdoutPipe() if err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) return err.Error() } defer std.Close() err = cmd.Start() if err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) return err.Error() } ret, err := io.ReadAll(std) if err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) return err.Error() } err = cmd.Wait() if err != nil { logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) return err.Error() } //fmt.Printf("\r\n%s\r\n\r\n", string(ret)) //logger.Printf("RequestID: %s; Result: \r\n%s\r\n\r\n", requestID, string(ret)) return string(ret) } func showVersion() { fmt.Printf("%s\r\nVersion: %s\r\n", os.Args[0], ver) }