package main import ( "flag" "fmt" "net/url" "os" "path/filepath" "reflect" "strconv" "strings" "time" "unsafe" "github.com/gyyyy/ZoomEye-go/zoomeye" ) func parseFlags(cmd string, flgs interface{}, examples ...string) []string { if flgs != nil { var ( v = reflect.ValueOf(flgs) elem = v.Elem() ptr = v.Pointer() ) for i := 0; i < elem.NumField(); i++ { var ( f = elem.Type().Field(i) fname = f.Tag.Get("name") fvalue = f.Tag.Get("value") fusage = f.Tag.Get("usage") fptr = unsafe.Pointer(ptr + f.Offset) ) if fname == "" { fname = strings.ToLower(f.Name) } switch f.Type.Kind() { case reflect.String: flag.StringVar((*string)(fptr), fname, fvalue, fusage) case reflect.Int: val, _ := strconv.Atoi(fvalue) flag.IntVar((*int)(fptr), fname, val, fusage) case reflect.Bool: val, _ := strconv.ParseBool(fvalue) flag.BoolVar((*bool)(fptr), fname, val, fusage) } } } flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "\nUsage of %s (%s):\n", filepath.Base(os.Args[0]), cmd) flag.PrintDefaults() if len(examples) > 0 { example := "Example:\n" for _, v := range examples { example += fmt.Sprintf(" ./ZoomEye-go %s %s\n", cmd, v) } fmt.Fprintf(flag.CommandLine.Output(), "\n%s\n", example) } } var args []string for i := 1; i < len(os.Args); { if p := os.Args[i]; !strings.HasPrefix(p, "-") { args = append(args, p) os.Args = append(os.Args[0:i], os.Args[i+1:]...) } else { break } } flag.Parse() return args } func checkError(err error) { switch err.(type) { case *NoAuthKeyErr: warnf("not found any Auth Keys, please run first") case *zoomeye.ErrorResult: errorf("failed to authenticate: %v", err) case nil: default: errorf("something is wrong: %v", err) } } type resultAnalyzer struct { count bool facet string stat string figure string filter string save bool } func newResultAnalyzer() *resultAnalyzer { analyzer := &resultAnalyzer{} flag.BoolVar(&analyzer.count, "count", false, "The total number of results in ZoomEye database") flag.StringVar(&analyzer.facet, "facet", "", "Perform statistics on ZoomEye database") flag.StringVar(&analyzer.stat, "stat", "", "Perform statistics on search results") flag.StringVar(&analyzer.figure, "figure", "", "Output Pie or bar chart only be used under -facet and -stat") flag.StringVar(&analyzer.filter, "filter", "", "Output more clearer search results by set filter field") flag.BoolVar(&analyzer.save, "save", false, "Save data in JSON format") return analyzer } func (a *resultAnalyzer) do(result *zoomeye.SearchResult, saveCallback func([]map[string]interface{})) { if a.count { infof("ZoomEye Total", "Count: %d", result.Total) } if a.figure != "" { if a.figure = strings.ToLower(a.figure); a.figure != "pie" { a.figure = "hist" } } if a.facet != "" { showFacet(result, strings.Split(a.facet, ","), a.figure) } if a.stat != "" { showStat(result, strings.Split(a.stat, ","), a.figure) } var filtered []map[string]interface{} if a.filter != "" { filtered = showFilter(result, strings.Split(a.filter, ",")) } if !a.count && a.facet == "" && a.stat == "" && a.filter == "" { showData(result) } if a.save && saveCallback != nil { saveCallback(filtered) } } func cmdInit(agent *ZoomEyeAgent) { var flgs struct { apiKey string `usage:"ZoomEye API-Key"` username string `usage:"ZoomEye account username"` password string `usage:"ZoomEye account password"` } parseFlags("init", &flgs, `-apikey "XXXXXXXX-XXXX-XXXXX-XXXX-XXXXXXXXXXX"`, `-username "username@zoomeye.org" -password "password"`) var ( result *zoomeye.ResourcesInfoResult err error ) if flgs.apiKey != "" { if result, err = agent.InitByKey(flgs.apiKey); err != nil { errorf("failed to initialize: %v", err) return } } else if flgs.username != "" && flgs.password != "" { if result, err = agent.InitByUser(flgs.username, flgs.password); err != nil { errorf("failed to initialize: %v", err) return } } else if result, err = agent.InitLocal(); err != nil { warnf("required parameter missing, please run for help") return } successf("succeed to initialize") infof("ZoomEye Resources Info", "Role: %s\nQuota: %d", result.Plan, result.Resources.Search) } func cmdInfo(agent *ZoomEyeAgent) { result, err := agent.Info() if err != nil { checkError(err) return } successf("succeed to query") infof("ZoomEye Resources Info", "Role: %s\nQuota: %d", result.Plan, result.Resources.Search) } func cmdSearch(agent *ZoomEyeAgent) { var ( analyzer = newResultAnalyzer() flgs struct { num int `value:"20" usage:"The number of search results that should be returned, multiple of 20"` resource string `name:"type" usage:"Specify the type of resource to search"` force bool `usage:"Ignore local and cache data"` } args = parseFlags("search", &flgs, `"weblogic" -facet "app" -count`) ) if len(args) == 0 { warnf("search keyword missing, please run for help") return } var ( dork = args[0] start = time.Now() result, err = agent.Search(dork, flgs.num, flgs.resource, flgs.force) since = time.Since(start) ) if err != nil { checkError(err) return } successf("succeed to search (in %v)", since) analyzer.do(result, func(filtered []map[string]interface{}) { name := fmt.Sprintf("%s_%s_%d", flgs.resource, url.QueryEscape(dork), flgs.num) if path, err := agent.Save(name, result); err != nil { errorf("failed to save: %v", err) } else { successf("succeed to save (%s)", path) agent.SaveFilterData(filepath.Join(agent.conf.DataPath, name+"_filtered.json"), filtered) } }) } func cmdLoad(agent *ZoomEyeAgent) { var ( analyzer = newResultAnalyzer() args = parseFlags("load", nil, `"data/host_weblogic_20.json" -facet "app" -count`) ) if len(args) == 0 { warnf("path of local data file missing, please run for help") return } var ( file = args[0] result, err = agent.Load(file) ) if err != nil { errorf("invalid local data: %v", err) return } successf("succeed to load") analyzer.do(result, func(filtered []map[string]interface{}) { var ( ext = filepath.Ext(file) path = strings.TrimSuffix(file, ext) + "_filtered" + ext ) if err := agent.SaveFilterData(path, filtered); err != nil { errorf("failed to save: %v", err) } else { path, _ = filepath.Abs(path) successf("succeed to save (%s)", path) } }) } func cmdHistory(agent *ZoomEyeAgent) { var ( flgs struct { filter string `usage:"Output more clearer query results by set filter field"` num int `value:"20" usage:"The number of results that should be returned"` force bool `usage:"Ignore cache data"` } args = parseFlags("history", &flgs, `"0.0.0.0" -filter "time=^2020-03,port,service" -num 1`) ) if len(args) == 0 { warnf("ip missing, please run for help") return } var ( start = time.Now() result, err = agent.History(args[0], flgs.force) since = time.Since(start) ) if err != nil { checkError(err) return } successf("succeed to query (in %v)", since) showHistory(result, strings.Split(flgs.filter, ","), flgs.num) } func cmdClear(agent *ZoomEyeAgent) { var flgs struct { cache bool setting bool } parseFlags("clear", &flgs, `-cache`, `-cache -setting`) agent.Clear(flgs.cache, flgs.setting) successf("succeed to clear data") }