package main import ( "APIServer/cnf" "APIServer/ews" "APIServer/racs" "crypto/md5" "encoding/hex" "encoding/json" "errors" "flag" "fmt" "io" "log" "net/http" _ "net/http/pprof" "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: 90, 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 Stopped.\r\n") logger.Printf("[MSG] Service Stopped.\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 Stopped.\r\n") logger.Printf("[MSG] Service Stopped.\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") return } } 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("/setuutinfo/", handleSetUutInfo) http.HandleFunc("/getuutinfo/", handleGetUutInfo) http.HandleFunc("/racs/inbound/", handleRacsInbound) http.HandleFunc("/racs/outbound/", handleRacsOutbound) http.HandleFunc("/racs/getfaillist/", handleRacsGetFailList) 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 cfg.Settings.MustPost { 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 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 handleSetUutInfo(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } var err error host := "" if cfg.Settings.HostMode == 0 { host = strings.Split(r.Host, ":")[0] } else { host, err = os.Hostname() if err != nil { host = "hostname" } } 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 } if cfg.Settings.LogRequest { logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) } ews := new(ews.EWS) rst = ews.SetUutInfo(cfg, logger, host, addr, uri, params) if cfg.Settings.LogResponse { logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) } 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 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, 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 } if cfg.Settings.LogRequest { logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) } ews := new(ews.EWS) switch params["ACTION"] { case "QueryByFilter": rst = ews.GetUutInfo(cfg, logger, host, addr, uri, params) case "GetIssueReport": rst = ews.GetIssueReport(cfg, logger, host, addr, uri, params) case "GetOfflineReport": rst = ews.GetOfflineReport(cfg, logger, host, addr, uri, params) default: rst = ews.GetUutInfo(cfg, logger, host, addr, uri, params) } if cfg.Settings.LogResponse { logger.Printf("[MSG] %s; %s; Response: Length=%d\r\n", addr, uri, len(rst["ErrMsg"])) } if rst["RESULT"] != "OK" { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } fmt.Fprintf(w, "%v", rst["ErrMsg"]) } func handleRacsInbound(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } var err error addr := strings.Split(r.RemoteAddr, ":")[0] uri := r.RequestURI rst := map[string]string{"Result": "OK", "ErrMsg": ""} params, err := parseReqParams(r) if err != nil { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleInbound: " + err.Error() logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } if params["USN"] == "" { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleInbound: Missing Parameter USN" logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } if cfg.Settings.LogRequest { logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) } racs := new(racs.RACS) rst = racs.Inbound(cfg, logger, addr, uri, params) if cfg.Settings.LogResponse { logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) } 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 handleRacsOutbound(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } var err error addr := strings.Split(r.RemoteAddr, ":")[0] uri := r.RequestURI rst := map[string]string{"Result": "OK", "ErrMsg": ""} params, err := parseReqParams(r) if err != nil { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleOutbound: " + err.Error() logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } if params["USN"] == "" { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleOutbound: Missing Parameter USN" logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } if cfg.Settings.LogRequest { logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) } racs := new(racs.RACS) rst = racs.Outbound(cfg, logger, addr, uri, params) if cfg.Settings.LogResponse { logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) } 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 handleRacsGetFailList(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.RequestURI == "/favicon.ico" { return } var err error addr := strings.Split(r.RemoteAddr, ":")[0] uri := r.RequestURI rst := map[string]interface{}{"Result": "OK", "ErrMsg": ""} params, err := parseReqParams(r) if err != nil { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleGetFailList: " + err.Error() logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) return } racs := new(racs.RACS) rst = racs.GetFailList(cfg, logger, addr, uri, params) rpl := strings.NewReplacer( "map[string]string", "", "map[string]interface {}", "", "[]{", "[", "}},", "}],", ) if rst["List"] != nil { jsonData, err := json.MarshalIndent(rst, "", "\t") if err != nil { rst["Result"] = "NG" rst["ErrMsg"] = "racs.HandleGetFailList: " + err.Error() logger.Printf("[ERR] %s; %s; %s\r\n", addr, uri, rst["ErrMsg"]) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "%v", rpl.Replace(fmt.Sprintf("%#v", rst))) return } w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "%v", rpl.Replace(string(jsonData))) return } if rst["Result"] != "OK" { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } fmt.Fprintf(w, "%v", rpl.Replace(fmt.Sprintf("%#v", rst))) } 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.ReplaceAll(strings.TrimSpace(v[0]), "\"", "") } } return params, 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, Version: %s\r\n", os.Args[0], ver) }