<commit_before>package server

import (
	"net/http"
	"strconv"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/drone/drone/bus"
	"github.com/drone/drone/queue"
	"github.com/drone/drone/remote"
	"github.com/drone/drone/shared/httputil"
	"github.com/drone/drone/store"
	"github.com/drone/drone/stream"
	"github.com/gin-gonic/gin"
	"github.com/square/go-jose"

	"github.com/drone/drone/model"
	"github.com/drone/drone/router/middleware/session"
)

func GetBuilds(c *gin.Context) {
	repo := session.Repo(c)
	builds, err := store.GetBuildList(c, repo)
	if err != nil {
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
	c.JSON(http.StatusOK, builds)
}

func GetBuild(c *gin.Context) {
	if c.Param("number") == "latest" {
		GetBuildLast(c)
		return
	}

	repo := session.Repo(c)
	num, err := strconv.Atoi(c.Param("number"))
	if err != nil {
		c.AbortWithError(http.StatusBadRequest, err)
		return
	}

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	jobs, _ := store.GetJobList(c, build)

	out := struct {
		*model.Build
		Jobs []*model.Job `json:"jobs"`
	}{build, jobs}

	c.JSON(http.StatusOK, &out)
}

func GetBuildLast(c *gin.Context) {
	repo := session.Repo(c)
	branch := c.DefaultQuery("branch", repo.Branch)

	build, err := store.GetBuildLast(c, repo, branch)
	if err != nil {
		c.String(http.StatusInternalServerError, err.Error())
		return
	}
	jobs, _ := store.GetJobList(c, build)

	out := struct {
		*model.Build
		Jobs []*model.Job `json:"jobs"`
	}{build, jobs}

	c.JSON(http.StatusOK, &out)
}

func GetBuildLogs(c *gin.Context) {
	repo := session.Repo(c)

	// the user may specify to stream the full logs,
	// or partial logs, capped at 2MB.
	full, _ := strconv.ParseBool(c.DefaultQuery("full", "false"))

	// parse the build number and job sequence number from
	// the repquest parameter.
	num, _ := strconv.Atoi(c.Params.ByName("number"))
	seq, _ := strconv.Atoi(c.Params.ByName("job"))

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	job, err := store.GetJobNumber(c, build, seq)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	r, err := store.ReadLog(c, job)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	defer r.Close()
	if full {
		// TODO implement limited streaming to avoid crashing the browser
	}

	c.Header("Content-Type", "application/json")
	stream.Copy(c.Writer, r)
}

func DeleteBuild(c *gin.Context) {
	repo := session.Repo(c)

	// parse the build number and job sequence number from
	// the repquest parameter.
	num, _ := strconv.Atoi(c.Params.ByName("number"))
	seq, _ := strconv.Atoi(c.Params.ByName("job"))

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	job, err := store.GetJobNumber(c, build, seq)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	bus.Publish(c, bus.NewEvent(bus.Cancelled, repo, build, job))
	c.String(204, "")
}

func PostBuild(c *gin.Context) {

	remote_ := remote.FromContext(c)
	repo := session.Repo(c)
	fork := c.DefaultQuery("fork", "false")

	num, err := strconv.Atoi(c.Param("number"))
	if err != nil {
		c.AbortWithError(http.StatusBadRequest, err)
		return
	}

	user, err := store.GetUser(c, repo.UserID)
	if err != nil {
		log.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
		c.AbortWithError(500, err)
		return
	}

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		log.Errorf("failure to get build %d. %s", num, err)
		c.AbortWithError(404, err)
		return
	}

	// if the remote has a refresh token, the current access token
	// may be stale. Therefore, we should refresh prior to dispatching
	// the job.
	if refresher, ok := remote_.(remote.Refresher); ok {
		ok, _ := refresher.Refresh(user)
		if ok {
			store.UpdateUser(c, user)
		}
	}

	// fetch the .drone.yml file from the database
	config := ToConfig(c)
	raw, err := remote_.File(user, repo, build, config.Yaml)
	if err != nil {
		log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
		c.AbortWithError(404, err)
		return
	}

	// Fetch secrets file but don't exit on error as it's optional
	sec, err := remote_.File(user, repo, build, config.Shasum)
	if err != nil {
		log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err)
	}

	netrc, err := remote_.Netrc(user, repo)
	if err != nil {
		log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
		c.AbortWithError(500, err)
		return
	}

	jobs, err := store.GetJobList(c, build)
	if err != nil {
		log.Errorf("failure to get build %d jobs. %s", build.Number, err)
		c.AbortWithError(404, err)
		return
	}

	// must not restart a running build
	if build.Status == model.StatusPending || build.Status == model.StatusRunning {
		c.String(409, "Cannot re-start a started build")
		return
	}

	// forking the build creates a duplicate of the build
	// and then executes. This retains prior build history.
	if forkit, _ := strconv.ParseBool(fork); forkit {
		build.ID = 0
		build.Number = 0
		for _, job := range jobs {
			job.ID = 0
			job.NodeID = 0
		}
		err := store.CreateBuild(c, build, jobs...)
		if err != nil {
			c.String(500, err.Error())
			return
		}

		event := c.DefaultQuery("event", build.Event)
		if event == model.EventPush ||
			event == model.EventPull ||
			event == model.EventTag ||
			event == model.EventDeploy {
			build.Event = event
		}
		build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
	}

	// todo move this to database tier
	// and wrap inside a transaction
	build.Status = model.StatusPending
	build.Started = 0
	build.Finished = 0
	build.Enqueued = time.Now().UTC().Unix()
	for _, job := range jobs {
		job.Status = model.StatusPending
		job.Started = 0
		job.Finished = 0
		job.ExitCode = 0
		job.NodeID = 0
		job.Enqueued = build.Enqueued
		store.UpdateJob(c, job)
	}

	err = store.UpdateBuild(c, build)
	if err != nil {
		c.AbortWithStatus(500)
		return
	}

	c.JSON(202, build)

	// get the previous build so that we can send
	// on status change notifications
	last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
	secs, err := store.GetSecretList(c, repo)
	if err != nil {
		log.Errorf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
	}

	var signed bool
	var verified bool

	signature, err := jose.ParseSigned(string(sec))
	if err != nil {
		log.Debugf("cannot parse .drone.yml.sig file. %s", err)
	} else if len(sec) == 0 {
		log.Debugf("cannot parse .drone.yml.sig file. empty file")
	} else {
		signed = true
		output, err := signature.Verify([]byte(repo.Hash))
		if err != nil {
			log.Debugf("cannot verify .drone.yml.sig file. %s", err)
		} else if string(output) != string(raw) {
			log.Debugf("cannot verify .drone.yml.sig file. no match. %q <> %q", string(output), string(raw))
		} else {
			verified = true
		}
	}

	log.Debugf(".drone.yml is signed=%v and verified=%v", signed, verified)

	bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
	for _, job := range jobs {
		queue.Publish(c, &queue.Work{
			Signed:    signed,
			Verified:  verified,
			User:      user,
			Repo:      repo,
			Build:     build,
			BuildLast: last,
			Job:       job,
			Netrc:     netrc,
			Yaml:      string(raw),
			Secrets:   secs,
			System:    &model.System{Link: httputil.GetURL(c.Request)},
		})
	}
}

func GetBuildQueue(c *gin.Context) {
	out, err := store.GetBuildQueue(c)
	if err != nil {
		c.String(500, "Error getting build queue. %s", err)
		return
	}
	c.JSON(200, out)
}
<commit_msg>modifying cancel logic<commit_after>package server

import (
	"net/http"
	"strconv"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/drone/drone/bus"
	"github.com/drone/drone/queue"
	"github.com/drone/drone/remote"
	"github.com/drone/drone/shared/httputil"
	"github.com/drone/drone/store"
	"github.com/drone/drone/stream"
	"github.com/gin-gonic/gin"
	"github.com/square/go-jose"

	"github.com/drone/drone/model"
	"github.com/drone/drone/router/middleware/session"
)

func GetBuilds(c *gin.Context) {
	repo := session.Repo(c)
	builds, err := store.GetBuildList(c, repo)
	if err != nil {
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
	c.JSON(http.StatusOK, builds)
}

func GetBuild(c *gin.Context) {
	if c.Param("number") == "latest" {
		GetBuildLast(c)
		return
	}

	repo := session.Repo(c)
	num, err := strconv.Atoi(c.Param("number"))
	if err != nil {
		c.AbortWithError(http.StatusBadRequest, err)
		return
	}

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	jobs, _ := store.GetJobList(c, build)

	out := struct {
		*model.Build
		Jobs []*model.Job `json:"jobs"`
	}{build, jobs}

	c.JSON(http.StatusOK, &out)
}

func GetBuildLast(c *gin.Context) {
	repo := session.Repo(c)
	branch := c.DefaultQuery("branch", repo.Branch)

	build, err := store.GetBuildLast(c, repo, branch)
	if err != nil {
		c.String(http.StatusInternalServerError, err.Error())
		return
	}
	jobs, _ := store.GetJobList(c, build)

	out := struct {
		*model.Build
		Jobs []*model.Job `json:"jobs"`
	}{build, jobs}

	c.JSON(http.StatusOK, &out)
}

func GetBuildLogs(c *gin.Context) {
	repo := session.Repo(c)

	// the user may specify to stream the full logs,
	// or partial logs, capped at 2MB.
	full, _ := strconv.ParseBool(c.DefaultQuery("full", "false"))

	// parse the build number and job sequence number from
	// the repquest parameter.
	num, _ := strconv.Atoi(c.Params.ByName("number"))
	seq, _ := strconv.Atoi(c.Params.ByName("job"))

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	job, err := store.GetJobNumber(c, build, seq)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	r, err := store.ReadLog(c, job)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	defer r.Close()
	if full {
		// TODO implement limited streaming to avoid crashing the browser
	}

	c.Header("Content-Type", "application/json")
	stream.Copy(c.Writer, r)
}

func DeleteBuild(c *gin.Context) {
	repo := session.Repo(c)

	// parse the build number and job sequence number from
	// the repquest parameter.
	num, _ := strconv.Atoi(c.Params.ByName("number"))
	seq, _ := strconv.Atoi(c.Params.ByName("job"))

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	job, err := store.GetJobNumber(c, build, seq)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}

	if job.Status != model.StatusRunning {
		c.String(400, "Cannot cancel a non-running build")
		return
	}

	job.Status = model.StatusKilled
	job.Finished = time.Now().Unix()
	if job.Started == 0 {
		job.Started = job.Finished
	}
	job.ExitCode = 137
	store.UpdateBuildJob(c, build, job)

	bus.Publish(c, bus.NewEvent(bus.Cancelled, repo, build, job))
	c.String(204, "")
}

func PostBuild(c *gin.Context) {

	remote_ := remote.FromContext(c)
	repo := session.Repo(c)
	fork := c.DefaultQuery("fork", "false")

	num, err := strconv.Atoi(c.Param("number"))
	if err != nil {
		c.AbortWithError(http.StatusBadRequest, err)
		return
	}

	user, err := store.GetUser(c, repo.UserID)
	if err != nil {
		log.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
		c.AbortWithError(500, err)
		return
	}

	build, err := store.GetBuildNumber(c, repo, num)
	if err != nil {
		log.Errorf("failure to get build %d. %s", num, err)
		c.AbortWithError(404, err)
		return
	}

	// if the remote has a refresh token, the current access token
	// may be stale. Therefore, we should refresh prior to dispatching
	// the job.
	if refresher, ok := remote_.(remote.Refresher); ok {
		ok, _ := refresher.Refresh(user)
		if ok {
			store.UpdateUser(c, user)
		}
	}

	// fetch the .drone.yml file from the database
	config := ToConfig(c)
	raw, err := remote_.File(user, repo, build, config.Yaml)
	if err != nil {
		log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
		c.AbortWithError(404, err)
		return
	}

	// Fetch secrets file but don't exit on error as it's optional
	sec, err := remote_.File(user, repo, build, config.Shasum)
	if err != nil {
		log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err)
	}

	netrc, err := remote_.Netrc(user, repo)
	if err != nil {
		log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
		c.AbortWithError(500, err)
		return
	}

	jobs, err := store.GetJobList(c, build)
	if err != nil {
		log.Errorf("failure to get build %d jobs. %s", build.Number, err)
		c.AbortWithError(404, err)
		return
	}

	// must not restart a running build
	if build.Status == model.StatusPending || build.Status == model.StatusRunning {
		c.String(409, "Cannot re-start a started build")
		return
	}

	// forking the build creates a duplicate of the build
	// and then executes. This retains prior build history.
	if forkit, _ := strconv.ParseBool(fork); forkit {
		build.ID = 0
		build.Number = 0
		for _, job := range jobs {
			job.ID = 0
			job.NodeID = 0
		}
		err := store.CreateBuild(c, build, jobs...)
		if err != nil {
			c.String(500, err.Error())
			return
		}

		event := c.DefaultQuery("event", build.Event)
		if event == model.EventPush ||
			event == model.EventPull ||
			event == model.EventTag ||
			event == model.EventDeploy {
			build.Event = event
		}
		build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
	}

	// todo move this to database tier
	// and wrap inside a transaction
	build.Status = model.StatusPending
	build.Started = 0
	build.Finished = 0
	build.Enqueued = time.Now().UTC().Unix()
	for _, job := range jobs {
		job.Status = model.StatusPending
		job.Started = 0
		job.Finished = 0
		job.ExitCode = 0
		job.NodeID = 0
		job.Enqueued = build.Enqueued
		store.UpdateJob(c, job)
	}

	err = store.UpdateBuild(c, build)
	if err != nil {
		c.AbortWithStatus(500)
		return
	}

	c.JSON(202, build)

	// get the previous build so that we can send
	// on status change notifications
	last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
	secs, err := store.GetSecretList(c, repo)
	if err != nil {
		log.Errorf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
	}

	var signed bool
	var verified bool

	signature, err := jose.ParseSigned(string(sec))
	if err != nil {
		log.Debugf("cannot parse .drone.yml.sig file. %s", err)
	} else if len(sec) == 0 {
		log.Debugf("cannot parse .drone.yml.sig file. empty file")
	} else {
		signed = true
		output, err := signature.Verify([]byte(repo.Hash))
		if err != nil {
			log.Debugf("cannot verify .drone.yml.sig file. %s", err)
		} else if string(output) != string(raw) {
			log.Debugf("cannot verify .drone.yml.sig file. no match. %q <> %q", string(output), string(raw))
		} else {
			verified = true
		}
	}

	log.Debugf(".drone.yml is signed=%v and verified=%v", signed, verified)

	bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
	for _, job := range jobs {
		queue.Publish(c, &queue.Work{
			Signed:    signed,
			Verified:  verified,
			User:      user,
			Repo:      repo,
			Build:     build,
			BuildLast: last,
			Job:       job,
			Netrc:     netrc,
			Yaml:      string(raw),
			Secrets:   secs,
			System:    &model.System{Link: httputil.GetURL(c.Request)},
		})
	}
}

func GetBuildQueue(c *gin.Context) {
	out, err := store.GetBuildQueue(c)
	if err != nil {
		c.String(500, "Error getting build queue. %s", err)
		return
	}
	c.JSON(200, out)
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"strings"
	"syscall"

	"github.com/deis/workflow/client/controller/client"
	"github.com/deis/workflow/client/controller/models/auth"
	"golang.org/x/crypto/ssh/terminal"
)

// Register creates a account on a Deis controller.
func Register(controller string, username string, password string, email string,
	sslVerify bool) error {

	u, err := url.Parse(controller)
	httpClient := client.CreateHTTPClient(sslVerify)

	if err != nil {
		return err
	}

	controllerURL, err := chooseScheme(*u)

	if err != nil {
		return err
	}

	if err = client.CheckConnection(httpClient, controllerURL); err != nil {
		return err
	}

	if username == "" {
		fmt.Print("username: ")
		fmt.Scanln(&username)
	}

	if password == "" {
		fmt.Print("password: ")
		password, err = readPassword()
		fmt.Printf("\npassword (confirm): ")
		passwordConfirm, err := readPassword()
		fmt.Println()

		if err != nil {
			return err
		}

		if password != passwordConfirm {
			return errors.New("Password mismatch, aborting registration.")
		}
	}

	if email == "" {
		fmt.Print("email: ")
		fmt.Scanln(&email)
	}

	c := &client.Client{ControllerURL: controllerURL, SSLVerify: sslVerify, HTTPClient: httpClient}

	tempClient, err := client.New()

	if err == nil {
		c.Token = tempClient.Token
	}

	err = auth.Register(c, username, password, email)

	c.Token = ""

	if err != nil {
		fmt.Fprint(os.Stderr, "Registration failed: ")
		return err
	}

	fmt.Printf("Registered %s\n", username)
	return doLogin(c, username, password)
}

func doLogin(c *client.Client, username, password string) error {
	token, err := auth.Login(c, username, password)

	if err != nil {
		return err
	}

	c.Token = token
	c.Username = username

	err = c.Save()

	if err != nil {
		return nil
	}

	fmt.Printf("Logged in as %s\n", username)
	return nil
}

// Login to a Deis controller.
func Login(controller string, username string, password string, sslVerify bool) error {
	u, err := url.Parse(controller)

	if err != nil {
		return err
	}

	controllerURL, err := chooseScheme(*u)
	httpClient := client.CreateHTTPClient(sslVerify)

	if err != nil {
		return err
	}

	if err = client.CheckConnection(httpClient, controllerURL); err != nil {
		return err
	}

	if username == "" {
		fmt.Print("username: ")
		fmt.Scanln(&username)
	}

	if password == "" {
		fmt.Print("password: ")
		password, err = readPassword()
		fmt.Println()

		if err != nil {
			return err
		}
	}

	c := &client.Client{ControllerURL: controllerURL, SSLVerify: sslVerify, HTTPClient: httpClient}

	return doLogin(c, username, password)
}

// Logout from a Deis controller.
func Logout() error {
	if err := client.Delete(); err != nil {
		return err
	}

	fmt.Println("Logged out")
	return nil
}

// Passwd changes a user's password.
func Passwd(username string, password string, newPassword string) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	if password == "" && username == "" {
		fmt.Print("current password: ")
		password, err = readPassword()
		fmt.Println()

		if err != nil {
			return err
		}
	}

	if newPassword == "" {
		fmt.Print("new password: ")
		newPassword, err = readPassword()
		fmt.Printf("\nnew password (confirm): ")
		passwordConfirm, err := readPassword()

		fmt.Println()

		if err != nil {
			return err
		}

		if newPassword != passwordConfirm {
			return errors.New("Password mismatch, not changing.")
		}
	}

	err = auth.Passwd(c, username, password, newPassword)

	if err != nil {
		fmt.Fprint(os.Stderr, "Password change failed: ")
		return err
	}

	fmt.Println("Password change succeeded.")
	return nil
}

// Cancel deletes a user's account.
func Cancel(username string, password string, yes bool) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	if username == "" || password != "" {
		fmt.Println("Please log in again in order to cancel this account")

		if err = Login(c.ControllerURL.String(), username, password, c.SSLVerify); err != nil {
			return err
		}
	}

	if yes == false {
		confirm := ""

		c, err = client.New()

		if err != nil {
			return err
		}

		deletedUser := username

		if deletedUser == "" {
			deletedUser = c.Username
		}

		fmt.Printf("cancel account %s at %s? (y/N): ", deletedUser, c.ControllerURL.String())
		fmt.Scanln(&confirm)

		if strings.ToLower(confirm) == "y" {
			yes = true
		}
	}

	if yes == false {
		fmt.Fprintln(os.Stderr, "Account not changed")
		return nil
	}

	err = auth.Delete(c, username)

	if err != nil {
		return err
	}

	// If user targets themselves, logout.
	if username != "" || c.Username == username {
		if err := client.Delete(); err != nil {
			return err
		}
	}

	fmt.Println("Account cancelled")
	return nil
}

// Whoami prints the logged in user.
func Whoami() error {
	c, err := client.New()

	if err != nil {
		return err
	}

	fmt.Printf("You are %s at %s\n", c.Username, c.ControllerURL.String())
	return nil
}

// Regenerate regenenerates a user's token.
func Regenerate(username string, all bool) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	token, err := auth.Regenerate(c, username, all)

	if err != nil {
		return err
	}

	if username == "" && all == false {
		c.Token = token

		err = c.Save()

		if err != nil {
			return err
		}
	}

	fmt.Println("Token Regenerated")
	return nil
}

func readPassword() (string, error) {
	password, err := terminal.ReadPassword(int(syscall.Stdin))

	return string(password), err
}

func chooseScheme(u url.URL) (url.URL, error) {
	if u.Scheme == "" {
		u.Scheme = "http"
		u, err := url.Parse(u.String())

		if err != nil {
			return url.URL{}, err
		}

		return *u, nil
	}

	return u, nil
}
<commit_msg>fix(client): only delete local ~/.deis/client.json if cancelling logged in user<commit_after>package cmd

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"strings"
	"syscall"

	"github.com/deis/workflow/client/controller/client"
	"github.com/deis/workflow/client/controller/models/auth"
	"golang.org/x/crypto/ssh/terminal"
)

// Register creates a account on a Deis controller.
func Register(controller string, username string, password string, email string,
	sslVerify bool) error {

	u, err := url.Parse(controller)
	httpClient := client.CreateHTTPClient(sslVerify)

	if err != nil {
		return err
	}

	controllerURL, err := chooseScheme(*u)

	if err != nil {
		return err
	}

	if err = client.CheckConnection(httpClient, controllerURL); err != nil {
		return err
	}

	if username == "" {
		fmt.Print("username: ")
		fmt.Scanln(&username)
	}

	if password == "" {
		fmt.Print("password: ")
		password, err = readPassword()
		fmt.Printf("\npassword (confirm): ")
		passwordConfirm, err := readPassword()
		fmt.Println()

		if err != nil {
			return err
		}

		if password != passwordConfirm {
			return errors.New("Password mismatch, aborting registration.")
		}
	}

	if email == "" {
		fmt.Print("email: ")
		fmt.Scanln(&email)
	}

	c := &client.Client{ControllerURL: controllerURL, SSLVerify: sslVerify, HTTPClient: httpClient}

	tempClient, err := client.New()

	if err == nil {
		c.Token = tempClient.Token
	}

	err = auth.Register(c, username, password, email)

	c.Token = ""

	if err != nil {
		fmt.Fprint(os.Stderr, "Registration failed: ")
		return err
	}

	fmt.Printf("Registered %s\n", username)
	return doLogin(c, username, password)
}

func doLogin(c *client.Client, username, password string) error {
	token, err := auth.Login(c, username, password)

	if err != nil {
		return err
	}

	c.Token = token
	c.Username = username

	err = c.Save()

	if err != nil {
		return nil
	}

	fmt.Printf("Logged in as %s\n", username)
	return nil
}

// Login to a Deis controller.
func Login(controller string, username string, password string, sslVerify bool) error {
	u, err := url.Parse(controller)

	if err != nil {
		return err
	}

	controllerURL, err := chooseScheme(*u)
	httpClient := client.CreateHTTPClient(sslVerify)

	if err != nil {
		return err
	}

	if err = client.CheckConnection(httpClient, controllerURL); err != nil {
		return err
	}

	if username == "" {
		fmt.Print("username: ")
		fmt.Scanln(&username)
	}

	if password == "" {
		fmt.Print("password: ")
		password, err = readPassword()
		fmt.Println()

		if err != nil {
			return err
		}
	}

	c := &client.Client{ControllerURL: controllerURL, SSLVerify: sslVerify, HTTPClient: httpClient}

	return doLogin(c, username, password)
}

// Logout from a Deis controller.
func Logout() error {
	if err := client.Delete(); err != nil {
		return err
	}

	fmt.Println("Logged out")
	return nil
}

// Passwd changes a user's password.
func Passwd(username string, password string, newPassword string) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	if password == "" && username == "" {
		fmt.Print("current password: ")
		password, err = readPassword()
		fmt.Println()

		if err != nil {
			return err
		}
	}

	if newPassword == "" {
		fmt.Print("new password: ")
		newPassword, err = readPassword()
		fmt.Printf("\nnew password (confirm): ")
		passwordConfirm, err := readPassword()

		fmt.Println()

		if err != nil {
			return err
		}

		if newPassword != passwordConfirm {
			return errors.New("Password mismatch, not changing.")
		}
	}

	err = auth.Passwd(c, username, password, newPassword)

	if err != nil {
		fmt.Fprint(os.Stderr, "Password change failed: ")
		return err
	}

	fmt.Println("Password change succeeded.")
	return nil
}

// Cancel deletes a user's account.
func Cancel(username string, password string, yes bool) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	if username == "" || password != "" {
		fmt.Println("Please log in again in order to cancel this account")

		if err = Login(c.ControllerURL.String(), username, password, c.SSLVerify); err != nil {
			return err
		}
	}

	if yes == false {
		confirm := ""

		c, err = client.New()

		if err != nil {
			return err
		}

		deletedUser := username

		if deletedUser == "" {
			deletedUser = c.Username
		}

		fmt.Printf("cancel account %s at %s? (y/N): ", deletedUser, c.ControllerURL.String())
		fmt.Scanln(&confirm)

		if strings.ToLower(confirm) == "y" {
			yes = true
		}
	}

	if yes == false {
		fmt.Fprintln(os.Stderr, "Account not changed")
		return nil
	}

	err = auth.Delete(c, username)

	if err != nil {
		return err
	}

	// If user targets themselves, logout.
	if username == "" || c.Username == username {
		if err := client.Delete(); err != nil {
			return err
		}
	}

	fmt.Println("Account cancelled")
	return nil
}

// Whoami prints the logged in user.
func Whoami() error {
	c, err := client.New()

	if err != nil {
		return err
	}

	fmt.Printf("You are %s at %s\n", c.Username, c.ControllerURL.String())
	return nil
}

// Regenerate regenenerates a user's token.
func Regenerate(username string, all bool) error {
	c, err := client.New()

	if err != nil {
		return err
	}

	token, err := auth.Regenerate(c, username, all)

	if err != nil {
		return err
	}

	if username == "" && all == false {
		c.Token = token

		err = c.Save()

		if err != nil {
			return err
		}
	}

	fmt.Println("Token Regenerated")
	return nil
}

func readPassword() (string, error) {
	password, err := terminal.ReadPassword(int(syscall.Stdin))

	return string(password), err
}

func chooseScheme(u url.URL) (url.URL, error) {
	if u.Scheme == "" {
		u.Scheme = "http"
		u, err := url.Parse(u.String())

		if err != nil {
			return url.URL{}, err
		}

		return *u, nil
	}

	return u, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"

	"runtime"
	"src/filepicker"
	"src/tknptr"
	"strconv"
	"strings"
)

/*
  REVISION HISTORY
  ----------------
   6 Apr 13 -- First modified version of module.  I will use VLI to compare all digits of the hashes.
  23 Apr 13 -- Fixed problem of a single line in the hashes file, that does not contain an EOL character, causes
                an immediate return without processing of the characters just read in.
  24 Apr 13 -- Added output of which file either matches or does not match.
  19 Sep 16 -- Finished conversion to Go, that was started 13 Sep 16.  Added the removed of '*' which is part of a std linux formated hash file.  And I forgot that
                 the routine allowed either order in the file.  If the token has a '.' I assume it is a filename, else it is a hash value.
  21 Sep 16 -- Fixed the case issue in tokenize.GetToken.  Edited code here to correspond to this fix.
  25 Nov 16 -- Need to not panic when target file is not found, only panic when hash file is not found.
                 And added a LastCompiled message and string.
  13 Oct 17 -- No changes here, but tokenize was changed so that horizontal tab char is now a delim.
  14 Oct 17 -- Tweaked output a bit.  And added executable timestamp code.
  19 Oct 17 -- Added ability to ignore the * that standard hash files for linux use.
  22 Oct 17 -- Added filepicker.
  21 Jan 18 -- Really ignore *.  Before method did not work.
  26 Jan 18 -- Changed tokenize so that SetMapDelim change sticks and actually works.
  13 Nov 18 -- Will use "-" and "_" also to detect a filename token.
  10 Nov 19 -- Now uses ToLower to compare the string hashes, to ignore case.
  15 Jul 20 -- Decided to make better guesses.  Sha1 has 40 digits, Sha256 has 64 digits and Sha512 has 128 digits.
  27 Sep 20 -- From help file of TakeCommand: MD-5 has 32 digits, SHA384 has 96 digits, and the above hash lengths are correct.
                 And I'm going to change from tokenize to tknptr.  Just to see if it works.
  25 Feb 21 -- Added 999 as a stop code.
   3 Mar 21 -- Now called sha.go, which will always use hash length, while ignoring file extension.
                 Errors now go to Stderr.  Uses bytes buffer to read sha file using io.ReadAll. and go 1.15.8
   7 Mar 21 -- added strings.TrimSpace
   8 Apr 21 -- Converted import list to module named src.  So essentially, very little has changed except for these import statements.
  13 Feb 22 -- filepicker API changed recently.  So I'm updating the code here that uses filepicker.
   9 Mar 22 -- Using package constants instead of my magic numbers.
*/

const LastCompiled = "9 Mar 2022"

//* ************************* MAIN ***************************************************************
func main() {

	const K = 1024
	const M = 1024 * 1024

	const (
		undetermined = iota
		md5hash
		sha1hash
		sha256hash
		sha384hash
		sha512hash
	)

	const ReadBufferSize = M

	var HashName = [...]string{"undetermined", "md5", "sha1", "sha256", "sha384", "sha512"}
	var ans, Filename string
	var WhichHash int
	var TargetFilename, HashValueReadFromFile, HashValueComputedStr string
	var hasher hash.Hash
	var FileSize int64

	fmt.Print(" sha.go.  GOOS =", runtime.GOOS, ".  ARCH=", runtime.GOARCH)
	fmt.Println(".  Last altered", LastCompiled, ", compiled using", runtime.Version())
	workingdir, _ := os.Getwd()
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	LastLinkedTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")
	fmt.Printf("%s has timestamp of %s.  Working directory is %s.  Full name of executable is %s.\n", ExecFI.Name(), LastLinkedTimeStamp, workingdir, execname)
	fmt.Println()

	// filepicker stuff.

	if len(os.Args) <= 1 { // need to use filepicker
		filenames, err := filepicker.GetFilenames("*.sha*")
		if err != nil {
			fmt.Fprintf(os.Stderr, " Error from filepicker is %v.  Exiting \n", err)
			os.Exit(1)
		}
		for i := 0; i < min(len(filenames), 20); i++ {
			fmt.Println("filename[", i, "] is", filenames[i])
		}
		fmt.Print(" Enter filename choice : ")
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			ans = "0"
		} else if ans == "999" {
			fmt.Println(" Stop code entered.  Exiting.")
			os.Exit(0)
		}
		i, err := strconv.Atoi(ans)
		if err == nil {
			Filename = filenames[i]
		} else {
			s := strings.ToUpper(ans)
			s = strings.TrimSpace(s)
			s0 := s[0]
			i = int(s0 - 'A')
			Filename = filenames[i]
		}
		fmt.Println(" Picked filename is", Filename)
	} else { // will use filename entered on commandline
		//            Filename = getcommandline.GetCommandLineString()  removed 3/3/21, as os.Args is fine.
		Filename = os.Args[1]
	}

	fmt.Println()

	// Now ignores extension, always going by hash length.

	// Read and parse the file with the hashes.

	filebyteslice := make([]byte, 0, 2000)
	f, err := os.Open(Filename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	filebyteslice, err = ioutil.ReadAll(f)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytesbuffer := bytes.NewBuffer(filebyteslice)

	for { /* to read multiple lines */
		FileSize = 0
		WhichHash = undetermined // reset it for this next line, allowing multiple types of hashes in same file.

		inputline, err := bytesbuffer.ReadString('\n')
		inputline = strings.TrimSpace(inputline)  // probably not needed as I tokenize this, but I want to see if this works.
		if err == io.EOF && len(inputline) == 0 { // reached EOF condition, there are no more lines to read, and no line
			break
		} else if len(inputline) == 0 && err != nil {
			fmt.Fprintln(os.Stderr, "While reading from the HashesFile:", err)
			os.Exit(1)
		}

		if strings.HasPrefix(inputline, ";") || strings.HasPrefix(inputline, "#") || (len(inputline) <= 10) {
			continue
		} // allow comments and essentially blank lines

		tokenPtr := tknptr.NewToken(inputline)
		tokenPtr.SetMapDelim('*')
		FirstToken, EOL := tokenPtr.GetTokenString(false)
		if EOL {
			fmt.Fprintln(os.Stderr, " EOL while getting 1st token in the hashing file.  Skipping to next line.")
			continue
		}
		hashlength := 0

		if strings.ContainsRune(FirstToken.Str, '.') || strings.ContainsRune(FirstToken.Str, '-') ||
			strings.ContainsRune(FirstToken.Str, '_') { // have filename first on line
			TargetFilename = FirstToken.Str
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get hash string from the line in the file
			if EOL {
				fmt.Fprintln(os.Stderr, " Got EOL while getting HashValue (2nd) token in the hashing file.  Skipping")
				continue
			}
			HashValueReadFromFile = SecondToken.Str
			hashlength = len(SecondToken.Str)

		} else { // have hash first on line
			HashValueReadFromFile = FirstToken.Str
			hashlength = len(FirstToken.Str)
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get name of file on which to compute the hash
			if EOL {
				fmt.Fprintln(os.Stderr, " EOL while gatting TargetFilename token in the hashing file.  Skipping")
				continue
			}

			if strings.ContainsRune(SecondToken.Str, '*') { // If it contains a *, it will be the first position.
				SecondToken.Str = SecondToken.Str[1:]
				if strings.ContainsRune(SecondToken.Str, '*') { // this should not happen
					fmt.Println(" Filename token still contains a * character.  Str:", SecondToken.Str, "  Skipping.")
					continue
				}
			}
			TargetFilename = (SecondToken.Str)
		} /* if have filename first or hash value first */

		//   now to compute the hash, compare them, and output results

		// Create Hash Section
		TargetFile, err := os.Open(TargetFilename)
		if os.IsNotExist(err) {
			fmt.Fprintln(os.Stderr, TargetFilename, " does not exist.  Skipping.")
			continue
		} else { // we know that the file exists
			check(err, " Error opening TargetFilename.")
		}

		defer TargetFile.Close()

		if WhichHash == undetermined {
			if hashlength == 2*sha256.Size { // 64, and the Size constant is number of bytes, not number of digits.
				WhichHash = sha256hash
			} else if hashlength == 2*sha512.Size { // 128
				WhichHash = sha512hash
			} else if hashlength == 2*sha1.Size { // 40
				WhichHash = sha1hash
			} else if hashlength == 2*sha512.Size384 { // 96
				WhichHash = sha384hash
			} else if hashlength == 2*md5.Size { // 32
				WhichHash = md5hash
			} else {
				fmt.Fprintln(os.Stderr, " Could not determine hash type for file.  Skipping.")
				continue
			}
			fmt.Println(" hash determined by length to be", HashName[WhichHash])
			fmt.Println()
		}

		switch WhichHash { // Initialing case switch on WhichHash
		case md5hash:
			hasher = md5.New()
		case sha1hash:
			hasher = sha1.New()
		case sha256hash:
			hasher = sha256.New()
		case sha384hash:
			hasher = sha512.New384()
		case sha512hash:
			hasher = sha512.New()
		default:
			hasher = sha256.New()
		}

		FileSize, err = io.Copy(hasher, TargetFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "Skipped.")
			continue
		}
		HashValueComputedStr = hex.EncodeToString(hasher.Sum(nil))

		// I got the idea to use the different base64 versions and my own hex converter code, just to see.
		// And I can also use Sfprintf with the %x verb.  base64 versions are not useful as they use a larger
		// character set than hex.  I deleted all references to the base64 versions.  And the hex encoded and
		// Sprintf using %x were the same, so I removed the sprintf code.
		//    HashValueComputedSprintf := fmt.Sprintf("%x",hasher.Sum(nil));

		fmt.Printf(" Filename  = %s, filesize = %d, using hash %s.\n", TargetFilename, FileSize, HashName[WhichHash])
		fmt.Println("       Read From File:", HashValueReadFromFile)
		fmt.Println(" Computed hex encoded:", HashValueComputedStr)

		if strings.ToLower(HashValueReadFromFile) == strings.ToLower(HashValueComputedStr) {
			fmt.Print(" Matched.")
		} else {
			fmt.Print(" Not matched.")
		} /* if hashes */
		TargetFile.Close() // Close the handle to allow opening a target from the next line, if there is one.
		fmt.Println()
		fmt.Println()
	} // outer LOOP to read multiple lines
} // Main for sha.go.

// ------------------------------------------------------- check -------------------------------
func check(e error, msg string) {
	if e != nil {
		fmt.Println(msg)
		panic(e)
	}
}

// ------------------------------------------------------- min ---------------------------------
func min(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}
<commit_msg>05/21/2022 06:26:51 PM sha/sha.go -- fixed typo in beginning comments.<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"

	"runtime"
	"src/filepicker"
	"src/tknptr"
	"strconv"
	"strings"
)

/*
  REVISION HISTORY
  ----------------
   6 Apr 13 -- First modified version of module.  I will use VLI to compare all digits of the hashes.
  23 Apr 13 -- Fixed problem of a single line in the hashes file, that does not contain an EOL character, causes
                an immediate return without processing of the characters just read in.
  24 Apr 13 -- Added output of which file either matches or does not match.
  19 Sep 16 -- Finished conversion to Go, that was started 13 Sep 16.  Added the removal of '*' which is part of a std linux formatted hash file.  And I forgot that
                 the routine allowed either order in the file.  If the token has a '.' I assume it is a filename, else it is a hash value.
  21 Sep 16 -- Fixed the case issue in tokenize.GetToken.  Edited code here to correspond to this fix.
  25 Nov 16 -- Need to not panic when target file is not found, only panic when hash file is not found.
                 And added a LastCompiled message and string.
  13 Oct 17 -- No changes here, but tokenize was changed so that horizontal tab char is now a delim.
  14 Oct 17 -- Tweaked output a bit.  And added executable timestamp code.
  19 Oct 17 -- Added ability to ignore the * that standard hash files for linux use.
  22 Oct 17 -- Added filepicker.
  21 Jan 18 -- Really ignore *.  Before method did not work.
  26 Jan 18 -- Changed tokenize so that SetMapDelim change sticks and actually works.
  13 Nov 18 -- Will use "-" and "_" also to detect a filename token.
  10 Nov 19 -- Now uses ToLower to compare the string hashes, to ignore case.
  15 Jul 20 -- Decided to make better guesses.  Sha1 has 40 digits, Sha256 has 64 digits and Sha512 has 128 digits.
  27 Sep 20 -- From help file of TakeCommand: MD-5 has 32 digits, SHA384 has 96 digits, and the above hash lengths are correct.
                 And I'm going to change from tokenize to tknptr.  Just to see if it works.
  25 Feb 21 -- Added 999 as a stop code.
   3 Mar 21 -- Now called sha.go, which will always use hash length, while ignoring file extension.
                 Errors now go to Stderr.  Uses bytes buffer to read sha file using io.ReadAll. and go 1.15.8
   7 Mar 21 -- added strings.TrimSpace
   8 Apr 21 -- Converted import list to module named src.  So essentially, very little has changed except for these import statements.
  13 Feb 22 -- filepicker API changed recently.  So I'm updating the code here that uses filepicker.
   9 Mar 22 -- Using package constants instead of my magic numbers.
*/

const LastCompiled = "9 Mar 2022"

//* ************************* MAIN ***************************************************************
func main() {

	const K = 1024
	const M = 1024 * 1024

	const (
		undetermined = iota
		md5hash
		sha1hash
		sha256hash
		sha384hash
		sha512hash
	)

	const ReadBufferSize = M

	var HashName = [...]string{"undetermined", "md5", "sha1", "sha256", "sha384", "sha512"}
	var ans, Filename string
	var WhichHash int
	var TargetFilename, HashValueReadFromFile, HashValueComputedStr string
	var hasher hash.Hash
	var FileSize int64

	fmt.Print(" sha.go.  GOOS =", runtime.GOOS, ".  ARCH=", runtime.GOARCH)
	fmt.Println(".  Last altered", LastCompiled, ", compiled using", runtime.Version())
	workingdir, _ := os.Getwd()
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	LastLinkedTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")
	fmt.Printf("%s has timestamp of %s.  Working directory is %s.  Full name of executable is %s.\n", ExecFI.Name(), LastLinkedTimeStamp, workingdir, execname)
	fmt.Println()

	// filepicker stuff.

	if len(os.Args) <= 1 { // need to use filepicker
		filenames, err := filepicker.GetFilenames("*.sha*")
		if err != nil {
			fmt.Fprintf(os.Stderr, " Error from filepicker is %v.  Exiting \n", err)
			os.Exit(1)
		}
		for i := 0; i < min(len(filenames), 20); i++ {
			fmt.Println("filename[", i, "] is", filenames[i])
		}
		fmt.Print(" Enter filename choice : ")
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			ans = "0"
		} else if ans == "999" {
			fmt.Println(" Stop code entered.  Exiting.")
			os.Exit(0)
		}
		i, err := strconv.Atoi(ans)
		if err == nil {
			Filename = filenames[i]
		} else {
			s := strings.ToUpper(ans)
			s = strings.TrimSpace(s)
			s0 := s[0]
			i = int(s0 - 'A')
			Filename = filenames[i]
		}
		fmt.Println(" Picked filename is", Filename)
	} else { // will use filename entered on commandline
		//            Filename = getcommandline.GetCommandLineString()  removed 3/3/21, as os.Args is fine.
		Filename = os.Args[1]
	}

	fmt.Println()

	// Now ignores extension, always going by hash length.

	// Read and parse the file with the hashes.

	filebyteslice := make([]byte, 0, 2000)
	f, err := os.Open(Filename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	filebyteslice, err = ioutil.ReadAll(f)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytesbuffer := bytes.NewBuffer(filebyteslice)

	for { /* to read multiple lines */
		FileSize = 0
		WhichHash = undetermined // reset it for this next line, allowing multiple types of hashes in same file.

		inputline, err := bytesbuffer.ReadString('\n')
		inputline = strings.TrimSpace(inputline)  // probably not needed as I tokenize this, but I want to see if this works.
		if err == io.EOF && len(inputline) == 0 { // reached EOF condition, there are no more lines to read, and no line
			break
		} else if len(inputline) == 0 && err != nil {
			fmt.Fprintln(os.Stderr, "While reading from the HashesFile:", err)
			os.Exit(1)
		}

		if strings.HasPrefix(inputline, ";") || strings.HasPrefix(inputline, "#") || (len(inputline) <= 10) {
			continue
		} // allow comments and essentially blank lines

		tokenPtr := tknptr.NewToken(inputline)
		tokenPtr.SetMapDelim('*')
		FirstToken, EOL := tokenPtr.GetTokenString(false)
		if EOL {
			fmt.Fprintln(os.Stderr, " EOL while getting 1st token in the hashing file.  Skipping to next line.")
			continue
		}
		hashlength := 0

		if strings.ContainsRune(FirstToken.Str, '.') || strings.ContainsRune(FirstToken.Str, '-') ||
			strings.ContainsRune(FirstToken.Str, '_') { // have filename first on line
			TargetFilename = FirstToken.Str
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get hash string from the line in the file
			if EOL {
				fmt.Fprintln(os.Stderr, " Got EOL while getting HashValue (2nd) token in the hashing file.  Skipping")
				continue
			}
			HashValueReadFromFile = SecondToken.Str
			hashlength = len(SecondToken.Str)

		} else { // have hash first on line
			HashValueReadFromFile = FirstToken.Str
			hashlength = len(FirstToken.Str)
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get name of file on which to compute the hash
			if EOL {
				fmt.Fprintln(os.Stderr, " EOL while gatting TargetFilename token in the hashing file.  Skipping")
				continue
			}

			if strings.ContainsRune(SecondToken.Str, '*') { // If it contains a *, it will be the first position.
				SecondToken.Str = SecondToken.Str[1:]
				if strings.ContainsRune(SecondToken.Str, '*') { // this should not happen
					fmt.Println(" Filename token still contains a * character.  Str:", SecondToken.Str, "  Skipping.")
					continue
				}
			}
			TargetFilename = (SecondToken.Str)
		} /* if have filename first or hash value first */

		//   now to compute the hash, compare them, and output results

		// Create Hash Section
		TargetFile, err := os.Open(TargetFilename)
		if os.IsNotExist(err) {
			fmt.Fprintln(os.Stderr, TargetFilename, " does not exist.  Skipping.")
			continue
		} else { // we know that the file exists
			check(err, " Error opening TargetFilename.")
		}

		defer TargetFile.Close()

		if WhichHash == undetermined {
			if hashlength == 2*sha256.Size { // 64, and the Size constant is number of bytes, not number of digits.
				WhichHash = sha256hash
			} else if hashlength == 2*sha512.Size { // 128
				WhichHash = sha512hash
			} else if hashlength == 2*sha1.Size { // 40
				WhichHash = sha1hash
			} else if hashlength == 2*sha512.Size384 { // 96
				WhichHash = sha384hash
			} else if hashlength == 2*md5.Size { // 32
				WhichHash = md5hash
			} else {
				fmt.Fprintln(os.Stderr, " Could not determine hash type for file.  Skipping.")
				continue
			}
			fmt.Println(" hash determined by length to be", HashName[WhichHash])
			fmt.Println()
		}

		switch WhichHash { // Initialing case switch on WhichHash
		case md5hash:
			hasher = md5.New()
		case sha1hash:
			hasher = sha1.New()
		case sha256hash:
			hasher = sha256.New()
		case sha384hash:
			hasher = sha512.New384()
		case sha512hash:
			hasher = sha512.New()
		default:
			hasher = sha256.New()
		}

		FileSize, err = io.Copy(hasher, TargetFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "Skipped.")
			continue
		}
		HashValueComputedStr = hex.EncodeToString(hasher.Sum(nil))

		// I got the idea to use the different base64 versions and my own hex converter code, just to see.
		// And I can also use Sfprintf with the %x verb.  base64 versions are not useful as they use a larger
		// character set than hex.  I deleted all references to the base64 versions.  And the hex encoded and
		// Sprintf using %x were the same, so I removed the sprintf code.
		//    HashValueComputedSprintf := fmt.Sprintf("%x",hasher.Sum(nil));

		fmt.Printf(" Filename  = %s, filesize = %d, using hash %s.\n", TargetFilename, FileSize, HashName[WhichHash])
		fmt.Println("       Read From File:", HashValueReadFromFile)
		fmt.Println(" Computed hex encoded:", HashValueComputedStr)

		if strings.ToLower(HashValueReadFromFile) == strings.ToLower(HashValueComputedStr) {
			fmt.Print(" Matched.")
		} else {
			fmt.Print(" Not matched.")
		} /* if hashes */
		TargetFile.Close() // Close the handle to allow opening a target from the next line, if there is one.
		fmt.Println()
		fmt.Println()
	} // outer LOOP to read multiple lines
} // Main for sha.go.

// ------------------------------------------------------- check -------------------------------
func check(e error, msg string) {
	if e != nil {
		fmt.Println(msg)
		panic(e)
	}
}

// ------------------------------------------------------- min ---------------------------------
func min(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Huawei Technologies Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/astaxie/beego/httplib"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	"github.com/opensds/opensds/pkg/model"
	"github.com/opensds/opensds/pkg/utils"
	"github.com/opensds/opensds/pkg/utils/constants"
)

func checkHTTPResponseStatusCode(resp *http.Response) error {
	if 400 <= resp.StatusCode && resp.StatusCode <= 599 {
		return fmt.Errorf("response == %d, %s", resp.StatusCode, http.StatusText(resp.StatusCode))
	}
	return nil
}

func NewHttpError(code int, msg string) error {
	return &HttpError{Code: code, Msg: msg}
}

type HttpError struct {
	Code int
	Msg  string
}

func (e *HttpError) Decode() {
	errSpec := model.ErrorSpec{}
	err := json.Unmarshal([]byte(e.Msg), &errSpec)
	if err == nil {
		e.Msg = errSpec.Message
	}
}

func (e *HttpError) Error() string {
	e.Decode()
	return fmt.Sprintf("Code: %v, Desc: %s, Msg: %v", e.Code, http.StatusText(e.Code), e.Msg)
}

// ParamOption
type HeaderOption map[string]string

// Receiver
type Receiver interface {
	Recv(url string, method string, input interface{}, output interface{}) error
}

// NewReceiver
func NewReceiver() Receiver {
	return &receiver{}
}

func request(url string, method string, headers HeaderOption, input interface{}, output interface{}) error {
	req := httplib.NewBeegoRequest(url, strings.ToUpper(method))
	// init body
	if input != nil {
		req.JSONBody(input)
	}
	//init header
	if headers != nil {
		for k, v := range headers {
			req.Header(k, v)
		}
	}
	// Get http response.
	resp, err := req.Response()
	if err != nil {
		return err
	}
	rbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if 400 <= resp.StatusCode && resp.StatusCode <= 599 {
		return NewHttpError(resp.StatusCode, string(rbody))
	}

	// If the format of output is nil, skip unmarshaling the result.
	if output == nil {
		return nil
	}
	if err = json.Unmarshal(rbody, output); err != nil {
		return fmt.Errorf("failed to unmarshal result message: %v", err)
	}
	return nil
}

type receiver struct{}

func (*receiver) Recv(url string, method string, input interface{}, output interface{}) error {
	return request(url, method, nil, input, output)
}

func NewKeystoneReciver(auth *KeystoneAuthOptions) Receiver {
	k := &KeystoneReciver{auth: auth}
	k.GetToken()
	return k
}

type KeystoneReciver struct {
	auth *KeystoneAuthOptions
}

func (k *KeystoneReciver) GetToken() error {
	opts := gophercloud.AuthOptions{
		IdentityEndpoint: k.auth.IdentityEndpoint,
		Username:         k.auth.Username,
		UserID:           k.auth.UserID,
		Password:         k.auth.Password,
		DomainID:         k.auth.DomainID,
		DomainName:       k.auth.DomainName,
		TenantID:         k.auth.TenantID,
		TenantName:       k.auth.TenantName,
		AllowReauth:      k.auth.AllowReauth,
	}

	provider, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		log.Printf("When get auth client:", err)
		return err
	}

	// Only support keystone v3
	identity, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{})
	if err != nil {
		log.Printf("When get identity session:", err)
		return err
	}
	r := tokens.Create(identity, &opts)
	token, err := r.ExtractToken()
	if err != nil {
		log.Printf("When get extract token session:", err)
		return err
	}
	project, err := r.ExtractProject()
	if err != nil {
		log.Printf("When get extract project session:", err)
		return err
	}
	k.auth.TenantID = project.ID
	k.auth.TokenID = token.ID
	return nil
}

func (k *KeystoneReciver) Recv(url string, method string, body interface{}, output interface{}) error {
	desc := fmt.Sprintf("%s %s", method, url)
	return utils.Retry(2, desc, true, func(retryIdx int, lastErr error) error {
		if retryIdx > 0 {
			err, ok := lastErr.(*HttpError)
			if ok && err.Code == http.StatusUnauthorized {
				k.GetToken()
			} else {
				return lastErr
			}
		}

		headers := HeaderOption{}
		headers[constants.AuthTokenHeader] = k.auth.TokenID
		return request(url, method, headers, body, output)
	})
}
<commit_msg>Fix a bug in CI work<commit_after>// Copyright (c) 2017 Huawei Technologies Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/astaxie/beego/httplib"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	"github.com/opensds/opensds/pkg/model"
	"github.com/opensds/opensds/pkg/utils"
	"github.com/opensds/opensds/pkg/utils/constants"
)

func checkHTTPResponseStatusCode(resp *http.Response) error {
	if 400 <= resp.StatusCode && resp.StatusCode <= 599 {
		return fmt.Errorf("response == %d, %s", resp.StatusCode, http.StatusText(resp.StatusCode))
	}
	return nil
}

func NewHttpError(code int, msg string) error {
	return &HttpError{Code: code, Msg: msg}
}

type HttpError struct {
	Code int
	Msg  string
}

func (e *HttpError) Decode() {
	errSpec := model.ErrorSpec{}
	err := json.Unmarshal([]byte(e.Msg), &errSpec)
	if err == nil {
		e.Msg = errSpec.Message
	}
}

func (e *HttpError) Error() string {
	e.Decode()
	return fmt.Sprintf("Code: %v, Desc: %s, Msg: %v", e.Code, http.StatusText(e.Code), e.Msg)
}

// ParamOption
type HeaderOption map[string]string

// Receiver
type Receiver interface {
	Recv(url string, method string, input interface{}, output interface{}) error
}

// NewReceiver
func NewReceiver() Receiver {
	return &receiver{}
}

func request(url string, method string, headers HeaderOption, input interface{}, output interface{}) error {
	req := httplib.NewBeegoRequest(url, strings.ToUpper(method))
	// init body
	if input != nil {
		req.JSONBody(input)
	}
	//init header
	if headers != nil {
		for k, v := range headers {
			req.Header(k, v)
		}
	}
	// Get http response.
	resp, err := req.Response()
	if err != nil {
		return err
	}
	rbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if 400 <= resp.StatusCode && resp.StatusCode <= 599 {
		return NewHttpError(resp.StatusCode, string(rbody))
	}

	// If the format of output is nil, skip unmarshaling the result.
	if output == nil {
		return nil
	}
	if err = json.Unmarshal(rbody, output); err != nil {
		return fmt.Errorf("failed to unmarshal result message: %v", err)
	}
	return nil
}

type receiver struct{}

func (*receiver) Recv(url string, method string, input interface{}, output interface{}) error {
	return request(url, method, nil, input, output)
}

func NewKeystoneReciver(auth *KeystoneAuthOptions) Receiver {
	k := &KeystoneReciver{auth: auth}
	k.GetToken()
	return k
}

type KeystoneReciver struct {
	auth *KeystoneAuthOptions
}

func (k *KeystoneReciver) GetToken() error {
	opts := gophercloud.AuthOptions{
		IdentityEndpoint: k.auth.IdentityEndpoint,
		Username:         k.auth.Username,
		UserID:           k.auth.UserID,
		Password:         k.auth.Password,
		DomainID:         k.auth.DomainID,
		DomainName:       k.auth.DomainName,
		TenantID:         k.auth.TenantID,
		TenantName:       k.auth.TenantName,
		AllowReauth:      k.auth.AllowReauth,
	}

	provider, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		return fmt.Errorf("When get auth client: %v", err)
	}

	// Only support keystone v3
	identity, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{})
	if err != nil {
		return fmt.Errorf("When get identity session: %v", err)
	}
	r := tokens.Create(identity, &opts)
	token, err := r.ExtractToken()
	if err != nil {
		return fmt.Errorf("When get extract token session: %v", err)
	}
	project, err := r.ExtractProject()
	if err != nil {
		return fmt.Errorf("When get extract project session: %v", err)
	}
	k.auth.TenantID = project.ID
	k.auth.TokenID = token.ID
	return nil
}

func (k *KeystoneReciver) Recv(url string, method string, body interface{}, output interface{}) error {
	desc := fmt.Sprintf("%s %s", method, url)
	return utils.Retry(2, desc, true, func(retryIdx int, lastErr error) error {
		if retryIdx > 0 {
			err, ok := lastErr.(*HttpError)
			if ok && err.Code == http.StatusUnauthorized {
				k.GetToken()
			} else {
				return lastErr
			}
		}

		headers := HeaderOption{}
		headers[constants.AuthTokenHeader] = k.auth.TokenID
		return request(url, method, headers, body, output)
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2014 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package etcd

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/coreos/etcd/config"
)

const (
	bootstrapId = 0xBEEF
)

type garbageHandler struct {
	t       *testing.T
	success bool
	sync.Mutex
}

func (g *garbageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, client")
	wp := fmt.Sprint("/v2/keys/_etcd/registry/1/", bootstrapId)
	if gp := r.URL.String(); gp != wp {
		g.t.Fatalf("url = %s, want %s", gp, wp)
	}
	g.Lock()
	defer g.Unlock()

	g.success = true
}

func TestBadDiscoveryService(t *testing.T) {
	g := garbageHandler{t: t}
	ts := httptest.NewServer(&g)

	c := config.New()
	c.Discovery = ts.URL + "/v2/keys/_etcd/registry/1"
	_, _, err := buildServer(c, bootstrapId)
	w := `discovery service error`
	if err == nil || !strings.HasPrefix(err.Error(), w) {
		t.Errorf("err = %v, want %s prefix", err, w)
	}

	g.Lock()
	defer g.Unlock()
	if !g.success {
		t.Fatal("Discovery server never called")
	}
	ts.Close()
	afterTest(t)
}

func TestBadDiscoveryServiceWithAdvisedPeers(t *testing.T) {
	g := garbageHandler{t: t}
	ts := httptest.NewServer(&g)

	es, hs := buildCluster(1, false)
	waitCluster(t, es)

	c := config.New()
	c.Discovery = ts.URL + "/v2/keys/_etcd/registry/1"
	c.Peers = []string{hs[0].URL}
	_, _, err := buildServer(c, bootstrapId)
	w := `discovery service error`
	if err == nil || !strings.HasPrefix(err.Error(), w) {
		t.Errorf("err = %v, want %s prefix", err, w)
	}

	for i := range hs {
		es[len(hs)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	ts.Close()
	afterTest(t)
}

func TestBootstrapByDiscoveryService(t *testing.T) {
	de, dh, _ := buildServer(config.New(), genId())

	c := config.New()
	c.Discovery = dh.URL + "/v2/keys/_etcd/registry/1"
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}

	destroyServer(e, h)
	destroyServer(de, dh)
	afterTest(t)
}

func TestRunByAdvisedPeers(t *testing.T) {
	es, hs := buildCluster(1, false)
	waitCluster(t, es)

	c := config.New()
	c.Peers = []string{hs[0].URL}
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}
	w := es[0].id
	if g, _ := waitLeader(append(es, e)); g != w {
		t.Errorf("leader = %d, want %d", g, w)
	}

	destroyServer(e, h)
	for i := range hs {
		es[len(hs)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	afterTest(t)
}

func TestRunByDiscoveryService(t *testing.T) {
	de, dh, _ := buildServer(config.New(), genId())

	tc := NewTestClient()
	v := url.Values{}
	v.Set("value", "started")
	resp, _ := tc.PutForm(fmt.Sprintf("%s%s", dh.URL, "/v2/keys/_etcd/registry/1/_state"), v)
	if g := resp.StatusCode; g != http.StatusCreated {
		t.Fatalf("put status = %d, want %d", g, http.StatusCreated)
	}
	resp.Body.Close()

	v.Set("value", dh.URL)
	resp, _ = tc.PutForm(fmt.Sprintf("%s%s%d", dh.URL, "/v2/keys/_etcd/registry/1/", de.id), v)
	if g := resp.StatusCode; g != http.StatusCreated {
		t.Fatalf("put status = %d, want %d", g, http.StatusCreated)
	}
	resp.Body.Close()

	c := config.New()
	c.Discovery = dh.URL + "/v2/keys/_etcd/registry/1"
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}
	w := de.id
	if g, _ := waitLeader([]*Server{e, de}); g != w {
		t.Errorf("leader = %d, want %d", g, w)
	}

	destroyServer(e, h)
	destroyServer(de, dh)
	afterTest(t)
}

func buildServer(c *config.Config, id int64) (e *Server, h *httptest.Server, err error) {
	e, h = initTestServer(c, id, false)
	go func() { err = e.Run() }()
	for {
		if e.mode.Get() == participantMode {
			break
		}
		if err != nil {
			destroyServer(e, h)
			return nil, nil, err
		}
		time.Sleep(10 * time.Millisecond)
	}
	return e, h, nil
}

func destroyServer(e *Server, h *httptest.Server) {
	e.Stop()
	h.Close()
}
<commit_msg>etcd: add a bootstrap test<commit_after>/*
Copyright 2014 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package etcd

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/coreos/etcd/config"
)

const (
	bootstrapId = 0xBEEF
)

type garbageHandler struct {
	t       *testing.T
	success bool
	sync.Mutex
}

func (g *garbageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, client")
	wp := fmt.Sprint("/v2/keys/_etcd/registry/1/", bootstrapId)
	if gp := r.URL.String(); gp != wp {
		g.t.Fatalf("url = %s, want %s", gp, wp)
	}
	g.Lock()
	defer g.Unlock()

	g.success = true
}

func TestBadDiscoveryService(t *testing.T) {
	g := garbageHandler{t: t}
	ts := httptest.NewServer(&g)

	c := config.New()
	c.Discovery = ts.URL + "/v2/keys/_etcd/registry/1"
	_, _, err := buildServer(c, bootstrapId)
	w := `discovery service error`
	if err == nil || !strings.HasPrefix(err.Error(), w) {
		t.Errorf("err = %v, want %s prefix", err, w)
	}

	g.Lock()
	defer g.Unlock()
	if !g.success {
		t.Fatal("Discovery server never called")
	}
	ts.Close()
	afterTest(t)
}

func TestBadDiscoveryServiceWithAdvisedPeers(t *testing.T) {
	g := garbageHandler{t: t}
	ts := httptest.NewServer(&g)

	es, hs := buildCluster(1, false)
	waitCluster(t, es)

	c := config.New()
	c.Discovery = ts.URL + "/v2/keys/_etcd/registry/1"
	c.Peers = []string{hs[0].URL}
	_, _, err := buildServer(c, bootstrapId)
	w := `discovery service error`
	if err == nil || !strings.HasPrefix(err.Error(), w) {
		t.Errorf("err = %v, want %s prefix", err, w)
	}

	for i := range hs {
		es[len(hs)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	ts.Close()
	afterTest(t)
}

func TestBootstrapByEmptyPeers(t *testing.T) {
	c := config.New()
	id := genId()
	e, h, err := buildServer(c, id)

	if err != nil {
		t.Error(err)
	}
	if e.p.node.Leader() != id {
		t.Error("leader = %x, want %x", e.p.node.Leader(), id)
	}
	destroyServer(e, h)
	afterTest(t)
}

func TestBootstrapByDiscoveryService(t *testing.T) {
	de, dh, _ := buildServer(config.New(), genId())

	c := config.New()
	c.Discovery = dh.URL + "/v2/keys/_etcd/registry/1"
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}

	destroyServer(e, h)
	destroyServer(de, dh)
	afterTest(t)
}

func TestRunByAdvisedPeers(t *testing.T) {
	es, hs := buildCluster(1, false)
	waitCluster(t, es)

	c := config.New()
	c.Peers = []string{hs[0].URL}
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}
	w := es[0].id
	if g, _ := waitLeader(append(es, e)); g != w {
		t.Errorf("leader = %d, want %d", g, w)
	}

	destroyServer(e, h)
	for i := range hs {
		es[len(hs)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	afterTest(t)
}

func TestRunByDiscoveryService(t *testing.T) {
	de, dh, _ := buildServer(config.New(), genId())

	tc := NewTestClient()
	v := url.Values{}
	v.Set("value", "started")
	resp, _ := tc.PutForm(fmt.Sprintf("%s%s", dh.URL, "/v2/keys/_etcd/registry/1/_state"), v)
	if g := resp.StatusCode; g != http.StatusCreated {
		t.Fatalf("put status = %d, want %d", g, http.StatusCreated)
	}
	resp.Body.Close()

	v.Set("value", dh.URL)
	resp, _ = tc.PutForm(fmt.Sprintf("%s%s%d", dh.URL, "/v2/keys/_etcd/registry/1/", de.id), v)
	if g := resp.StatusCode; g != http.StatusCreated {
		t.Fatalf("put status = %d, want %d", g, http.StatusCreated)
	}
	resp.Body.Close()

	c := config.New()
	c.Discovery = dh.URL + "/v2/keys/_etcd/registry/1"
	e, h, err := buildServer(c, bootstrapId)
	if err != nil {
		t.Fatalf("build server err = %v, want nil", err)
	}
	w := de.id
	if g, _ := waitLeader([]*Server{e, de}); g != w {
		t.Errorf("leader = %d, want %d", g, w)
	}

	destroyServer(e, h)
	destroyServer(de, dh)
	afterTest(t)
}

func buildServer(c *config.Config, id int64) (e *Server, h *httptest.Server, err error) {
	e, h = initTestServer(c, id, false)
	go func() { err = e.Run() }()
	for {
		if e.mode.Get() == participantMode {
			break
		}
		if err != nil {
			destroyServer(e, h)
			return nil, nil, err
		}
		time.Sleep(10 * time.Millisecond)
	}
	return e, h, nil
}

func destroyServer(e *Server, h *httptest.Server) {
	e.Stop()
	h.Close()
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"pixur.org/pixur/api/handlers"
	sdb "pixur.org/pixur/schema/db"
	"pixur.org/pixur/server/config"
)

type Server struct {
	db          sdb.DB
	s           *http.Server
	pixPath     string
	tokenSecret []byte
	publicKey   *rsa.PublicKey
	privateKey  *rsa.PrivateKey
	insecure    bool
}

func (s *Server) setup(c *config.Config) error {
	db, err := sdb.Open(c.DbName, c.DbConfig)
	if err != nil {
		return err
	}
	s.db = db

	// setup storage
	fi, err := os.Stat(c.PixPath)
	if os.IsNotExist(err) {
		if err := os.MkdirAll(c.PixPath, os.ModeDir|0775); err != nil {
			return err
		}
		//make it
	} else if err != nil {
		return err
	} else if !fi.IsDir() {
		return fmt.Errorf("%s is not a directory", c.PixPath)
	}
	s.pixPath = c.PixPath

	if c.SessionPrivateKeyPath != "" {
		f, err := os.Open(c.SessionPrivateKeyPath)
		if err != nil {
			return err
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		block, _ := pem.Decode(data)
		if block == nil {
			return fmt.Errorf("No key in %s", c.SessionPrivateKeyPath)
		}
		if block.Type != "RSA PRIVATE KEY" {
			return fmt.Errorf("Wrong private key type")
		}
		key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return err
		}
		key.Precompute()
		s.privateKey = key
	}

	if c.SessionPublicKeyPath != "" {
		f, err := os.Open(c.SessionPublicKeyPath)
		if err != nil {
			return err
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		block, _ := pem.Decode(data)
		if block == nil {
			return fmt.Errorf("No key in %s", c.SessionPublicKeyPath)
		}
		if block.Type != "PUBLIC KEY" {
			return fmt.Errorf("Wrong public key type")
		}
		key, err := x509.ParsePKIXPublicKey(block.Bytes)
		if err != nil {
			return err
		}
		if rsaKey, ok := key.(*rsa.PublicKey); ok {
			s.publicKey = rsaKey
		} else {
			return fmt.Errorf("Wrong public key type %T", key)
		}
	}
	if c.TokenSecret != "" {
		s.tokenSecret = []byte(c.TokenSecret)
	}
	s.insecure = c.Insecure

	s.s = new(http.Server)
	s.s.Addr = c.HttpSpec
	mux := http.NewServeMux()
	s.s.Handler = mux

	handlers.AddAllHandlers(mux, &handlers.ServerConfig{
		DB:          db,
		PixPath:     s.pixPath,
		TokenSecret: s.tokenSecret,
		PrivateKey:  s.privateKey,
		PublicKey:   s.publicKey,
		Secure:      !s.insecure,
	})
	return nil
}

func (s *Server) StartAndWait(c *config.Config) error {
	if err := s.setup(c); err != nil {
		return err
	}
	return s.s.ListenAndServe()
}
<commit_msg>server: handle cleartext http2<commit_after>package server

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/carl-mastrangelo/h2c"

	"pixur.org/pixur/api/handlers"
	sdb "pixur.org/pixur/schema/db"
	"pixur.org/pixur/server/config"
)

type Server struct {
	db          sdb.DB
	s           *http.Server
	pixPath     string
	tokenSecret []byte
	publicKey   *rsa.PublicKey
	privateKey  *rsa.PrivateKey
	insecure    bool
}

func (s *Server) setup(c *config.Config) error {
	db, err := sdb.Open(c.DbName, c.DbConfig)
	if err != nil {
		return err
	}
	s.db = db

	// setup storage
	fi, err := os.Stat(c.PixPath)
	if os.IsNotExist(err) {
		if err := os.MkdirAll(c.PixPath, os.ModeDir|0775); err != nil {
			return err
		}
		//make it
	} else if err != nil {
		return err
	} else if !fi.IsDir() {
		return fmt.Errorf("%s is not a directory", c.PixPath)
	}
	s.pixPath = c.PixPath

	if c.SessionPrivateKeyPath != "" {
		f, err := os.Open(c.SessionPrivateKeyPath)
		if err != nil {
			return err
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		block, _ := pem.Decode(data)
		if block == nil {
			return fmt.Errorf("No key in %s", c.SessionPrivateKeyPath)
		}
		if block.Type != "RSA PRIVATE KEY" {
			return fmt.Errorf("Wrong private key type")
		}
		key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return err
		}
		key.Precompute()
		s.privateKey = key
	}

	if c.SessionPublicKeyPath != "" {
		f, err := os.Open(c.SessionPublicKeyPath)
		if err != nil {
			return err
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		block, _ := pem.Decode(data)
		if block == nil {
			return fmt.Errorf("No key in %s", c.SessionPublicKeyPath)
		}
		if block.Type != "PUBLIC KEY" {
			return fmt.Errorf("Wrong public key type")
		}
		key, err := x509.ParsePKIXPublicKey(block.Bytes)
		if err != nil {
			return err
		}
		if rsaKey, ok := key.(*rsa.PublicKey); ok {
			s.publicKey = rsaKey
		} else {
			return fmt.Errorf("Wrong public key type %T", key)
		}
	}
	if c.TokenSecret != "" {
		s.tokenSecret = []byte(c.TokenSecret)
	}
	s.insecure = c.Insecure

	s.s = new(http.Server)
	s.s.Addr = c.HttpSpec
	mux := http.NewServeMux()
	s.s.Handler = mux
	h2c.AttachClearTextHandler(nil /* default http2 server */, s.s)

	handlers.AddAllHandlers(mux, &handlers.ServerConfig{
		DB:          db,
		PixPath:     s.pixPath,
		TokenSecret: s.tokenSecret,
		PrivateKey:  s.privateKey,
		PublicKey:   s.publicKey,
		Secure:      !s.insecure,
	})
	return nil
}

func (s *Server) StartAndWait(c *config.Config) error {
	if err := s.setup(c); err != nil {
		return err
	}
	return s.s.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"html/template"
	"jiacrontab/libs"
	"jiacrontab/libs/proto"
	"jiacrontab/server/store"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

func listTask(rw http.ResponseWriter, r *http.Request, m *modelView) {

	var addr string
	var systemInfo map[string]interface{}
	var locals proto.Mdata
	var clientList map[string]proto.ClientConf

	sortedKeys := make([]string, 0)
	sortedKeys2 := make([]string, 0)
	clientList, _ = m.s.GetRPCClientList()

	if clientList != nil && len(clientList) > 0 {
		for k := range clientList {
			sortedKeys = append(sortedKeys, k)
		}
		sort.Strings(sortedKeys)
		firstK := sortedKeys[0]
		addr = replaceEmpty(r.FormValue("addr"), firstK)
	} else {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "nothing to show",
		}, nil)
		return
	}

	locals = make(proto.Mdata)

	if err := m.rpcCall(addr, "Task.All", "", &locals); err != nil {
		http.Redirect(rw, r, "/", http.StatusFound)
		return
	}

	for k := range locals {
		sortedKeys2 = append(sortedKeys2, k)
	}
	sort.Strings(sortedKeys2)

	m.rpcCall(addr, "Task.SystemInfo", "", &systemInfo)
	m.renderHtml2([]string{"listTask"}, map[string]interface{}{
		"title":         "灵魂百度",
		"list":          locals,
		"addrs":         sortedKeys,
		"listKey":       sortedKeys2,
		"rpcClientsMap": clientList,
		"client":        clientList[addr],
		"addr":          addr,
		"systemInfo":    systemInfo,
		"appName":       globalConfig.appName,
	}, template.FuncMap{
		"date":     date,
		"formatMs": int2floatstr,
	})

}

func index(rw http.ResponseWriter, r *http.Request, m *modelView) {
	var clientList map[string]proto.ClientConf
	if r.URL.Path != "/" {
		rw.WriteHeader(http.StatusNotFound)
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "404 page not found",
		}, nil)
		return
	}

	sInfo := libs.SystemInfo(startTime)
	sortedKeys := make([]string, 0)
	clientList, _ = m.s.GetRPCClientList()

	for k := range clientList {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)
	m.renderHtml2([]string{"index"}, map[string]interface{}{
		"rpcClientsKey": sortedKeys,
		"rpcClientsMap": clientList,
		"systemInfo":    sInfo,
	}, template.FuncMap{
		"date": date,
	})

}

func updateTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	var reply bool

	sortedKeys := make([]string, 0)
	addr := strings.TrimSpace(r.FormValue("addr"))
	id := strings.TrimSpace(r.FormValue("taskId"))
	if addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "params error",
		}, nil)
		return
	}

	if r.Method == http.MethodPost {

		n := strings.TrimSpace(r.FormValue("taskName"))
		command := strings.TrimSpace(r.FormValue("command"))
		timeoutStr := strings.TrimSpace(r.FormValue("timeout"))
		timeout, err := strconv.Atoi(timeoutStr)
		optimeout := strings.TrimSpace(r.FormValue("optimeout"))
		mailTo := strings.TrimSpace(r.FormValue("mailTo"))
		if _, ok := map[string]bool{"email": true, "kill": true, "email_and_kill": true, "ignore": true}[optimeout]; !ok {
			optimeout = "ignore"
		}

		if err != nil {
			timeout = 0
		}

		a := r.FormValue("args")
		month := replaceEmpty(strings.TrimSpace(r.FormValue("month")), "*")
		weekday := replaceEmpty(strings.TrimSpace(r.FormValue("weekday")), "*")
		day := replaceEmpty(strings.TrimSpace(r.FormValue("day")), "*")
		hour := replaceEmpty(strings.TrimSpace(r.FormValue("hour")), "*")
		minute := replaceEmpty(strings.TrimSpace(r.FormValue("minute")), "*")

		if err := m.rpcCall(addr, "Task.Update", proto.TaskArgs{
			Id:        id,
			Name:      n,
			Command:   command,
			Args:      a,
			Timeout:   int64(timeout),
			OpTimeout: optimeout,
			Create:    time.Now().Unix(),
			MailTo:    mailTo,
			C: struct {
				Weekday string
				Month   string
				Day     string
				Hour    string
				Minute  string
			}{

				Month:   month,
				Day:     day,
				Hour:    hour,
				Minute:  minute,
				Weekday: weekday,
			},
		}, &reply); err != nil {
			m.renderHtml2([]string{"public/error"}, map[string]interface{}{
				"error": err.Error(),
			}, nil)
			return
		}
		if reply {
			http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
			return
		}

	} else {
		var t proto.TaskArgs
		var clientList map[string]proto.ClientConf
		if id != "" {
			m.rpcCall(addr, "Task.Get", id, &t)
			if reply {
				http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
				return
			}
		}

		clientList, _ = m.s.GetRPCClientList()
		if len(clientList) > 0 {
			for k := range clientList {
				sortedKeys = append(sortedKeys, k)
			}
			sort.Strings(sortedKeys)
			firstK := sortedKeys[0]
			addr = replaceEmpty(r.FormValue("addr"), firstK)
		} else {
			m.renderHtml2([]string{"public/error"}, map[string]interface{}{
				"error": "nothing to show",
			}, nil)
			return
		}

		m.renderHtml2([]string{"updateTask"}, map[string]interface{}{
			"addr":          addr,
			"addrs":         sortedKeys,
			"rpcClientsMap": clientList,
			"task":          t,
			"allowCommands": globalConfig.allowCommands,
		}, nil)
	}

}

func stopTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	action := replaceEmpty(r.FormValue("action"), "stop")
	var reply bool
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	// if c, err := newRpcClient(addr); err != nil {
	// 	m.renderHtml2([]string{"public/error"}, map[string]interface{}{
	// 		"error": "failed stop task" + taskId,
	// 	}, nil)
	// 	return
	// } else {
	var method string
	if action == "stop" {
		method = "Task.Stop"
	} else if action == "delete" {
		method = "Task.Delete"
	} else {
		method = "Task.Kill"
	}
	if err := m.rpcCall(addr, method, taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	if reply {
		http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
		return
	}

	m.renderHtml2([]string{"public/error"}, map[string]interface{}{
		"error": fmt.Sprintf("failed %s %s", method, taskId),
	}, nil)

}

func startTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	var reply bool
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if err := m.rpcCall(addr, "Task.Start", taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if reply {
		http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
		return
	}

	m.renderHtml2([]string{"error"}, map[string]interface{}{
		"error": "failed start task" + taskId,
	}, nil)

}

func login(rw http.ResponseWriter, r *http.Request, m *modelView) {
	if r.Method == http.MethodPost {

		u := r.FormValue("username")
		pwd := r.FormValue("passwd")
		remb := r.FormValue("remember")

		if u == globalConfig.user && pwd == globalConfig.passwd {
			md5p := fmt.Sprintf("%x", md5.Sum([]byte(pwd)))
			if remb == "yes" {
				globalJwt.accessToken(rw, r, u, md5p)
			} else {
				globalJwt.accessTempToken(rw, r, u, md5p)
			}

			http.Redirect(rw, r, "/", http.StatusFound)
			return
		}

		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "auth failed",
		}, nil)

	} else {
		var user map[string]interface{}
		if globalJwt.auth(rw, r, &user) {
			http.Redirect(rw, r, "/", http.StatusFound)
			return
		}
		m.renderHtml2([]string{"login"}, nil, nil)

	}
}

func quickStart(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	var reply []byte
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if err := m.rpcCall(addr, "Task.QuickStart", taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	logList := strings.Split(string(reply), "\n")
	m.renderHtml2([]string{"log"}, map[string]interface{}{
		"logList": logList,
		"addr":    addr,
	}, nil)

}

func logout(rw http.ResponseWriter, r *http.Request, m *modelView) {
	globalJwt.cleanCookie(rw)
	http.Redirect(rw, r, "/login", http.StatusFound)
}

func recentLog(rw http.ResponseWriter, r *http.Request, m *modelView) {
	id := r.FormValue("taskId")
	addr := r.FormValue("addr")
	var content []byte
	if id == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}
	if err := m.rpcCall(addr, "Task.Log", id, &content); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	logList := strings.Split(string(content), "\n")

	m.renderHtml2([]string{"log"}, map[string]interface{}{
		"logList": logList,
		"addr":    addr,
	}, nil)
	return

}

func readme(rw http.ResponseWriter, r *http.Request, m *modelView) {

	m.renderHtml2([]string{"readme"}, map[string]interface{}{}, nil)
	return

}

func reloadConfig(rw http.ResponseWriter, r *http.Request, m *modelView) {
	globalConfig.reload()
	http.Redirect(rw, r, "/", http.StatusFound)
	log.Println("reload config")
}

func deleteClient(rw http.ResponseWriter, r *http.Request, m *modelView) {

	addr := r.FormValue("addr")
	m.s.Wrap(func(s *store.Store) {

		if v, ok := s.RpcClientList[addr]; ok {
			if v.State == 1 {
				return
			}
		}
		delete(s.RpcClientList, addr)

	}).Sync()
	http.Redirect(rw, r, "/", http.StatusFound)
}

func viewConfig(rw http.ResponseWriter, r *http.Request, m *modelView) {

	c := globalConfig.category()

	if r.Method == http.MethodPost {
		mailTo := strings.TrimSpace(r.FormValue("mailTo"))
		libs.SendMail("测试邮件", "测试邮件请勿回复", globalConfig.mailHost, globalConfig.mailUser, globalConfig.mailPass, globalConfig.mailPort, mailTo)
	}

	m.renderHtml2([]string{"viewConfig"}, map[string]interface{}{
		"configs": c,
	}, nil)
	return
}
<commit_msg>master<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"html/template"
	"jiacrontab/libs"
	"jiacrontab/libs/proto"
	"jiacrontab/server/store"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

func listTask(rw http.ResponseWriter, r *http.Request, m *modelView) {

	var addr string
	var systemInfo map[string]interface{}
	var locals proto.Mdata
	var clientList map[string]proto.ClientConf

	sortedKeys := make([]string, 0)
	sortedKeys2 := make([]string, 0)
	clientList, _ = m.s.GetRPCClientList()

	if clientList != nil && len(clientList) > 0 {
		for k := range clientList {
			sortedKeys = append(sortedKeys, k)
		}
		sort.Strings(sortedKeys)
		firstK := sortedKeys[0]
		addr = replaceEmpty(r.FormValue("addr"), firstK)
	} else {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "nothing to show",
		}, nil)
		return
	}

	locals = make(proto.Mdata)

	if err := m.rpcCall(addr, "Task.All", "", &locals); err != nil {
		http.Redirect(rw, r, "/", http.StatusFound)
		return
	}

	for k := range locals {
		sortedKeys2 = append(sortedKeys2, k)
	}
	sort.Strings(sortedKeys2)

	m.rpcCall(addr, "Task.SystemInfo", "", &systemInfo)
	m.renderHtml2([]string{"listTask"}, map[string]interface{}{
		"title":         "灵魂百度",
		"list":          locals,
		"addrs":         sortedKeys,
		"listKey":       sortedKeys2,
		"rpcClientsMap": clientList,
		"client":        clientList[addr],
		"addr":          addr,
		"systemInfo":    systemInfo,
		"appName":       globalConfig.appName,
	}, template.FuncMap{
		"date":     date,
		"formatMs": int2floatstr,
	})

}

func index(rw http.ResponseWriter, r *http.Request, m *modelView) {
	var clientList map[string]proto.ClientConf
	if r.URL.Path != "/" {
		rw.WriteHeader(http.StatusNotFound)
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "404 page not found",
		}, nil)
		return
	}

	sInfo := libs.SystemInfo(startTime)
	sortedKeys := make([]string, 0)
	clientList, _ = m.s.GetRPCClientList()

	for k := range clientList {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)
	m.renderHtml2([]string{"index"}, map[string]interface{}{
		"rpcClientsKey": sortedKeys,
		"rpcClientsMap": clientList,
		"systemInfo":    sInfo,
	}, template.FuncMap{
		"date": date,
	})

}

func updateTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	var reply bool

	sortedKeys := make([]string, 0)
	addr := strings.TrimSpace(r.FormValue("addr"))
	id := strings.TrimSpace(r.FormValue("taskId"))
	if addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "params error",
		}, nil)
		return
	}

	if r.Method == http.MethodPost {

		n := strings.TrimSpace(r.FormValue("taskName"))
		command := strings.TrimSpace(r.FormValue("command"))
		timeoutStr := strings.TrimSpace(r.FormValue("timeout"))
		timeout, err := strconv.Atoi(timeoutStr)
		optimeout := strings.TrimSpace(r.FormValue("optimeout"))
		mailTo := strings.TrimSpace(r.FormValue("mailTo"))
		if _, ok := map[string]bool{"email": true, "kill": true, "email_and_kill": true, "ignore": true}[optimeout]; !ok {
			optimeout = "ignore"
		}

		if err != nil {
			timeout = 0
		}

		a := r.FormValue("args")
		month := replaceEmpty(strings.TrimSpace(r.FormValue("month")), "*")
		weekday := replaceEmpty(strings.TrimSpace(r.FormValue("weekday")), "*")
		day := replaceEmpty(strings.TrimSpace(r.FormValue("day")), "*")
		hour := replaceEmpty(strings.TrimSpace(r.FormValue("hour")), "*")
		minute := replaceEmpty(strings.TrimSpace(r.FormValue("minute")), "*")

		if err := m.rpcCall(addr, "Task.Update", proto.TaskArgs{
			Id:        id,
			Name:      n,
			Command:   command,
			Args:      a,
			Timeout:   int64(timeout),
			OpTimeout: optimeout,
			Create:    time.Now().Unix(),
			MailTo:    mailTo,
			C: struct {
				Weekday string
				Month   string
				Day     string
				Hour    string
				Minute  string
			}{

				Month:   month,
				Day:     day,
				Hour:    hour,
				Minute:  minute,
				Weekday: weekday,
			},
		}, &reply); err != nil {
			m.renderHtml2([]string{"public/error"}, map[string]interface{}{
				"error": err.Error(),
			}, nil)
			return
		}
		if reply {
			http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
			return
		}

	} else {
		var t proto.TaskArgs
		var clientList map[string]proto.ClientConf
		if id != "" {
			m.rpcCall(addr, "Task.Get", id, &t)
			if reply {
				http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
				return
			}
		} else {
			client, _ := m.s.SearchRPCClientList(addr)
			log.Println(client)
			t.MailTo = client.Mail
		}

		clientList, _ = m.s.GetRPCClientList()

		if len(clientList) > 0 {
			for k := range clientList {
				sortedKeys = append(sortedKeys, k)
			}
			sort.Strings(sortedKeys)
			firstK := sortedKeys[0]
			addr = replaceEmpty(r.FormValue("addr"), firstK)
		} else {
			m.renderHtml2([]string{"public/error"}, map[string]interface{}{
				"error": "nothing to show",
			}, nil)
			return
		}

		m.renderHtml2([]string{"updateTask"}, map[string]interface{}{
			"addr":          addr,
			"addrs":         sortedKeys,
			"rpcClientsMap": clientList,
			"task":          t,
			"allowCommands": globalConfig.allowCommands,
		}, nil)
	}

}

func stopTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	action := replaceEmpty(r.FormValue("action"), "stop")
	var reply bool
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	// if c, err := newRpcClient(addr); err != nil {
	// 	m.renderHtml2([]string{"public/error"}, map[string]interface{}{
	// 		"error": "failed stop task" + taskId,
	// 	}, nil)
	// 	return
	// } else {
	var method string
	if action == "stop" {
		method = "Task.Stop"
	} else if action == "delete" {
		method = "Task.Delete"
	} else {
		method = "Task.Kill"
	}
	if err := m.rpcCall(addr, method, taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	if reply {
		http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
		return
	}

	m.renderHtml2([]string{"public/error"}, map[string]interface{}{
		"error": fmt.Sprintf("failed %s %s", method, taskId),
	}, nil)

}

func startTask(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	var reply bool
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if err := m.rpcCall(addr, "Task.Start", taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if reply {
		http.Redirect(rw, r, "/list?addr="+addr, http.StatusFound)
		return
	}

	m.renderHtml2([]string{"error"}, map[string]interface{}{
		"error": "failed start task" + taskId,
	}, nil)

}

func login(rw http.ResponseWriter, r *http.Request, m *modelView) {
	if r.Method == http.MethodPost {

		u := r.FormValue("username")
		pwd := r.FormValue("passwd")
		remb := r.FormValue("remember")

		if u == globalConfig.user && pwd == globalConfig.passwd {
			md5p := fmt.Sprintf("%x", md5.Sum([]byte(pwd)))
			if remb == "yes" {
				globalJwt.accessToken(rw, r, u, md5p)
			} else {
				globalJwt.accessTempToken(rw, r, u, md5p)
			}

			http.Redirect(rw, r, "/", http.StatusFound)
			return
		}

		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "auth failed",
		}, nil)

	} else {
		var user map[string]interface{}
		if globalJwt.auth(rw, r, &user) {
			http.Redirect(rw, r, "/", http.StatusFound)
			return
		}
		m.renderHtml2([]string{"login"}, nil, nil)

	}
}

func quickStart(rw http.ResponseWriter, r *http.Request, m *modelView) {
	taskId := strings.TrimSpace(r.FormValue("taskId"))
	addr := strings.TrimSpace(r.FormValue("addr"))
	var reply []byte
	if taskId == "" || addr == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}

	if err := m.rpcCall(addr, "Task.QuickStart", taskId, &reply); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	logList := strings.Split(string(reply), "\n")
	m.renderHtml2([]string{"log"}, map[string]interface{}{
		"logList": logList,
		"addr":    addr,
	}, nil)

}

func logout(rw http.ResponseWriter, r *http.Request, m *modelView) {
	globalJwt.cleanCookie(rw)
	http.Redirect(rw, r, "/login", http.StatusFound)
}

func recentLog(rw http.ResponseWriter, r *http.Request, m *modelView) {
	id := r.FormValue("taskId")
	addr := r.FormValue("addr")
	var content []byte
	if id == "" {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": "param error",
		}, nil)
		return
	}
	if err := m.rpcCall(addr, "Task.Log", id, &content); err != nil {
		m.renderHtml2([]string{"public/error"}, map[string]interface{}{
			"error": err,
		}, nil)
		return
	}
	logList := strings.Split(string(content), "\n")

	m.renderHtml2([]string{"log"}, map[string]interface{}{
		"logList": logList,
		"addr":    addr,
	}, nil)
	return

}

func readme(rw http.ResponseWriter, r *http.Request, m *modelView) {

	m.renderHtml2([]string{"readme"}, map[string]interface{}{}, nil)
	return

}

func reloadConfig(rw http.ResponseWriter, r *http.Request, m *modelView) {
	globalConfig.reload()
	http.Redirect(rw, r, "/", http.StatusFound)
	log.Println("reload config")
}

func deleteClient(rw http.ResponseWriter, r *http.Request, m *modelView) {

	addr := r.FormValue("addr")
	m.s.Wrap(func(s *store.Store) {

		if v, ok := s.RpcClientList[addr]; ok {
			if v.State == 1 {
				return
			}
		}
		delete(s.RpcClientList, addr)

	}).Sync()
	http.Redirect(rw, r, "/", http.StatusFound)
}

func viewConfig(rw http.ResponseWriter, r *http.Request, m *modelView) {

	c := globalConfig.category()

	if r.Method == http.MethodPost {
		mailTo := strings.TrimSpace(r.FormValue("mailTo"))
		libs.SendMail("测试邮件", "测试邮件请勿回复", globalConfig.mailHost, globalConfig.mailUser, globalConfig.mailPass, globalConfig.mailPort, mailTo)
	}

	m.renderHtml2([]string{"viewConfig"}, map[string]interface{}{
		"configs": c,
	}, nil)
	return
}
<|endoftext|>


<commit_before>package server

import (
	"strings"
)

// usage defines the message shown when a help flag is passed to etcd.
var usage = `
etcd

Usage:
  etcd -name <name>
  etcd -name <name> [-data-dir=<path>]
  etcd -h | -help
  etcd -version

Options:
  -h -help          Show this screen.
  --version         Show version.
  -f -force         Force a new configuration to be used.
  -config=<path>    Path to configuration file.
  -name=<name>      Name of this node in the etcd cluster.
  -data-dir=<path>  Path to the data directory.
  -cors=<origins>   Comma-separated list of CORS origins.
  -v                Enabled verbose logging.
  -vv               Enabled very verbose logging.

Cluster Configuration Options:
  -peers=<peers>      Comma-separated list of peers (ip + port) in the cluster.
  -peers-file=<path>  Path to a file containing the peer list.

Client Communication Options:
  -addr=<host:port>   The public host:port used for client communication.
  -bind-addr=<host>   The listening hostname used for client communication.
  -ca-file=<path>     Path to the client CA file.
  -cert-file=<path>   Path to the client cert file.
  -key-file=<path>    Path to the client key file.

Peer Communication Options:
  -peer-addr=<host:port>  The public host:port used for peer communication.
  -peer-bind-addr=<host>  The listening hostname used for peer communication.
  -peer-ca-file=<path>    Path to the peer CA file.
  -peer-cert-file=<path>  Path to the peer cert file.
  -peer-key-file=<path>   Path to the peer key file.

Other Options:
  -max-result-buffer   Max size of the result buffer.
  -max-retry-attempts  Number of times a node will try to join a cluster.
  -max-cluster-size    Maximum number of nodes in the cluster.
  -snapshot            Open or close the snapshot.
  -snapshot-count      Number of transactions before issuing a snapshot.
`

// Usage returns the usage message for etcd.
func Usage() string {
	return strings.TrimSpace(usage)
}
<commit_msg>fix(server/usage): fixup the usage based on feedback<commit_after>package server

import (
	"strings"
)

// usage defines the message shown when a help flag is passed to etcd.
var usage = `
etcd

Usage:
  etcd -name <name>
  etcd -name <name> [-data-dir=<path>]
  etcd -h | -help
  etcd -version

Options:
  -h -help          Show this screen.
  --version         Show version.
  -f -force         Force a new configuration to be used.
  -config=<path>    Path to configuration file.
  -name=<name>      Name of this node in the etcd cluster.
  -data-dir=<path>  Path to the data directory.
  -cors=<origins>   Comma-separated list of CORS origins.
  -v                Enabled verbose logging.
  -vv               Enabled very verbose logging.

Cluster Configuration Options:
  -peers-file=<path>              Path to a file containing the peer list.
  -peers=<host:port>,<host:port>  Comma-separated list of peers. The members
                                  should match the peer's '-peer-addr' flag.

Client Communication Options:
  -addr=<host:port>   The public host:port used for client communication.
  -bind-addr=<host>   The listening hostname used for client communication.
  -ca-file=<path>     Path to the client CA file.
  -cert-file=<path>   Path to the client cert file.
  -key-file=<path>    Path to the client key file.

Peer Communication Options:
  -peer-addr=<host:port>  The public host:port used for peer communication.
  -peer-bind-addr=<host>  The listening hostname used for peer communication.
  -peer-ca-file=<path>    Path to the peer CA file.
  -peer-cert-file=<path>  Path to the peer cert file.
  -peer-key-file=<path>   Path to the peer key file.

Other Options:
  -max-result-buffer   Max size of the result buffer.
  -max-retry-attempts  Number of times a node will try to join a cluster.
  -max-cluster-size    Maximum number of nodes in the cluster.
  -snapshot            Open or close the snapshot.
  -snapshot-count      Number of transactions before issuing a snapshot.
`

// Usage returns the usage message for etcd.
func Usage() string {
	return strings.TrimSpace(usage)
}
<|endoftext|>