func isAndroidStudioProject(path string) bool {
	if f, err := os.Stat(path); err != nil || !f.IsDir() {
		return false
	}
	f := filepath.Join(path, "app", "src", "main", "AndroidManifest.xml")
	fi, err := os.Stat(f)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Failed to stat %s: %v\n", f, err)
		}
		return false
	}
	if !fi.Mode().IsRegular() {
		return false
	}
	f = filepath.Join(path, "app", "src", "main", "java")
	fi, err = os.Stat(f)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Failed to stat %s: %v\n", f, err)
		}
		return false
	}
	if !fi.IsDir() {
		return false
	}
	return true
}
isAndroidStudioProject returns true if the given path is the root directory
of an Android Studio project, or false if not.

The following conditions must hold for a directory structure to be considered
an Android Studio project by MobSF:
- the file app/src/main/AndroidManifest.xml must exist;
- the directory app/src/main/java/ must exist.
package main
import (
	"archive/zip"
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	"github.com/thought-machine/dracon/api/proto/v1"
	dtemplate "github.com/thought-machine/dracon/pkg/template"
	"github.com/thought-machine/dracon/producers"
	mreport "github.com/thought-machine/dracon/producers/mobsf/report"
	"github.com/thought-machine/dracon/producers/mobsf/report/android"
	"github.com/thought-machine/dracon/producers/mobsf/report/ios"
)
const (
	MobSFBindHost = "127.0.0.1"
	MobSFBindPort = 8080
)
var mobSFAPIKey = generateAPIKey()
func parseCLIOptions() *CLI {
	cli := NewCLI()
	flag.StringVar(
		&cli.InPath,
		"in",
		dtemplate.TemplateVars.ProducerSourcePath,
		"Path to directory containing source code to scan",
	)
	flag.StringVar(
		&cli.OutPath,
		"out",
		dtemplate.TemplateVars.ProducerOutPath,
		"Path to which Dracon Issues protobuf should be written",
	)
	flag.Var(
		&cli.CodeAnalysisExclusions,
		"exclude",
		"Comma-delimited list of static analysis rule IDs to ignore",
	)
	flag.Parse()
	producers.OutFile = cli.OutPath
	return cli
}
func generateAPIKey() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	hash := sha256.Sum256([]byte(strconv.Itoa(r.Int())))
	return fmt.Sprintf("%x", hash)
}
func startMobSF() (int, error) {
	bindArg := fmt.Sprintf("--bind=%s:%d", MobSFBindHost, MobSFBindPort)
	mobSF := exec.Command(
		"/usr/local/bin/gunicorn",
		"--daemon", bindArg, "--workers=1", "--threads=10", "--timeout=1800",
		"MobSF.wsgi:application",
	)
	mobSF.Env = append(os.Environ(), "MOBSF_API_KEY="+mobSFAPIKey)
	mobSF.Dir = "/root/Mobile-Security-Framework-MobSF"
	if err := mobSF.Run(); err != nil {
		return 0, err
	}
	return mobSF.Process.Pid, nil
}
func isAPIResponsive() bool {
	client := http.Client{Timeout: 1 * time.Second}
	resp, err := client.Get(fmt.Sprintf("http://%s:%d/api/v1", MobSFBindHost, MobSFBindPort))
	if err != nil {
		return false
	}
	return strings.Contains(resp.Header.Get("Access-Control-Allow-Headers"), "Authorization")
}
func findProjects(path string) ([]*Project, error) {
	if dir, err := os.Stat(path); err != nil || !dir.IsDir() {
		return nil, fmt.Errorf("%s is not a directory", path)
	}
	projects := make([]*Project, 0)
	err := filepath.Walk(path, func(f string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !fi.IsDir() {
			return nil
		}
		var projectType *ProjectType
		if isAndroidEclipseProject(f) {
			projectType = new(ProjectType)
			*projectType = AndroidEclipse
		} else if isAndroidStudioProject(f) {
			projectType = new(ProjectType)
			*projectType = AndroidStudio
		} else if isXcodeiOSProject(f) {
			projectType = new(ProjectType)
			*projectType = XcodeIos
		}
		if projectType != nil {
			log.Printf("Found %s project at %s\n", *projectType, f)
			projects = append(projects, &Project{
				RootDir: f,
				Type:    *projectType,
			})
			return filepath.SkipDir
		}
		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("failed to walk directory %s: %w", path, err)
	}
	return projects, nil
}
func isAndroidEclipseProject(path string) bool {
	if f, err := os.Stat(path); err != nil || !f.IsDir() {
		return false
	}
	f := filepath.Join(path, "AndroidManifest.xml")
	fi, err := os.Stat(f)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Failed to stat %s: %v\n", f, err)
		}
		return false
	}
	if !fi.Mode().IsRegular() {
		return false
	}
	f = filepath.Join(path, "src")
	fi, err = os.Stat(f)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Failed to stat %s: %v\n", f, err)
		}
		return false
	}
	if !fi.IsDir() {
		return false
	}
	return true
}