<commit_before>package git

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	"github.com/boyvanduuren/octorunner/lib/pipeline"
	"github.com/docker/docker/client"
	"github.com/google/go-github/github"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
	TMPDIR_PREFIX   = "octorunner-"
	TMPFILE_PREFIX  = "archive-"
	PIPELINEFILE    = ".octorunner"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(context.Context, hookPayload){
		"push": handlePush,
	}

	// Create a context for this request
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(context.Context, hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for \"" + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Errorf("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// The repository that this payload is for might have a secret configured, in which case we expect
	// a signature with the payload. The given signature then needs to match a signature we calculate ourselves.
	// Only then will we call our handler, else we'll log an error and return
	repoSecret := Auth.RequestSecret(payload.Repository.FullName)
	if len(repoSecret) == 0 {
		log.Error("No secret was configured, cannot verify their signature")
	} else {
		signature := r.Header.Get(SIGNATUREHEADER)
		if signature == "" {
			log.Error("Expected signature for payload, but none given")
			return
		}
		log.Debug("Received signature " + signature)
		calculatedSignature := "sha1=" + authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if !authentication.CompareSignatures([]byte(signature), []byte(calculatedSignature)) {
			log.Error("Signatures didn't match")
			return
		}
	}
	eventHandler(ctx, payload)
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(ctx context.Context, payload hookPayload) {
	log.Info("Handling received push event")

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After

	/*
	 When a commit is merged from a branch to another branch, the "after" ID is set to
	 "0000000000000000000000000000000000000000", and the "previous" ID is the ID of the commit being merged.
	 That commit will probably already have a state assigned, so we can just return
	*/
	if commitId == "0000000000000000000000000000000000000000" {
		return
	}

	httpClient := oauth2.NewClient(ctx, oauth2.StaticTokenSource(repoToken))
	gitClient := github.NewClient(httpClient)

	repoDir, err := getRepository(httpClient, gitClient, repoName, repoOwner, commitId, repoToken)
	if err != nil {
		log.Errorf("Error while downloading copy of repository: %v", err)
		return
	}

	ctx = context.WithValue(ctx, "repoData", map[string]string{
		"fullName":   repoFullName,
		"commitId":   commitId,
		"fsLocation": repoDir,
	})

	repoPipeline, err := readPipelineConfig(repoDir)
	if err != nil {
		log.Errorf("Error while reading pipeline configuration: %v", err)
		return
	}

	// set state of commit to pending
	log.Debug("Setting state to pending")
	gitSetState(gitClient, "pending", repoOwner, repoName, commitId)

	// create Docker client
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Errorf("Error while creating connection to Docker: %q", err)
		gitSetState(gitClient, "error", repoOwner, repoName, commitId)
		return
	}
	defer cli.Close()

	exitcode, err := repoPipeline.Execute(ctx, cli)
	if err != nil {
		log.Errorf("Error while executing pipeline: %v", err)
		gitSetState(gitClient, "error", repoOwner, repoName, commitId)
		return
	}

	log.Debugf("Pipeline returned %d, setting state accordingly", exitcode)
	if exitcode == 0 {
		gitSetState(gitClient, "success", repoOwner, repoName, commitId)
	} else {
		gitSetState(gitClient, "failure", repoOwner, repoName, commitId)
	}
}

func getRepository(httpClient *http.Client, gitClient *github.Client, repoName string, repoOwner string,
	commitId string, repoToken *oauth2.Token) (string, error) {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_ROOTDIR = "%s-%s-%s"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			return "", errors.New(fmt.Sprintf("Error while constructing archive URL: %v", err))
		}
	} else {
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			return "", errors.New(fmt.Sprintf("Error while getting archive URL: %v", err))
		}
	}
	log.Debug("Found archive URL ", archiveUrl)

	tmpDir, err := ioutil.TempDir("", TMPDIR_PREFIX)
	if err != nil {
		log.Error()
		return "", errors.New(fmt.Sprintf("Error while creating temporary directory: %v", err))
	}

	log.Debug("Created temporary directory " + tmpDir)
	archivePath, err := downloadFile(httpClient, archiveUrl, tmpDir)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error while downloading archive: %v", err))
	}
	log.Debug("Archive downloaded to ", archivePath.Name())

	err = unzip(archivePath.Name(), tmpDir)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error while unpacking archive: %v", err))
	}

	// cleanup the archive
	archivePath.Close()
	os.Remove(archivePath.Name())

	// we should now have a copy of the repository at the latest commit
	repoDir := path.Join(tmpDir, fmt.Sprintf(GITHUB_ARCHIVE_ROOTDIR, repoOwner, repoName, commitId))
	if checkDirNotExists(repoDir) {
		repoDir = path.Join(tmpDir, fmt.Sprintf(GITHUB_ARCHIVE_ROOTDIR, repoOwner, repoName, commitId[0:7]))
		if checkDirNotExists(repoDir) {
			log.Error()
			return "", errors.New(fmt.Sprintf("Repository not found at expected directory \"%s\" after unpacking", repoDir))
		}
	}
	log.Debug("Repository unpacked to ", repoDir)

	return repoDir, nil
}

func downloadFile(httpClient *http.Client, url *url.URL, downloadDirectory string) (*os.File, error) {
	log.Debug("Downloading \"" + url.String() + "\"")
	filePath, err := ioutil.TempFile(downloadDirectory, TMPFILE_PREFIX)
	if err != nil {
		return nil, err
	}

	resp, err := httpClient.Get(url.String())
	defer resp.Body.Close()
	n, err := io.Copy(filePath, resp.Body)
	if err != nil {
		return nil, err
	} else {
		log.Debugf("Downloaded %d bytes", n)
		return filePath, nil
	}
}

func readPipelineConfig(directory string) (pipeline.Pipeline, error) {
	var pipelineConfig pipeline.Pipeline
	pipelineConfigPath := path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yaml"}, ""))
	if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
		pipelineConfigPath = path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yml"}, ""))
		if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
			return pipelineConfig, errors.New("Couldn't find .octorunner.yaml or .octorunner.yml in repository")
		}
	}
	pipelineConfigBuf, err := ioutil.ReadFile(pipelineConfigPath)
	if err != nil {
		return pipelineConfig, errors.New(fmt.Sprintf("Error while reading from %s: %v", pipelineConfigPath, err))
	}

	pipelineConfig, err = pipeline.ParseConfig(pipelineConfigBuf)
	return pipelineConfig, err
}

func checkDirNotExists(dir string) bool {
	s, err := os.Stat(dir)
	return os.IsNotExist(err) == true || !s.IsDir()
}

func gitSetState(git *github.Client, state string, owner string, repo string, commit string) {
	git.Repositories.CreateStatus(owner, repo, commit, &github.RepoStatus{State: &state})
}

// Extract a zip file to a destination.
// By "swtdrgn" from http://stackoverflow.com/a/24430720
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		fpath := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
		} else {
			var fdir string
			if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
				fdir = fpath[:lastIndex]
			}

			err = os.MkdirAll(fdir, f.Mode())
			if err != nil {
				log.Error(err)
				return err
			}
			f, err := os.OpenFile(
				fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer f.Close()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Fix timeouts on (github) client<commit_after>package git

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	"github.com/boyvanduuren/octorunner/lib/pipeline"
	"github.com/docker/docker/client"
	"github.com/google/go-github/github"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
	TMPDIR_PREFIX   = "octorunner-"
	TMPFILE_PREFIX  = "archive-"
	PIPELINEFILE    = ".octorunner"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(hookPayload){
		"push": handlePush,
	}

	// Return 200 to the client
	w.WriteHeader(200)

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for \"" + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Errorf("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// The repository that this payload is for might have a secret configured, in which case we expect
	// a signature with the payload. The given signature then needs to match a signature we calculate ourselves.
	// Only then will we call our handler, else we'll log an error and return
	repoSecret := Auth.RequestSecret(payload.Repository.FullName)
	if len(repoSecret) == 0 {
		log.Error("No secret was configured, cannot verify their signature")
	} else {
		signature := r.Header.Get(SIGNATUREHEADER)
		if signature == "" {
			log.Error("Expected signature for payload, but none given")
			return
		}
		log.Debug("Received signature " + signature)
		calculatedSignature := "sha1=" + authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if !authentication.CompareSignatures([]byte(signature), []byte(calculatedSignature)) {
			log.Error("Signatures didn't match")
			return
		}
	}
	go eventHandler(payload)
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(payload hookPayload) {
	log.Info("Handling received push event")

	// Create a context for this request
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After

	/*
	 When a commit is merged from a branch to another branch, the "after" ID is set to
	 "0000000000000000000000000000000000000000", and the "previous" ID is the ID of the commit being merged.
	 That commit will probably already have a state assigned, so we can just return
	*/
	if commitId == "0000000000000000000000000000000000000000" {
		return
	}

	httpClient := oauth2.NewClient(ctx, oauth2.StaticTokenSource(repoToken))
	gitClient := github.NewClient(httpClient)

	repoDir, err := getRepository(httpClient, gitClient, repoName, repoOwner, commitId, repoToken)
	if err != nil {
		log.Errorf("Error while downloading copy of repository: %v", err)
		return
	}

	ctx = context.WithValue(ctx, "repoData", map[string]string{
		"fullName":   repoFullName,
		"commitId":   commitId,
		"fsLocation": repoDir,
	})

	repoPipeline, err := readPipelineConfig(repoDir)
	if err != nil {
		log.Errorf("Error while reading pipeline configuration: %v", err)
		return
	}

	// set state of commit to pending
	log.Debug("Setting state to pending")
	gitSetState(gitClient, "pending", repoOwner, repoName, commitId)

	// create Docker client
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Errorf("Error while creating connection to Docker: %q", err)
		gitSetState(gitClient, "error", repoOwner, repoName, commitId)
		return
	}
	defer cli.Close()

	exitcode, err := repoPipeline.Execute(ctx, cli)
	if err != nil {
		log.Errorf("Error while executing pipeline: %v", err)
		gitSetState(gitClient, "error", repoOwner, repoName, commitId)
		return
	}

	log.Debugf("Pipeline returned %d, setting state accordingly", exitcode)
	if exitcode == 0 {
		gitSetState(gitClient, "success", repoOwner, repoName, commitId)
	} else {
		gitSetState(gitClient, "failure", repoOwner, repoName, commitId)
	}
}

func getRepository(httpClient *http.Client, gitClient *github.Client, repoName string, repoOwner string,
	commitId string, repoToken *oauth2.Token) (string, error) {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_ROOTDIR = "%s-%s-%s"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			return "", errors.New(fmt.Sprintf("Error while constructing archive URL: %v", err))
		}
	} else {
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			return "", errors.New(fmt.Sprintf("Error while getting archive URL: %v", err))
		}
	}
	log.Debug("Found archive URL ", archiveUrl)

	tmpDir, err := ioutil.TempDir("", TMPDIR_PREFIX)
	if err != nil {
		log.Error()
		return "", errors.New(fmt.Sprintf("Error while creating temporary directory: %v", err))
	}

	log.Debug("Created temporary directory " + tmpDir)
	archivePath, err := downloadFile(httpClient, archiveUrl, tmpDir)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error while downloading archive: %v", err))
	}
	log.Debug("Archive downloaded to ", archivePath.Name())

	err = unzip(archivePath.Name(), tmpDir)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error while unpacking archive: %v", err))
	}

	// cleanup the archive
	archivePath.Close()
	os.Remove(archivePath.Name())

	// we should now have a copy of the repository at the latest commit
	repoDir := path.Join(tmpDir, fmt.Sprintf(GITHUB_ARCHIVE_ROOTDIR, repoOwner, repoName, commitId))
	if checkDirNotExists(repoDir) {
		repoDir = path.Join(tmpDir, fmt.Sprintf(GITHUB_ARCHIVE_ROOTDIR, repoOwner, repoName, commitId[0:7]))
		if checkDirNotExists(repoDir) {
			log.Error()
			return "", errors.New(fmt.Sprintf("Repository not found at expected directory \"%s\" after unpacking", repoDir))
		}
	}
	log.Debug("Repository unpacked to ", repoDir)

	return repoDir, nil
}

func downloadFile(httpClient *http.Client, url *url.URL, downloadDirectory string) (*os.File, error) {
	log.Debug("Downloading \"" + url.String() + "\"")
	filePath, err := ioutil.TempFile(downloadDirectory, TMPFILE_PREFIX)
	if err != nil {
		return nil, err
	}

	resp, err := httpClient.Get(url.String())
	defer resp.Body.Close()
	n, err := io.Copy(filePath, resp.Body)
	if err != nil {
		return nil, err
	} else {
		log.Debugf("Downloaded %d bytes", n)
		return filePath, nil
	}
}

func readPipelineConfig(directory string) (pipeline.Pipeline, error) {
	var pipelineConfig pipeline.Pipeline
	pipelineConfigPath := path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yaml"}, ""))
	if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
		pipelineConfigPath = path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yml"}, ""))
		if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
			return pipelineConfig, errors.New("Couldn't find .octorunner.yaml or .octorunner.yml in repository")
		}
	}
	pipelineConfigBuf, err := ioutil.ReadFile(pipelineConfigPath)
	if err != nil {
		return pipelineConfig, errors.New(fmt.Sprintf("Error while reading from %s: %v", pipelineConfigPath, err))
	}

	pipelineConfig, err = pipeline.ParseConfig(pipelineConfigBuf)
	return pipelineConfig, err
}

func checkDirNotExists(dir string) bool {
	s, err := os.Stat(dir)
	return os.IsNotExist(err) == true || !s.IsDir()
}

func gitSetState(git *github.Client, state string, owner string, repo string, commit string) {
	git.Repositories.CreateStatus(owner, repo, commit, &github.RepoStatus{State: &state})
}

// Extract a zip file to a destination.
// By "swtdrgn" from http://stackoverflow.com/a/24430720
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		fpath := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
		} else {
			var fdir string
			if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
				fdir = fpath[:lastIndex]
			}

			err = os.MkdirAll(fdir, f.Mode())
			if err != nil {
				log.Error(err)
				return err
			}
			f, err := os.OpenFile(
				fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer f.Close()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package model

import (
	"math"
	"reflect"
	"testing"
	"time"
)

func TestEmptyPost(t *testing.T) {
	post := NewPost()
	if !reflect.DeepEqual(post, &Post{-1, "", post.Published, -1, -1, nil}) {
		t.Error("post not empty")
	}
}

func TestValidatePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	post := NewPost()

	ok, errs := ValidatePost(db, post)
	if ok || len(errs) != 3 {
		t.Error("blank post should not validate")
	}

	post.UserId = math.MaxInt64
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 3 {
		t.Error("this is not a real user")
	}

	post.UserId = 1
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 2 {
		t.Error("valid user, still missing topic and text")
	}

	post.Text = "Hello!"
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("still missing topic!")
	}

	post.TopicId = math.MaxInt64
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("should not validate invalid forum")
	}

	post.TopicId = 2
	ok, errs = ValidatePost(db, post)
	if !ok || len(errs) != 0 {
		t.Error("post should now validate")
	}
}

func TestWhitespacePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	whitespace := "\t\n\t\n\t\n    \t\n\t\n\t\n"
	post := &Post{1, whitespace, time.Now().UTC(), 1, 1, nil}
	ok, errs := ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("whitespace is only invalid item")
	}
}

func TestFindOnePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	post, err := FindOnePost(db, "1")
	if err != nil {
		t.Fatal(err)
	}

	if post.Id != 1 {
		t.Error("wrong id")
	}

	if post.User == nil {
		t.Error("no forum relation")
	}

	if post.TopicId != 1 {
		t.Error("wrong forum")
	}

	if post.Text != "test" {
		t.Error("wrong text")
	}
}

func TestFindPostsNoLimit(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	postsTopic1, err := FindPosts(db, "1", math.MaxUint32, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 11 {
		t.Error("wrong number of posts")
	}
}

func TestFindPostsSmallLimit(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	postsTopic1, err := FindPosts(db, "1", 5, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 5 {
		t.Error("wrong number of posts")
	}
}
<commit_msg>more tests<commit_after>package model

import (
	"math"
	"reflect"
	"testing"
	"time"
)

func TestEmptyPost(t *testing.T) {
	post := NewPost()
	if !reflect.DeepEqual(post, &Post{-1, "", post.Published, -1, -1, nil}) {
		t.Error("post not empty")
	}
}

func TestValidatePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	post := NewPost()

	ok, errs := ValidatePost(db, post)
	if ok || len(errs) != 3 {
		t.Error("blank post should not validate")
	}

	post.UserId = math.MaxInt64
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 3 {
		t.Error("this is not a real user")
	}

	post.UserId = 1
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 2 {
		t.Error("valid user, still missing topic and text")
	}

	post.Text = "Hello!"
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("still missing topic!")
	}

	post.TopicId = math.MaxInt64
	ok, errs = ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("should not validate invalid forum")
	}

	post.TopicId = 2
	ok, errs = ValidatePost(db, post)
	if !ok || len(errs) != 0 {
		t.Error("post should now validate")
	}
}

func TestWhitespacePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	whitespace := "\t\n\t\n\t\n    \t\n\t\n\t\n"
	post := &Post{1, whitespace, time.Now().UTC(), 1, 1, nil}
	ok, errs := ValidatePost(db, post)
	if ok || len(errs) != 1 {
		t.Error("whitespace is only invalid item")
	}
}

func TestFindOnePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	post, err := FindOnePost(db, "1")
	if err != nil {
		t.Fatal(err)
	}

	if post.Id != 1 {
		t.Error("wrong id")
	}

	if post.User == nil {
		t.Error("no forum relation")
	}

	if post.TopicId != 1 {
		t.Error("wrong forum")
	}

	if post.Text != "test" {
		t.Error("wrong text")
	}
}

func TestFindPostsNoLimit(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	postsTopic1, err := FindPosts(db, "1", math.MaxUint32, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 11 {
		t.Error("wrong number of posts")
	}
}

func TestFindPostsSmallLimit(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	postsTopic1, err := FindPosts(db, "1", 5, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 5 {
		t.Error("wrong number of posts")
	}
}

func TestDeletePost(t *testing.T) {
	db, err := GetMockupDB()
	defer db.Close()
	if err != nil {
		t.Fatal(err)
	}

	postsTopic1, err := FindPosts(db, "1", math.MaxUint32, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 11 {
		t.Error("wrong number of posts")
	}

	err = DeletePost(db, 1)
	if err != nil {
		t.Error(err)
	}

	postsTopic1, err = FindPosts(db, "1", math.MaxUint32, 0)
	if err != nil {
		t.Fatal(err)
	}

	if len(postsTopic1) != 10 {
		t.Error("wrong number of posts")
	}
}
<|endoftext|>


<commit_before>package epmd

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

type NodeInfo struct {
	FullName string
	Name     string
	Domain   string
	Port     uint16
	Type     uint8				// 77 — Normal Erlang node, 72 — hidden (C-node)
	Protocol uint8
	HighVsn  uint16
	LowVsn   uint16
	Extra    []byte
	Creation uint16
}

func Read_ALIVE2_REQ(buf []byte) (ni *NodeInfo) {
	if len(buf) < 11 {
		return nil
	}
	nPort := binary.BigEndian.Uint16(buf[1:3])
	nType := buf[3]
	nProto := buf[4]
	highVsn := binary.BigEndian.Uint16(buf[5:7])
	lowVsn := binary.BigEndian.Uint16(buf[7:9])
	nLen := binary.BigEndian.Uint16(buf[9:11])

	offset := (11 + nLen)
	if len(buf) < int(offset+2) {
		return nil
	}
	nName := string(buf[11:offset])
	nELen := binary.BigEndian.Uint16(buf[offset:(offset + 2)])
	if len(buf) < int(offset+2+nELen) {
		return nil
	}
	nExtra := buf[(offset + 2):]

	return &NodeInfo{
		Name:     nName,
		Port:     nPort,
		Type:     nType,
		Protocol: nProto,
		HighVsn:  highVsn,
		LowVsn:   lowVsn,
		Extra:    nExtra,
		Creation: 0,
	}
}

func Compose_ALIVE2_REQ(ni *NodeInfo) (reply []byte) {
	reply = make([]byte, 14+len(ni.Name)+len(ni.Extra))
	reply[0] = byte(ALIVE2_REQ)
	binary.BigEndian.PutUint16(reply[1:3], ni.Port)
	reply[3] = ni.Type
	reply[4] = ni.Protocol
	binary.BigEndian.PutUint16(reply[5:7], ni.HighVsn)
	binary.BigEndian.PutUint16(reply[7:9], ni.LowVsn)
	nLen := len(ni.Name)
	binary.BigEndian.PutUint16(reply[9:11], uint16(nLen))
	offset := (11 + nLen)
	copy(reply[11:offset], ni.Name)
	nELen := len(ni.Extra)
	binary.BigEndian.PutUint16(reply[offset:offset+2], uint16(nELen))
	copy(reply[offset+2:offset+2+nELen], ni.Extra)
	return
}

func Compose_ALIVE2_RESP(isOk bool, value ...interface{}) (reply []byte) {
	reply = make([]byte, 4)
	reply[0] = byte(ALIVE2_RESP)
	if isOk {
		reply[1] = 0
		binary.BigEndian.PutUint16(reply[2:4], value[0].(*NodeInfo).Creation)
	} else {
		reply[1] = 1
		binary.BigEndian.PutUint16(reply[2:4], 99)
	}
	return
}

func Read_ALIVE2_RESP(reply []byte) (creation uint16, ok bool) {
	if reply[1] == 0 {
		ok = true
		creation = binary.BigEndian.Uint16(reply[2:4])
	} else {
		ok = false
		creation = binary.BigEndian.Uint16(reply[2:4])
	}
	return
}

func Read_PORT_PLEASE2_REQ(buf []byte) (name string) {
	name = string(buf[1:])
	return
}

func Compose_PORT2_RESP(ni *NodeInfo) (reply []byte) {
	if ni != nil {
		reply = make([]byte, 14+len(ni.Name)+len(ni.Extra))
		reply[0] = byte(PORT2_RESP)
		reply[1] = 0 // OK
		binary.BigEndian.PutUint16(reply[2:4], ni.Port)
		reply[4] = ni.Type
		reply[5] = ni.Protocol
		binary.BigEndian.PutUint16(reply[6:8], ni.HighVsn)
		binary.BigEndian.PutUint16(reply[8:10], ni.LowVsn)
		nLen := len(ni.Name)
		binary.BigEndian.PutUint16(reply[10:12], uint16(nLen))
		offset := (12 + nLen)
		copy(reply[12:offset], ni.Name)
		nELen := len(ni.Extra)
		binary.BigEndian.PutUint16(reply[offset:offset+2], uint16(nELen))
		copy(reply[offset+2:offset+2+nELen], ni.Extra)
	} else {
		reply = make([]byte, 2)
		reply[0] = byte(PORT2_RESP)
		reply[1] = 1 // ERROR
	}
	return
}

func Compose_START_NAMES_RESP(replyB *bytes.Buffer, port int) {
	reply := make([]byte, 4)
	binary.BigEndian.PutUint32(reply, uint32(port))
	replyB.Write(reply)
	return
}

func Append_NAMES_RESP(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("name %s at port %d\n", ni.Name, ni.Port)))
}

func Append_DUMP_RESP_ACTIVE(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("active name     <%s> at port %d\n", ni.Name, ni.Port)))
}

func Append_DUMP_RESP_UNUSED(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("old/unused name <%s>, port = %d\n", ni.Name, ni.Port)))
}

func Compose_KILL_RESP() (reply []byte) {
	reply = []byte("OK")
	return
}

func Read_STOP_REQ(buf []byte) (name string) {
	name = string(buf[1:])
	return
}

func Compose_STOP_RESP(isOk bool) (reply []byte) {
	if isOk {
		reply = []byte("STOPPED")
	} else {
		reply = []byte("NOEXIST")
	}
	return
}
<commit_msg>Code formatting<commit_after>package epmd

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

type NodeInfo struct {
	FullName string
	Name     string
	Domain   string
	Port     uint16
	Type     uint8 // 77 — Normal Erlang node, 72 — hidden (C-node)
	Protocol uint8
	HighVsn  uint16
	LowVsn   uint16
	Extra    []byte
	Creation uint16
}

func Read_ALIVE2_REQ(buf []byte) (ni *NodeInfo) {
	if len(buf) < 11 {
		return nil
	}
	nPort := binary.BigEndian.Uint16(buf[1:3])
	nType := buf[3]
	nProto := buf[4]
	highVsn := binary.BigEndian.Uint16(buf[5:7])
	lowVsn := binary.BigEndian.Uint16(buf[7:9])
	nLen := binary.BigEndian.Uint16(buf[9:11])

	offset := (11 + nLen)
	if len(buf) < int(offset+2) {
		return nil
	}
	nName := string(buf[11:offset])
	nELen := binary.BigEndian.Uint16(buf[offset:(offset + 2)])
	if len(buf) < int(offset+2+nELen) {
		return nil
	}
	nExtra := buf[(offset + 2):]

	return &NodeInfo{
		Name:     nName,
		Port:     nPort,
		Type:     nType,
		Protocol: nProto,
		HighVsn:  highVsn,
		LowVsn:   lowVsn,
		Extra:    nExtra,
		Creation: 0,
	}
}

func Compose_ALIVE2_REQ(ni *NodeInfo) (reply []byte) {
	reply = make([]byte, 14+len(ni.Name)+len(ni.Extra))
	reply[0] = byte(ALIVE2_REQ)
	binary.BigEndian.PutUint16(reply[1:3], ni.Port)
	reply[3] = ni.Type
	reply[4] = ni.Protocol
	binary.BigEndian.PutUint16(reply[5:7], ni.HighVsn)
	binary.BigEndian.PutUint16(reply[7:9], ni.LowVsn)
	nLen := len(ni.Name)
	binary.BigEndian.PutUint16(reply[9:11], uint16(nLen))
	offset := (11 + nLen)
	copy(reply[11:offset], ni.Name)
	nELen := len(ni.Extra)
	binary.BigEndian.PutUint16(reply[offset:offset+2], uint16(nELen))
	copy(reply[offset+2:offset+2+nELen], ni.Extra)
	return
}

func Compose_ALIVE2_RESP(isOk bool, value ...interface{}) (reply []byte) {
	reply = make([]byte, 4)
	reply[0] = byte(ALIVE2_RESP)
	if isOk {
		reply[1] = 0
		binary.BigEndian.PutUint16(reply[2:4], value[0].(*NodeInfo).Creation)
	} else {
		reply[1] = 1
		binary.BigEndian.PutUint16(reply[2:4], 99)
	}
	return
}

func Read_ALIVE2_RESP(reply []byte) (creation uint16, ok bool) {
	if reply[1] == 0 {
		ok = true
		creation = binary.BigEndian.Uint16(reply[2:4])
	} else {
		ok = false
		creation = binary.BigEndian.Uint16(reply[2:4])
	}
	return
}

func Read_PORT_PLEASE2_REQ(buf []byte) (name string) {
	name = string(buf[1:])
	return
}

func Compose_PORT2_RESP(ni *NodeInfo) (reply []byte) {
	if ni != nil {
		reply = make([]byte, 14+len(ni.Name)+len(ni.Extra))
		reply[0] = byte(PORT2_RESP)
		reply[1] = 0 // OK
		binary.BigEndian.PutUint16(reply[2:4], ni.Port)
		reply[4] = ni.Type
		reply[5] = ni.Protocol
		binary.BigEndian.PutUint16(reply[6:8], ni.HighVsn)
		binary.BigEndian.PutUint16(reply[8:10], ni.LowVsn)
		nLen := len(ni.Name)
		binary.BigEndian.PutUint16(reply[10:12], uint16(nLen))
		offset := (12 + nLen)
		copy(reply[12:offset], ni.Name)
		nELen := len(ni.Extra)
		binary.BigEndian.PutUint16(reply[offset:offset+2], uint16(nELen))
		copy(reply[offset+2:offset+2+nELen], ni.Extra)
	} else {
		reply = make([]byte, 2)
		reply[0] = byte(PORT2_RESP)
		reply[1] = 1 // ERROR
	}
	return
}

func Compose_START_NAMES_RESP(replyB *bytes.Buffer, port int) {
	reply := make([]byte, 4)
	binary.BigEndian.PutUint32(reply, uint32(port))
	replyB.Write(reply)
	return
}

func Append_NAMES_RESP(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("name %s at port %d\n", ni.Name, ni.Port)))
}

func Append_DUMP_RESP_ACTIVE(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("active name     <%s> at port %d\n", ni.Name, ni.Port)))
}

func Append_DUMP_RESP_UNUSED(replyB *bytes.Buffer, ni *NodeInfo) {
	replyB.Write([]byte(fmt.Sprintf("old/unused name <%s>, port = %d\n", ni.Name, ni.Port)))
}

func Compose_KILL_RESP() (reply []byte) {
	reply = []byte("OK")
	return
}

func Read_STOP_REQ(buf []byte) (name string) {
	name = string(buf[1:])
	return
}

func Compose_STOP_RESP(isOk bool) (reply []byte) {
	if isOk {
		reply = []byte("STOPPED")
	} else {
		reply = []byte("NOEXIST")
	}
	return
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"encoding/json"
	"github.com/jmoiron/sqlx"
	"github.com/mssola/user_agent"
	"net"
	"strconv"
	"strings"
)

type Campaign int

func (id Campaign) IntID() int {
	return int(id)
}

func (id Campaign) StringID() string {
	return strconv.Itoa(id.IntID())
}

type CampaignReportRecipients struct {
	ID          int             `db:"id" json:"id"`
	Email       string          `db:"email" json:"email"`
	Name        string          `db:"name" json:"name"`
	At          string          `db:"at" json:"at"`
	Status      sql.NullString  `db:"status" json:"-"`
	StatusValid string          `json:"status"`
	Open        bool            `db:"open" json:"open"`
	Data        sql.NullString  `db:"data" json:"-"`
	DataValid   json.RawMessage `json:"data"`
}

func (cq *CampaignReportRecipients) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
	if cq.Status.Valid {
		cq.StatusValid = cq.Status.String
	} else {
		cq.StatusValid = "null"
	}
}

func (id Campaign) ReportRecipients() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
  			r.id,
  			r.email AS "email",
  			r.name,
			r.date AS "at",
  			r.status,
  			IF(COALESCE(r.web_agent,r.client_agent) IS NULL, false, true) as "open",
    		`+SQLKeyValueTableToJSON("d.key", "d.value", "parameter d", "d.recipient_id=r.id")+` AS "data"
 		FROM recipient r
 		WHERE r.removed=0 AND r.campaign_id=?`, id)
}

type CampaignReportUnsubscribed struct {
	Email     string          `db:"email" json:"email"`
	At        string          `db:"at" json:"at"`
	Data      sql.NullString  `db:"data" json:"-"`
	DataValid json.RawMessage `json:"data"`
}

func (cq *CampaignReportUnsubscribed) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
}

func (id Campaign) ReportUnsubscribed() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
        	u.email,
			u.date as "at",
            `+SQLKeyValueTableToJSON("d.name", "d.value", "unsubscribe_extra d", "d.unsubscribe_id=u.id")+` AS "data"
		FROM unsubscribe u
		WHERE u.campaign_id=?`, id)
}

type CampaignReportQuestion struct {
	ID        int             `db:"id" json:"id"`
	Email     string          `db:"email" json:"email"`
	At        string          `db:"at" json:"at"`
	Data      sql.NullString  `db:"data" json:"-"`
	DataValid json.RawMessage `json:"data"`
}

func (cq *CampaignReportQuestion) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
}

func (id Campaign) ReportQuestion() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
			q.recipient_id AS id,
			r.email,
			q.at,
			`+SQLKeyValueTableToJSON("d.name", "d.value", "question_data d", "d.question_id=q.id")+` AS "data"
		FROM question q
		LEFT JOIN recipient r ON q.recipient_id=r.id
		WHERE r.campaign_id=?`, id)
}

type CampaignReportClicks struct {
	ID    int    `db:"id" json:"id"`
	Email string `db:"email" json:"email"`
	At    string `db:"at" json:"at"`
	URL   string `db:"url" json:"url"`
}

func (cq *CampaignReportClicks) Validate() {}

func (id Campaign) ReportClicks() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
			r.id,
			r.email,
			j.date as at,
			j.url
		FROM jumping j INNER JOIN recipient r ON j.recipient_id=r.id
		WHERE r.removed=0
			AND j.url NOT IN ('`+OpenTrace+`','`+Unsubscribe+`')
			AND j.campaign_id=?
		ORDER BY r.id, id`, id)
}

type UserAgent struct {
	IP             string `json:"ip"`
	IsMobile       bool   `json:"is_mobile"`
	IsBot          bool   `json:"is_bot"`
	Platform       string `json:"platform"`
	OS             string `json:"os"`
	EngineName     string `json:"engine_name"`
	EngineVersion  string `json:"engine_version"`
	BrowserName    string `json:"browser_name"`
	BrowserVersion string `json:"browser_version"`
}

func (ua *UserAgent) Parse(str string) {
	split := strings.SplitN(str, " ", 2)
	if len(split) != 2 {
		return
	}
	ua.IP = split[0]
	if ua.isGoogle(ua.IP) {
		ua.IsBot = true
		ua.BrowserName = "Google bot"
		return
	}
	if ua.isMailRu(ua.IP) {
		ua.IsBot = true
		ua.BrowserName = "MailRu bot"
		return
	}
	agent := user_agent.New(split[1])
	ua.IsMobile = agent.Mobile()
	ua.IsBot = agent.Bot()
	ua.Platform = agent.Platform()
	ua.OS = agent.OS()
	ua.EngineName, ua.EngineVersion = agent.Engine()
	ua.BrowserName, ua.BrowserVersion = agent.Browser()
}

func (ua UserAgent) isMailRu(ip string) bool {
	// Mail.ru bot from networks
	// 188.93.56.0/24
	// 185.30.176.0/23
	// has user agent
	// Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.2 Safari/537.36
	return ua.isIPSubnet(ip, "188.93.56.0", 24, 32) || ua.isIPSubnet(ip, "185.30.176.0", 23, 32)
}

func (ua UserAgent) isGoogle(ip string) bool {
	// Google proxy bot from networks
	// 66.102.0.0/20
	// 66.249.64.0/19
	// 64.233.160.0/19
	// has user agent
	// Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)
	return ua.isIPSubnet(ip, "66.102.0.0", 20, 32) || ua.isIPSubnet(ip, "66.249.64.0", 19, 32) || ua.isIPSubnet(ip, "64.233.160.0", 19, 32)
}

func (ua UserAgent) isIPSubnet(ip, network string, ones, bits int) bool {
	ipv4Mask := net.CIDRMask(ones, bits)
	return network == net.ParseIP(ip).Mask(ipv4Mask).String()
}

type CampaignReportUserAgent struct {
	ID            int            `db:"id" json:"id"`
	Email         string         `db:"email" json:"email"`
	Name          string         `db:"name" json:"name"`
	Client        sql.NullString `db:"client_agent" json:"-"`
	Browser       sql.NullString `db:"web_agent" json:"-"`
	ClientParsed  *UserAgent     `json:"client"`
	BrowserParsed *UserAgent     `json:"browser"`
}

func (rua *CampaignReportUserAgent) Validate() {
	if rua.Client.Valid {
		rua.ClientParsed = new(UserAgent)
		rua.ClientParsed.Parse(rua.Client.String)
	}
	if rua.Browser.Valid {
		rua.BrowserParsed = new(UserAgent)
		rua.BrowserParsed.Parse(rua.Browser.String)
	}
}

func (id Campaign) ReportUserAgent() (*sqlx.Rows, error) {
	return Db.Queryx(`SELECT id, email, name, client_agent, web_agent FROM recipient WHERE campaign_id=?`, id)
}
<commit_msg>open trace in report by click<commit_after>package models

import (
	"database/sql"
	"encoding/json"
	"github.com/jmoiron/sqlx"
	"github.com/mssola/user_agent"
	"net"
	"strconv"
	"strings"
)

type Campaign int

func (id Campaign) IntID() int {
	return int(id)
}

func (id Campaign) StringID() string {
	return strconv.Itoa(id.IntID())
}

type CampaignReportRecipients struct {
	ID          int             `db:"id" json:"id"`
	Email       string          `db:"email" json:"email"`
	Name        string          `db:"name" json:"name"`
	At          string          `db:"at" json:"at"`
	Status      sql.NullString  `db:"status" json:"-"`
	StatusValid string          `json:"status"`
	Open        bool            `db:"open" json:"open"`
	Data        sql.NullString  `db:"data" json:"-"`
	DataValid   json.RawMessage `json:"data"`
}

func (cq *CampaignReportRecipients) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
	if cq.Status.Valid {
		cq.StatusValid = cq.Status.String
	} else {
		cq.StatusValid = "null"
	}
}

func (id Campaign) ReportRecipients() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
  			r.id,
  			r.email AS "email",
  			r.name,
			r.date AS "at",
  			r.status,
  			IF(COALESCE(r.web_agent,r.client_agent) IS NULL, false, true) as "open",
    		`+SQLKeyValueTableToJSON("d.key", "d.value", "parameter d", "d.recipient_id=r.id")+` AS "data"
 		FROM recipient r
 		WHERE r.removed=0 AND r.campaign_id=?`, id)
}

type CampaignReportUnsubscribed struct {
	Email     string          `db:"email" json:"email"`
	At        string          `db:"at" json:"at"`
	Data      sql.NullString  `db:"data" json:"-"`
	DataValid json.RawMessage `json:"data"`
}

func (cq *CampaignReportUnsubscribed) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
}

func (id Campaign) ReportUnsubscribed() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
        	u.email,
			u.date as "at",
            `+SQLKeyValueTableToJSON("d.name", "d.value", "unsubscribe_extra d", "d.unsubscribe_id=u.id")+` AS "data"
		FROM unsubscribe u
		WHERE u.campaign_id=?`, id)
}

type CampaignReportQuestion struct {
	ID        int             `db:"id" json:"id"`
	Email     string          `db:"email" json:"email"`
	At        string          `db:"at" json:"at"`
	Data      sql.NullString  `db:"data" json:"-"`
	DataValid json.RawMessage `json:"data"`
}

func (cq *CampaignReportQuestion) Validate() {
	if cq.Data.Valid {
		cq.DataValid = []byte(cq.Data.String)
	} else {
		cq.DataValid = []byte("null")
	}
}

func (id Campaign) ReportQuestion() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
			q.recipient_id AS id,
			r.email,
			q.at,
			`+SQLKeyValueTableToJSON("d.name", "d.value", "question_data d", "d.question_id=q.id")+` AS "data"
		FROM question q
		LEFT JOIN recipient r ON q.recipient_id=r.id
		WHERE r.campaign_id=?`, id)
}

type CampaignReportClicks struct {
	ID    int    `db:"id" json:"id"`
	Email string `db:"email" json:"email"`
	At    string `db:"at" json:"at"`
	URL   string `db:"url" json:"url"`
}

func (cq *CampaignReportClicks) Validate() {}

func (id Campaign) ReportClicks() (*sqlx.Rows, error) {
	return Db.Queryx(`
		SELECT
			r.id,
			r.email,
			j.date as at,
			j.url
		FROM jumping j INNER JOIN recipient r ON j.recipient_id=r.id
		WHERE r.removed=0
			AND j.url <> '`+Unsubscribe+`'
			AND j.campaign_id=?
		ORDER BY r.id, id`, id)
}

type UserAgent struct {
	IP             string `json:"ip"`
	IsMobile       bool   `json:"is_mobile"`
	IsBot          bool   `json:"is_bot"`
	Platform       string `json:"platform"`
	OS             string `json:"os"`
	EngineName     string `json:"engine_name"`
	EngineVersion  string `json:"engine_version"`
	BrowserName    string `json:"browser_name"`
	BrowserVersion string `json:"browser_version"`
}

func (ua *UserAgent) Parse(str string) {
	split := strings.SplitN(str, " ", 2)
	if len(split) != 2 {
		return
	}
	ua.IP = split[0]
	if ua.isGoogle(ua.IP) {
		ua.IsBot = true
		ua.BrowserName = "Google bot"
		return
	}
	if ua.isMailRu(ua.IP) {
		ua.IsBot = true
		ua.BrowserName = "MailRu bot"
		return
	}
	agent := user_agent.New(split[1])
	ua.IsMobile = agent.Mobile()
	ua.IsBot = agent.Bot()
	ua.Platform = agent.Platform()
	ua.OS = agent.OS()
	ua.EngineName, ua.EngineVersion = agent.Engine()
	ua.BrowserName, ua.BrowserVersion = agent.Browser()
}

func (ua UserAgent) isMailRu(ip string) bool {
	// Mail.ru bot from networks
	// 188.93.56.0/24
	// 185.30.176.0/23
	// has user agent
	// Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.2 Safari/537.36
	return ua.isIPSubnet(ip, "188.93.56.0", 24, 32) || ua.isIPSubnet(ip, "185.30.176.0", 23, 32)
}

func (ua UserAgent) isGoogle(ip string) bool {
	// Google proxy bot from networks
	// 66.102.0.0/20
	// 66.249.64.0/19
	// 64.233.160.0/19
	// has user agent
	// Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)
	return ua.isIPSubnet(ip, "66.102.0.0", 20, 32) || ua.isIPSubnet(ip, "66.249.64.0", 19, 32) || ua.isIPSubnet(ip, "64.233.160.0", 19, 32)
}

func (ua UserAgent) isIPSubnet(ip, network string, ones, bits int) bool {
	ipv4Mask := net.CIDRMask(ones, bits)
	return network == net.ParseIP(ip).Mask(ipv4Mask).String()
}

type CampaignReportUserAgent struct {
	ID            int            `db:"id" json:"id"`
	Email         string         `db:"email" json:"email"`
	Name          string         `db:"name" json:"name"`
	Client        sql.NullString `db:"client_agent" json:"-"`
	Browser       sql.NullString `db:"web_agent" json:"-"`
	ClientParsed  *UserAgent     `json:"client"`
	BrowserParsed *UserAgent     `json:"browser"`
}

func (rua *CampaignReportUserAgent) Validate() {
	if rua.Client.Valid {
		rua.ClientParsed = new(UserAgent)
		rua.ClientParsed.Parse(rua.Client.String)
	}
	if rua.Browser.Valid {
		rua.BrowserParsed = new(UserAgent)
		rua.BrowserParsed.Parse(rua.Browser.String)
	}
}

func (id Campaign) ReportUserAgent() (*sqlx.Rows, error) {
	return Db.Queryx(`SELECT id, email, name, client_agent, web_agent FROM recipient WHERE campaign_id=?`, id)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	fmt.Println("Hello World")
}
<commit_msg>Playing around with types and Println<commit_after>package main

import "fmt"

func main() {
	fmt.Println("Hello" + " World")
	fmt.Println("My", 0 + 1, "st greet is", true || false)
}
<|endoftext|>


<commit_before>package hive

import (
	"log"
	"strings"
	"sync"
	"time"
)

// manager model
type HiveManager struct {
	FreeWorkers  chan *HiveWorker
	Tasks        chan string
	Done         chan bool
	TimeProcess  chan int64
	LastProcess  int64
	TotalTimeOut int64
}

// worker model
type HiveWorker struct {
	WorkerId    int
	TimeProcess chan int64
	FreeWorkers chan *HiveWorker
	Context     *Hive
	IsConnOpen  bool
}

// initiate new manager
func NewHiveManager(numWorkers int) HiveManager {
	var totaltimeout int64 = 10

	m := HiveManager{}
	m.FreeWorkers = make(chan *HiveWorker, numWorkers)
	m.Tasks = make(chan string)
	m.TimeProcess = make(chan int64)
	m.TotalTimeOut = totaltimeout
	m.LastProcess = time.Now().Unix()
	m.Done = make(chan bool, 1)
	return m
}

// do monitoring worker thats free or not
func (m *HiveManager) DoMonitor(wg *sync.WaitGroup) {
	for {
		select {
		case task := <-m.Tasks:
			wg.Add(1)
			go m.AssignTask(task, wg)
		case result := <-m.TimeProcess:
			wg.Add(1)
			go m.InProgress(result, wg)
		case <-m.Done:
			m.Done <- true
			m.EndWorker()
			return
		}
	}
	wg.Wait()
}

// assign task to free worker
func (m *HiveManager) AssignTask(task string, wg *sync.WaitGroup) {
	defer wg.Done()
	select {
	case worker := <-m.FreeWorkers:
		log.Println("Assign task to worker", worker.WorkerId)
		wg.Add(1)
		go worker.Work(task, wg)
	case isDone := <-m.Done:
		m.Done <- isDone
		return
	}
}

// check if a task still in progress to wait it till finish
func (m *HiveManager) InProgress(result int64, wg *sync.WaitGroup) {
	defer wg.Done()
	m.LastProcess = int64(result)
}

// set the timeout to waiting for tasks execution
func (m *HiveManager) Timeout(seconds int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		if time.Now().Unix()-m.LastProcess > int64(seconds) {
			m.Done <- true
			return
		} else {
			time.Sleep(time.Millisecond)
		}
	}
}

func (m *HiveManager) EndWorker() {
	for {
		select {
		case worker := <-m.FreeWorkers:
			worker.Context.Conn.Close()
		default:
			return
		}
	}
}

// do a task for worker
func (w *HiveWorker) Work(task string, wg *sync.WaitGroup) {
	defer wg.Done()

	if !w.IsConnOpen {
		w.Context.Conn.Open()
		w.IsConnOpen = true
	}

	log.Println("Do task ", task)
	query := task
	if strings.LastIndex(query, ";") == -1 {
		query += ";"
	}
	w.Context.Conn.SendInput(query)

	w.TimeProcess <- time.Now().Unix()
	w.FreeWorkers <- w
}
<commit_msg>modify pointer part 8<commit_after>package hive

import (
	"log"
	"strings"
	"sync"
	"time"
)

// manager model
type HiveManager struct {
	FreeWorkers  chan *HiveWorker
	Tasks        chan string
	Done         chan bool
	TimeProcess  chan int64
	LastProcess  int64
	TotalTimeOut int64
}

// worker model
type HiveWorker struct {
	WorkerId    int
	TimeProcess chan int64
	FreeWorkers chan *HiveWorker
	Context     *Hive
	IsConnOpen  bool
}

// initiate new manager
func NewHiveManager(numWorkers int) HiveManager {
	var totaltimeout int64 = 10

	m := HiveManager{}
	m.FreeWorkers = make(chan *HiveWorker, numWorkers)
	m.Tasks = make(chan string)
	m.TimeProcess = make(chan int64)
	m.TotalTimeOut = totaltimeout
	m.LastProcess = time.Now().Unix()
	m.Done = make(chan bool, 1)
	return m
}

// do monitoring worker thats free or not
func (m *HiveManager) DoMonitor(wg *sync.WaitGroup) {
	for {
		select {
		case task := <-m.Tasks:
			wg.Add(1)
			go m.AssignTask(task, wg)
		case result := <-m.TimeProcess:
			wg.Add(1)
			go m.InProgress(result, wg)
		case <-m.Done:
			m.Done <- true
			return
		}
	}
	wg.Wait()
}

// assign task to free worker
func (m *HiveManager) AssignTask(task string, wg *sync.WaitGroup) {
	defer wg.Done()
	select {
	case worker := <-m.FreeWorkers:
		log.Println("Assign task to worker", worker.WorkerId)
		wg.Add(1)
		go worker.Work(task, wg)
	case isDone := <-m.Done:
		m.Done <- isDone
		return
	}
}

// check if a task still in progress to wait it till finish
func (m *HiveManager) InProgress(result int64, wg *sync.WaitGroup) {
	defer wg.Done()
	m.LastProcess = int64(result)
}

// set the timeout to waiting for tasks execution
func (m *HiveManager) Timeout(seconds int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		if time.Now().Unix()-m.LastProcess > int64(seconds) {
			m.Done <- true
			return
		} else {
			time.Sleep(time.Millisecond)
		}
	}
}

func (m *HiveManager) EndWorker() {
	for {
		select {
		case worker := <-m.FreeWorkers:
			if worker.IsConnOpen {
				worker.Context.Conn.Close()
			}
		default:
			return
		}
	}
}

// do a task for worker
func (w *HiveWorker) Work(task string, wg *sync.WaitGroup) {
	defer wg.Done()

	if !w.IsConnOpen {
		w.Context.Conn.Open()
		w.IsConnOpen = true
	}

	log.Println("Do task ", task)
	query := task
	if strings.LastIndex(query, ";") == -1 {
		query += ";"
	}
	w.Context.Conn.SendInput(query)

	w.TimeProcess <- time.Now().Unix()
	w.FreeWorkers <- w
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package hopwatch

import (
	"io"
	"net/http"
)

func js(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/javascript")
	io.WriteString(w, `
	var wsUri = "ws://" + window.location.hostname + ":" + window.location.port + "/hopwatch";
	var output;
	var websocket = new WebSocket(wsUri);
	var connected = false;
	var suspended = false;
	var golineSize = 18;

	function init() {
		output = document.getElementById("output");
		setupWebSocket();
	}
	function setupWebSocket() {
		websocket.onopen = function(evt) { onOpen(evt) };
		websocket.onclose = function(evt) { onClose(evt) };
		websocket.onmessage = function(evt) { onMessage(evt) };
		websocket.onerror = function(evt) { onError(evt) };
	}
	function onOpen(evt) {
		connected = true;
		document.getElementById("disconnect").className = "buttonEnabled";
		writeToScreen("<-> connected","info mono");
		sendConnected();
	}
	function onClose(evt) {
		handleDisconnected();
	}
	function onMessage(evt) {
 		try {
            var cmd = JSON.parse(evt.data);
        } catch (e) {
            console.log('[hopwatch] failed to read valid JSON: ', message.data);
            return;
        }
        // console.log("[hopwatch] received: " + evt.data);
        if (cmd.Action == "display") {
        	var logdiv = document.createElement("div");
			logdiv.className = "logline"
        	addTime(logdiv);
        	addGoline(logdiv,cmd);
        	addMessage(logdiv,watchParametersToHtml(cmd.Parameters),"watch mono");
        	output.appendChild(logdiv);
        	logdiv.scrollIntoView();
        	sendResume();
        	return;
        }
        if (cmd.Action == "print") {
        	var logdiv = document.createElement("div");
			logdiv.className = "logline"
        	addTime(logdiv);
        	addGoline(logdiv,cmd);
        	addMessage(logdiv,cmd.Parameters["line"],"watch mono");
        	output.appendChild(logdiv);
			logdiv.scrollIntoView();
        	sendResume();
        	return;
        }
        if (cmd.Action == "break") {
        	handleSuspended(cmd);
        	return;
        }
	}
	function onError(evt) {
		writeToScreen(evt,"err mono");
	}
	function handleSuspended(cmd) {
        suspended = true;
        document.getElementById("resume").className = "buttonEnabled";
        var logdiv = document.createElement("div");
		logdiv.className = "logline"
       	addTime(logdiv);
       	addGoline(logdiv,cmd);
       	var celldiv = addMessage(logdiv,"--> program suspended", "suspend mono");
       	addStack(celldiv,cmd);
       	output.appendChild(logdiv);
		logdiv.scrollIntoView();
       	handleSourceUpdate(cmd);
	}
	func handleSourceUpdate(cmd) {
		loadSource(cmd.Parameters["go.file"], cmd.Parameters["go.line"]);
	}
	function writeToScreen(text,cls) {
		var logdiv = document.createElement("div");
		logdiv.className = "logline"
		addTime(logdiv);
		addEmptiness(logdiv);
		addMessage(logdiv,text,cls)
		output.appendChild(logdiv);
	}
	function addTime(logdiv) {
		var stamp = document.createElement("span");
		stamp.innerHTML = timeHHMMSS();
		stamp.className = "time mono"
		logdiv.appendChild(stamp);
	}
	function addMessage(logdiv,msg,msgcls) {
		var txt = document.createElement("span");
		txt.className = msgcls
		var escaped = "";
		var arr = safe_tags(msg).split('\n');
		for (var i = 0; i < arr.length; i++) {
			if (i > 0) escaped += "\n";
			escaped += "        " + arr[i]; // TODO remove hack
		}
		txt.innerHTML = escaped;
		logdiv.appendChild(txt);
		return txt;
	}
	function safe_tags(str) {
	    return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') ;
	}
	function addEmptiness(logdiv) {
		var empt = document.createElement("span");
		empt.className = "goline"
		empt.innerHTML = " ";
		logdiv.appendChild(empt);
	}
	function addGoline(logdiv,cmd) {
		var where = document.createElement("span");
		where.className = "srcline"
		var link = document.createElement("a");
		link.href = "#";
		link.className = "goline mono";
		link.onclick = function() {
			loadSource(cmd.Parameters["go.file"], cmd.Parameters["go.line"]);
		};
		link.innerHTML = goline(cmd.Parameters);
		where.appendChild(link);
		logdiv.appendChild(where);
	}
	function loadSource(fileName, nr) {
		$("#gofile").html(shortenFileName(fileName));
		$("#gosource-pane").show();
		$.ajax({
			url:"/gosource?file="+fileName
		}).done(
			function(responseText,status,xhr) {
				handleSourceLoaded(responseText,nr);
			}
		);
	}
	function handleSourceLoaded(responseText,line) {
		gosource = $("#gosource");
		gosource.empty();
		var breakElm
		// Insert line numbers
		var arr = responseText.split('\n');
        for (var i = 0; i < arr.length; i++) {
	   		var nr = i + 1
			var buf = space_padded(nr) + arr[i];
        	var elm = document.createElement("div");
        	elm.innerHTML = buf;
        	if (line == nr) {
        		elm.className = "break";
				breakElm = elm
        	}
        	gosource.append(elm)
        }
		breakElm.scrollIntoView();
	}
	function space_padded(i) {
		var buf = "" + i
		if (i<1000) { buf += " " }
		if (i<100) { buf += " " }
		if (i<10) { buf += " " }
		return buf
	}
	function shortenFileName(fileName) {
		return fileName.length > 48 ? "..." + fileName.substring(fileName.length - 48) : fileName;
	}
	function addStack(celldiv,cmd) {
		var stack = cmd.Parameters["go.stack"];
		if (stack != null && stack.length > 0) {
			addNonEmptyStackTo(stack,celldiv);
		}
	}
	function addNonEmptyStackTo(stack,celldiv) {
		var toggle = document.createElement("a");
		toggle.href = "#";
		toggle.className = "toggle";
		toggle.onclick = function() { toggleStack(toggle); };
		toggle.innerHTML="stack ▶";
		celldiv.appendChild(toggle);

		var stk = document.createElement("div");
		stk.style.display = "none";
		var lines = document.createElement("pre");
		lines.innerHTML = stack
		lines.className = "stack mono"
		stk.appendChild(lines)
		celldiv.appendChild(stk)
	}
	function toggleStack(link) {
		var stack = link.nextSibling;
		if (stack.style.display == "none") {
			link.innerHTML = "stack ▼";
			stack.style.display = "block"
			stack.scrollIntoView();
		} else {
			link.innerHTML = "stack ▶";
			stack.style.display = "none";
		}
	}
	// http://www.quirksmode.org/js/keys.html
	function handleKeyDown(event) {
		if (event.keyCode == 119) {
			actionResume();
		}
	}
	function watchParametersToHtml(parameters) {
		var line = "";
		var multiline = false;
		for (var prop in parameters) {
			if (prop.slice(0,3) != "go.") {
				if (multiline) { line = line + ", "; }
				line = line + prop + "=" + parameters[prop];
				multiline = true;
			}
		}
		return line
	}
	function goline(parameters) {
		var f = parameters["go.file"]
		f = f.substr(f.lastIndexOf("/")+1)
		var padded = f + ":" + parameters["go.line"]
		while (padded.length > golineSize) {
			golineSize += 4
		}
		for (i=padded.length;i<golineSize;i++) padded += " "
		return padded
	}
	function actionResume() {
		if (!connected) return;
		if (!suspended) return;
		suspended = false;
		document.getElementById("resume").className = "buttonDisabled";
		// writeToScreen("<-- resume program","info mono");
		sendResume();
	}
	function actionDisconnect() {
		if (!connected) return;
		connected = false;
		document.getElementById("disconnect").className = "buttonDisabled";
		sendQuit();
		writeToScreen("<-- disconnect requested","info mono");
		websocket.close();  // seems not to trigger close on Go-side ; so handleDisconnected cannot be used here
	}
	function handleDisconnected() {
		connected = false;
		document.getElementById("resume").className = "buttonDisabled";
		document.getElementById("disconnect").className = "buttonDisabled";
		writeToScreen(">-< disconnected","info mono");
	}
	function timeHHMMSS()    { return new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1"); }
	function sendConnected() { doSend('{"Action":"connected"}'); }
	function sendResume()    { doSend('{"Action":"resume"}'); }
	function sendQuit()      { doSend('{"Action":"quit"}'); }
	function doSend(message) {
		// console.log("[hopwatch] send: " + message);
		websocket.send(message);
	}
	window.addEventListener("load", init, false);
	window.addEventListener("keydown", handleKeyDown, true); `)
	return
}
<commit_msg>fix typo<commit_after>// Copyright 2012-2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package hopwatch

import (
	"io"
	"net/http"
)

func js(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/javascript")
	io.WriteString(w, `
	var wsUri = "ws://" + window.location.hostname + ":" + window.location.port + "/hopwatch";
	var output;
	var websocket = new WebSocket(wsUri);
	var connected = false;
	var suspended = false;
	var golineSize = 18;

	function init() {
		output = document.getElementById("output");
		setupWebSocket();
	}
	function setupWebSocket() {
		websocket.onopen = function(evt) { onOpen(evt) };
		websocket.onclose = function(evt) { onClose(evt) };
		websocket.onmessage = function(evt) { onMessage(evt) };
		websocket.onerror = function(evt) { onError(evt) };
	}
	function onOpen(evt) {
		connected = true;
		document.getElementById("disconnect").className = "buttonEnabled";
		writeToScreen("<-> connected","info mono");
		sendConnected();
	}
	function onClose(evt) {
		handleDisconnected();
	}
	function onMessage(evt) {
 		try {
            var cmd = JSON.parse(evt.data);
        } catch (e) {
            console.log('[hopwatch] failed to read valid JSON: ', message.data);
            return;
        }
        // console.log("[hopwatch] received: " + evt.data);
        if (cmd.Action == "display") {
        	var logdiv = document.createElement("div");
			logdiv.className = "logline"
        	addTime(logdiv);
        	addGoline(logdiv,cmd);
        	addMessage(logdiv,watchParametersToHtml(cmd.Parameters),"watch mono");
        	output.appendChild(logdiv);
        	logdiv.scrollIntoView();
        	sendResume();
        	return;
        }
        if (cmd.Action == "print") {
        	var logdiv = document.createElement("div");
			logdiv.className = "logline"
        	addTime(logdiv);
        	addGoline(logdiv,cmd);
        	addMessage(logdiv,cmd.Parameters["line"],"watch mono");
        	output.appendChild(logdiv);
			logdiv.scrollIntoView();
        	sendResume();
        	return;
        }
        if (cmd.Action == "break") {
        	handleSuspended(cmd);
        	return;
        }
	}
	function onError(evt) {
		writeToScreen(evt,"err mono");
	}
	function handleSuspended(cmd) {
        suspended = true;
        document.getElementById("resume").className = "buttonEnabled";
        var logdiv = document.createElement("div");
		logdiv.className = "logline"
       	addTime(logdiv);
       	addGoline(logdiv,cmd);
       	var celldiv = addMessage(logdiv,"--> program suspended", "suspend mono");
       	addStack(celldiv,cmd);
       	output.appendChild(logdiv);
		logdiv.scrollIntoView();
       	handleSourceUpdate(cmd);
	}
	function handleSourceUpdate(cmd) {
		loadSource(cmd.Parameters["go.file"], cmd.Parameters["go.line"]);
	}
	function writeToScreen(text,cls) {
		var logdiv = document.createElement("div");
		logdiv.className = "logline"
		addTime(logdiv);
		addEmptiness(logdiv);
		addMessage(logdiv,text,cls);
		logdiv.scrollIntoView();
		output.appendChild(logdiv);
	}
	function addTime(logdiv) {
		var stamp = document.createElement("span");
		stamp.innerHTML = timeHHMMSS();
		stamp.className = "time mono"
		logdiv.appendChild(stamp);
	}
	function addMessage(logdiv,msg,msgcls) {
		var txt = document.createElement("span");
		txt.className = msgcls
		var escaped = "";
		var arr = safe_tags(msg).split('\n');
		for (var i = 0; i < arr.length; i++) {
			if (i > 0) escaped += "\n        "; // TODO remove hack
			escaped += arr[i];
		}
		txt.innerHTML = escaped;
		logdiv.appendChild(txt);
		return txt;
	}
	function safe_tags(str) {
	    return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') ;
	}
	function addEmptiness(logdiv) {
		var empt = document.createElement("span");
		empt.className = "goline"
		empt.innerHTML = " ";
		logdiv.appendChild(empt);
	}
	function addGoline(logdiv,cmd) {
		var where = document.createElement("span");
		where.className = "srcline"
		var link = document.createElement("a");
		link.href = "#";
		link.className = "goline mono";
		link.onclick = function() {
			loadSource(cmd.Parameters["go.file"], cmd.Parameters["go.line"]);
		};
		link.innerHTML = goline(cmd.Parameters);
		where.appendChild(link);
		logdiv.appendChild(where);
	}
	function loadSource(fileName, nr) {
		$("#gofile").html(shortenFileName(fileName));
		$("#gosource-pane").show();
		$.ajax({
			url:"/gosource?file="+fileName
		}).done(
			function(responseText,status,xhr) {
				handleSourceLoaded(responseText,nr);
			}
		);
	}
	function handleSourceLoaded(responseText,line) {
		gosource = $("#gosource");
		gosource.empty();
		var breakElm
		// Insert line numbers
		var arr = responseText.split('\n');
        for (var i = 0; i < arr.length; i++) {
	   		var nr = i + 1
			var buf = space_padded(nr) + arr[i];
        	var elm = document.createElement("div");
        	elm.innerHTML = buf;
        	if (line == nr) {
        		elm.className = "break";
				breakElm = elm
        	}
        	gosource.append(elm)
        }
		breakElm.scrollIntoView();
	}
	function space_padded(i) {
		var buf = "" + i
		if (i<1000) { buf += " " }
		if (i<100) { buf += " " }
		if (i<10) { buf += " " }
		return buf
	}
	function shortenFileName(fileName) {
		return fileName.length > 48 ? "..." + fileName.substring(fileName.length - 48) : fileName;
	}
	function addStack(celldiv,cmd) {
		var stack = cmd.Parameters["go.stack"];
		if (stack != null && stack.length > 0) {
			addNonEmptyStackTo(stack,celldiv);
		}
	}
	function addNonEmptyStackTo(stack,celldiv) {
		var toggle = document.createElement("a");
		toggle.href = "#";
		toggle.className = "toggle";
		toggle.onclick = function() { toggleStack(toggle); };
		toggle.innerHTML="stack ▶";
		celldiv.appendChild(toggle);

		var stk = document.createElement("div");
		stk.style.display = "none";
		var lines = document.createElement("pre");
		lines.innerHTML = stack
		lines.className = "stack mono"
		stk.appendChild(lines)
		celldiv.appendChild(stk)
	}
	function toggleStack(link) {
		var stack = link.nextSibling;
		if (stack.style.display == "none") {
			link.innerHTML = "stack ▼";
			stack.style.display = "block"
			stack.scrollIntoView();
		} else {
			link.innerHTML = "stack ▶";
			stack.style.display = "none";
		}
	}
	// http://www.quirksmode.org/js/keys.html
	function handleKeyDown(event) {
		if (event.keyCode == 119) {
			actionResume();
		}
	}
	function watchParametersToHtml(parameters) {
		var line = "";
		var multiline = false;
		for (var prop in parameters) {
			if (prop.slice(0,3) != "go.") {
				if (multiline) { line = line + ", "; }
				line = line + prop + "=" + parameters[prop];
				multiline = true;
			}
		}
		return line
	}
	function goline(parameters) {
		var f = parameters["go.file"]
		f = f.substr(f.lastIndexOf("/")+1)
		var padded = f + ":" + parameters["go.line"]
		while (padded.length > golineSize) {
			golineSize += 4
		}
		for (i=padded.length;i<golineSize;i++) padded += " "
		return padded
	}
	function actionResume() {
		if (!connected) return;
		if (!suspended) return;
		suspended = false;
		document.getElementById("resume").className = "buttonDisabled";
		// writeToScreen("<-- resume program","info mono");
		sendResume();
	}
	function actionDisconnect() {
		if (!connected) return;
		connected = false;
		document.getElementById("disconnect").className = "buttonDisabled";
		sendQuit();
		writeToScreen("<-- disconnect requested","info mono");
		websocket.close();  // seems not to trigger close on Go-side ; so handleDisconnected cannot be used here
	}
	function handleDisconnected() {
		connected = false;
		document.getElementById("resume").className = "buttonDisabled";
		document.getElementById("disconnect").className = "buttonDisabled";
		writeToScreen(">-< disconnected","info mono");
	}
	function timeHHMMSS()    { return new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1"); }
	function sendConnected() { doSend('{"Action":"connected"}'); }
	function sendResume()    { doSend('{"Action":"resume"}'); }
	function sendQuit()      { doSend('{"Action":"quit"}'); }
	function doSend(message) {
		// console.log("[hopwatch] send: " + message);
		websocket.send(message);
	}
	window.addEventListener("load", init, false);
	window.addEventListener("keydown", handleKeyDown, true); `)
	return
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"
	"os"
	"sort"
	"text/tabwriter"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/pkg/units"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
)

func init() {
	Register("ps", runPs, `
usage: flynn-host ps [-a|--all] [-q|--quiet]

List jobs`)
}

type sortJobs []host.ActiveJob

func (s sortJobs) Len() int           { return len(s) }
func (s sortJobs) Less(i, j int) bool { return s[i].StartedAt.Sub(s[j].StartedAt) < 0 }
func (s sortJobs) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func runPs(args *docopt.Args, client *cluster.Client) error {
	jobs, err := jobList(client, args.Bool["-a"] || args.Bool["--all"])
	if err != nil {
		return err
	}
	if args.Bool["-q"] || args.Bool["--quiet"] {
		for _, job := range jobs {
			fmt.Println(clusterJobID(job))
		}
		return nil
	}
	printJobs(jobs, os.Stdout)
	return nil
}

func jobList(client *cluster.Client, all bool) (sortJobs, error) {
	hosts, err := client.ListHosts()
	if err != nil {
		return nil, fmt.Errorf("could not list hosts: %s", err)
	}

	var jobs []host.ActiveJob
	for _, host := range hosts {
		h, err := client.DialHost(host.ID)
		if err != nil {
			return nil, fmt.Errorf("could not dial host %s: %s", host.ID, err)
		}
		hostJobs, err := h.ListJobs()
		if err != nil {
			return nil, fmt.Errorf("could not get jobs for host %s: %s", host.ID, err)
		}
		for _, job := range hostJobs {
			jobs = append(jobs, job)
		}
	}

	sorted := make(sortJobs, 0, len(jobs))
	for _, job := range jobs {
		if !all && job.Status != host.StatusStarting && job.Status != host.StatusRunning {
			continue
		}
		sorted = append(sorted, job)
	}
	sort.Sort(sort.Reverse(sorted))
	return sorted, nil
}

func printJobs(jobs sortJobs, out io.Writer) {
	w := tabwriter.NewWriter(out, 1, 2, 2, ' ', 0)
	defer w.Flush()
	listRec(w,
		"ID",
		"STATE",
		"STARTED",
		"CONTROLLER APP",
		"CONTROLLER TYPE",
	)

	for _, job := range jobs {
		var started string
		if !job.StartedAt.IsZero() {
			started = units.HumanDuration(time.Now().UTC().Sub(job.StartedAt)) + " ago"
		}

		listRec(w,
			clusterJobID(job),
			job.Status,
			started,
			job.Job.Metadata["flynn-controller.app_name"],
			job.Job.Metadata["flynn-controller.type"],
		)
	}
}

func clusterJobID(job host.ActiveJob) string {
	return job.HostID + "-" + job.Job.ID
}
<commit_msg>host/cli: Treat no hosts as an error in ps command<commit_after>package cli

import (
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"text/tabwriter"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/pkg/units"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
)

func init() {
	Register("ps", runPs, `
usage: flynn-host ps [-a|--all] [-q|--quiet]

List jobs`)
}

type sortJobs []host.ActiveJob

func (s sortJobs) Len() int           { return len(s) }
func (s sortJobs) Less(i, j int) bool { return s[i].StartedAt.Sub(s[j].StartedAt) < 0 }
func (s sortJobs) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func runPs(args *docopt.Args, client *cluster.Client) error {
	jobs, err := jobList(client, args.Bool["-a"] || args.Bool["--all"])
	if err != nil {
		return err
	}
	if args.Bool["-q"] || args.Bool["--quiet"] {
		for _, job := range jobs {
			fmt.Println(clusterJobID(job))
		}
		return nil
	}
	printJobs(jobs, os.Stdout)
	return nil
}

func jobList(client *cluster.Client, all bool) (sortJobs, error) {
	hosts, err := client.ListHosts()
	if err != nil {
		return nil, fmt.Errorf("could not list hosts: %s", err)
	}
	if len(hosts) == 0 {
		return nil, errors.New("no hosts found")
	}

	var jobs []host.ActiveJob
	for _, host := range hosts {
		h, err := client.DialHost(host.ID)
		if err != nil {
			return nil, fmt.Errorf("could not dial host %s: %s", host.ID, err)
		}
		hostJobs, err := h.ListJobs()
		if err != nil {
			return nil, fmt.Errorf("could not get jobs for host %s: %s", host.ID, err)
		}
		for _, job := range hostJobs {
			jobs = append(jobs, job)
		}
	}

	sorted := make(sortJobs, 0, len(jobs))
	for _, job := range jobs {
		if !all && job.Status != host.StatusStarting && job.Status != host.StatusRunning {
			continue
		}
		sorted = append(sorted, job)
	}
	sort.Sort(sort.Reverse(sorted))
	return sorted, nil
}

func printJobs(jobs sortJobs, out io.Writer) {
	w := tabwriter.NewWriter(out, 1, 2, 2, ' ', 0)
	defer w.Flush()
	listRec(w,
		"ID",
		"STATE",
		"STARTED",
		"CONTROLLER APP",
		"CONTROLLER TYPE",
	)

	for _, job := range jobs {
		var started string
		if !job.StartedAt.IsZero() {
			started = units.HumanDuration(time.Now().UTC().Sub(job.StartedAt)) + " ago"
		}

		listRec(w,
			clusterJobID(job),
			job.Status,
			started,
			job.Job.Metadata["flynn-controller.app_name"],
			job.Job.Metadata["flynn-controller.type"],
		)
	}
}

func clusterJobID(job host.ActiveJob) string {
	return job.HostID + "-" + job.Job.ID
}
<|endoftext|>


<commit_before>package cmd_test

import (
	"path/filepath"

	"github.com/cloudfoundry/cli/testhelpers/pluginbuilder"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestMain(t *testing.T) {
	RegisterFailHandler(Fail)

	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_1")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_2")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_push")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_push_short_name")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_help")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "my_say")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "call_core_cmd")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "input")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "panics")

	//compile plugin examples to ensure they're up to date
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "basic_plugin")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "echo")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "interactive")

	RunSpecs(t, "Cmd Suite")
}
<commit_msg>Increase timeout on cmd suite<commit_after>package cmd_test

import (
	"path/filepath"
	"time"

	"github.com/cloudfoundry/cli/testhelpers/pluginbuilder"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestMain(t *testing.T) {
	RegisterFailHandler(Fail)

	SetDefaultEventuallyTimeout(2 * time.Second)

	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_1")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_2")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_push")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_push_short_name")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "test_with_help")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "my_say")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "call_core_cmd")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "input")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "fixtures", "plugins"), "panics")

	//compile plugin examples to ensure they're up to date
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "basic_plugin")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "echo")
	pluginbuilder.BuildTestBinary(filepath.Join("..", "..", "plugin_examples"), "interactive")

	RunSpecs(t, "Cmd Suite")
}
<|endoftext|>


<commit_before>// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see <http://www.gnu.org/licenses/>.

package url

import (
	"errors"
	"github.com/nmeum/marvin/irc"
	"github.com/nmeum/marvin/modules"
	"html"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

const (
	urlRegex = `(http|https)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*`
)

type Module struct {
	Exclude []string `json:"exclude"`
}

func Init(moduleSet *modules.ModuleSet) {
	moduleSet.Register(new(Module))
}

func (m *Module) Name() string {
	return "url"
}

func (m *Module) Help() string {
	return "Displays HTML titles for HTTP links."
}

func (m *Module) Defaults() {
	return
}

func (m *Module) Load(client *irc.Client) error {
	regex := regexp.MustCompile(urlRegex)
	client.CmdHook("privmsg", func(c *irc.Client, msg irc.Message) error {
		link := regex.FindString(msg.Data)
		if len(link) <= 0 {
			return nil
		}

		uri, err := url.Parse(link)
		if err != nil {
			return err
		}

		if err == nil && !m.isExcluded(uri.Host) {
			title, err := m.extractTitle(link)
			if err == nil {
				c.Write("NOTICE %s :Page title: %s", msg.Receiver, title)
			}
		}

		return nil
	})

	return nil
}

func (m *Module) isExcluded(host string) bool {
	for _, h := range m.Exclude {
		if host == h {
			return true
		}
	}

	return false
}

func (m *Module) extractTitle(uri string) (title string, err error) {
	resp, err := http.Get(uri)
	if err != nil {
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	regex := regexp.MustCompile("(?is)<title>(.*)</title>")
	match := regex.FindStringSubmatch(string(body))

	if len(match) < 2 {
		err = errors.New("Couldn't extract title")
		return
	}

	title = html.UnescapeString(match[1])
	title = strings.TrimSpace(strings.Replace(title, "\n", "", -1))

	return
}
<commit_msg>url: Use FindSubmatch instead of FindStringSubmatch<commit_after>// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see <http://www.gnu.org/licenses/>.

package url

import (
	"errors"
	"github.com/nmeum/marvin/irc"
	"github.com/nmeum/marvin/modules"
	"html"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

const (
	urlRegex = `(http|https)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*`
)

type Module struct {
	Exclude []string `json:"exclude"`
}

func Init(moduleSet *modules.ModuleSet) {
	moduleSet.Register(new(Module))
}

func (m *Module) Name() string {
	return "url"
}

func (m *Module) Help() string {
	return "Displays HTML titles for HTTP links."
}

func (m *Module) Defaults() {
	return
}

func (m *Module) Load(client *irc.Client) error {
	regex := regexp.MustCompile(urlRegex)
	client.CmdHook("privmsg", func(c *irc.Client, msg irc.Message) error {
		link := regex.FindString(msg.Data)
		if len(link) <= 0 {
			return nil
		}

		uri, err := url.Parse(link)
		if err != nil {
			return err
		}

		if err == nil && !m.isExcluded(uri.Host) {
			title, err := m.extractTitle(link)
			if err == nil {
				c.Write("NOTICE %s :Page title: %s", msg.Receiver, title)
			}
		}

		return nil
	})

	return nil
}

func (m *Module) isExcluded(host string) bool {
	for _, h := range m.Exclude {
		if host == h {
			return true
		}
	}

	return false
}

func (m *Module) extractTitle(uri string) (title string, err error) {
	resp, err := http.Get(uri)
	if err != nil {
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	regex := regexp.MustCompile("(?is)<title>(.*)</title>")
	match := regex.FindSubmatch(body)

	if len(match) < 2 {
		err = errors.New("Couldn't extract title")
		return
	}

	title = html.UnescapeString(string(match[1]))
	title = strings.TrimSpace(strings.Replace(title, "\n", "", -1))

	return
}
<|endoftext|>


<commit_before>// Copyright ©2016 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package native

import (
	"github.com/gonum/blas"
	"github.com/gonum/lapack"
)

// Dorgql generates the m×n matrix Q with orthonormal columns defined as the
// last n columns of a product of k elementary reflectors of order m
//  Q = H_{k-1} * ... * H_1 * H_0.
//
// It must hold that
//  0 <= k <= n <= m,
// and Dorgql will panic otherwise.
//
// On entry, the (n-k+i)-th column of A must contain the vector which defines
// the elementary reflector H_i, for i=0,...,k-1, and tau[i] must contain its
// scalar factor. On return, a contains the m×n matrix Q.
//
// tau must have length at least k, and Dorgql will panic otherwise.
//
// work must have length at least max(1,lwork), and lwork must be at least
// max(1,n), otherwise Dorgql will panic. For optimum performance lwork must
// be a sufficiently large multiple of n.
//
// If lwork == -1, instead of computing Dorgql the optimal work length is stored
// into work[0].
//
// Dorgql is an internal routine. It is exported for testing purposes.
func (impl Implementation) Dorgql(m, n, k int, a []float64, lda int, tau, work []float64, lwork int) {
	switch {
	case n < 0:
		panic(nLT0)
	case m < n:
		panic(mLTN)
	case k < 0:
		panic(kLT0)
	case k > n:
		panic(kGTN)
	case lwork < max(1, n) && lwork != -1:
		panic(badWork)
	case len(work) < lwork:
		panic(shortWork)
	}
	if lwork != -1 {
		checkMatrix(m, n, a, lda)
		if len(tau) < k {
			panic(badTau)
		}
	}

	if n == 0 {
		work[0] = 1
		return
	}

	nb := impl.Ilaenv(1, "DORGQL", " ", m, n, k, -1)
	if lwork == -1 {
		work[0] = float64(n * nb)
		return
	}

	nbmin := 2
	var nx, ldwork int
	iws := n
	if nb > 1 && nb < k {
		// Determine when to cross over from blocked to unblocked code.
		nx = max(0, impl.Ilaenv(3, "DORGQL", " ", m, n, k, -1))
		if nx < k {
			// Determine if workspace is large enough for blocked code.
			ldwork = nb
			iws = n * nb
			if lwork < iws {
				// Not enough workspace to use optimal nb: reduce nb and determine
				// the minimum value of nb.
				nb = lwork / n
				nbmin = max(2, impl.Ilaenv(2, "DORGQL", " ", m, n, k, -1))
			}
		}
	}

	var kk int
	if nb >= nbmin && nb < k && nx < k {
		// Use blocked code after the first block. The last kk columns are handled
		// by the block method.
		kk = min(k, ((k-nx+nb-1)/nb)*nb)

		// Set A(m-kk:m, 0:n-kk) to zero.
		for i := m - kk; i < m; i++ {
			for j := 0; j < n-kk; j++ {
				a[i*lda+j] = 0
			}
		}
	}

	// Use unblocked code for the first or only block.
	impl.Dorg2l(m-kk, n-kk, k-kk, a, lda, tau, work)
	if kk > 0 {
		// Use blocked code.
		for i := k - kk; i < k; i += nb {
			ib := min(nb, k-i)
			if n-k+i > 0 {
				// Form the triangular factor of the block reflector
				// H = H_{i+ib-1} * ... * H_{i+1} * H_i.
				impl.Dlarft(lapack.Backward, lapack.ColumnWise, m-k+i+ib, ib,
					a[n-k+i:], lda, tau[i:], work, ldwork)

				// Apply H to A[0:m-k+i+ib, 0:n-k+i] from the left.
				impl.Dlarfb(blas.Left, blas.NoTrans, lapack.Backward, lapack.ColumnWise,
					m-k+i+ib, n-k+i, ib, a[n-k+i:], lda, work, ldwork,
					a, lda, work[ib*ldwork:], ldwork)
			}

			// Apply H to rows 0:m-k+i+ib of current block.
			impl.Dorg2l(m-k+i+ib, ib, ib, a[n-k+i:], lda, tau[i:], work)

			// Set rows m-k+i+ib:m of current block to zero.
			for j := n - k + i; j < n-k+i+ib; j++ {
				for l := m - k + i + ib; l < m; l++ {
					a[l*lda+j] = 0
				}
			}
		}
	}
	work[0] = float64(iws)
}
<commit_msg>native: fix bad ldwork in Dorgql<commit_after>// Copyright ©2016 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package native

import (
	"github.com/gonum/blas"
	"github.com/gonum/lapack"
)

// Dorgql generates the m×n matrix Q with orthonormal columns defined as the
// last n columns of a product of k elementary reflectors of order m
//  Q = H_{k-1} * ... * H_1 * H_0.
//
// It must hold that
//  0 <= k <= n <= m,
// and Dorgql will panic otherwise.
//
// On entry, the (n-k+i)-th column of A must contain the vector which defines
// the elementary reflector H_i, for i=0,...,k-1, and tau[i] must contain its
// scalar factor. On return, a contains the m×n matrix Q.
//
// tau must have length at least k, and Dorgql will panic otherwise.
//
// work must have length at least max(1,lwork), and lwork must be at least
// max(1,n), otherwise Dorgql will panic. For optimum performance lwork must
// be a sufficiently large multiple of n.
//
// If lwork == -1, instead of computing Dorgql the optimal work length is stored
// into work[0].
//
// Dorgql is an internal routine. It is exported for testing purposes.
func (impl Implementation) Dorgql(m, n, k int, a []float64, lda int, tau, work []float64, lwork int) {
	switch {
	case n < 0:
		panic(nLT0)
	case m < n:
		panic(mLTN)
	case k < 0:
		panic(kLT0)
	case k > n:
		panic(kGTN)
	case lwork < max(1, n) && lwork != -1:
		panic(badWork)
	case len(work) < lwork:
		panic(shortWork)
	}
	if lwork != -1 {
		checkMatrix(m, n, a, lda)
		if len(tau) < k {
			panic(badTau)
		}
	}

	if n == 0 {
		work[0] = 1
		return
	}

	nb := impl.Ilaenv(1, "DORGQL", " ", m, n, k, -1)
	if lwork == -1 {
		work[0] = float64(n * nb)
		return
	}

	nbmin := 2
	var nx, ldwork int
	iws := n
	if nb > 1 && nb < k {
		// Determine when to cross over from blocked to unblocked code.
		nx = max(0, impl.Ilaenv(3, "DORGQL", " ", m, n, k, -1))
		if nx < k {
			// Determine if workspace is large enough for blocked code.
			iws = n * nb
			if lwork < iws {
				// Not enough workspace to use optimal nb: reduce nb and determine
				// the minimum value of nb.
				nb = lwork / n
				nbmin = max(2, impl.Ilaenv(2, "DORGQL", " ", m, n, k, -1))
			}
			ldwork = nb
		}
	}

	var kk int
	if nb >= nbmin && nb < k && nx < k {
		// Use blocked code after the first block. The last kk columns are handled
		// by the block method.
		kk = min(k, ((k-nx+nb-1)/nb)*nb)

		// Set A(m-kk:m, 0:n-kk) to zero.
		for i := m - kk; i < m; i++ {
			for j := 0; j < n-kk; j++ {
				a[i*lda+j] = 0
			}
		}
	}

	// Use unblocked code for the first or only block.
	impl.Dorg2l(m-kk, n-kk, k-kk, a, lda, tau, work)
	if kk > 0 {
		// Use blocked code.
		for i := k - kk; i < k; i += nb {
			ib := min(nb, k-i)
			if n-k+i > 0 {
				// Form the triangular factor of the block reflector
				// H = H_{i+ib-1} * ... * H_{i+1} * H_i.
				impl.Dlarft(lapack.Backward, lapack.ColumnWise, m-k+i+ib, ib,
					a[n-k+i:], lda, tau[i:], work, ldwork)

				// Apply H to A[0:m-k+i+ib, 0:n-k+i] from the left.
				impl.Dlarfb(blas.Left, blas.NoTrans, lapack.Backward, lapack.ColumnWise,
					m-k+i+ib, n-k+i, ib, a[n-k+i:], lda, work, ldwork,
					a, lda, work[ib*ldwork:], ldwork)
			}

			// Apply H to rows 0:m-k+i+ib of current block.
			impl.Dorg2l(m-k+i+ib, ib, ib, a[n-k+i:], lda, tau[i:], work)

			// Set rows m-k+i+ib:m of current block to zero.
			for j := n - k + i; j < n-k+i+ib; j++ {
				for l := m - k + i + ib; l < m; l++ {
					a[l*lda+j] = 0
				}
			}
		}
	}
	work[0] = float64(iws)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var cmdExec = &Command{
	Exec:        runExec,
	UsageLine:   "exec [OPTIONS] SERVER COMMAND", // FIXME: add [ARGS...] support
	Description: "Run a command on a running server",
	Help:        "Run a command on a running server.",
}

func runExec(cmd *Command, args []string) {
	if len(args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: scw %s\n", cmd.UsageLine)
		os.Exit(1)
	}
	serverId := cmd.GetServer(args[0])
	command := args[1]
	server, err := cmd.API.GetServer(serverId)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to get server information for %s: %s\n", server, err)
	}
	execCmd := []string{"-l", "root", server.PublicAddress.IP, "-t", "--", command}
	log.Debugf("Executing: ssh %s", strings.Join(execCmd, " "))
	spawn := exec.Command("ssh", execCmd...)
	spawn.Stdout = os.Stdout
	spawn.Stdin = os.Stdin
	spawn.Stderr = os.Stderr
	err = spawn.Run()
	if err != nil {
		os.Exit(1)
	}
}
<commit_msg>Added handling of 'unsecure' exec<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var cmdExec = &Command{
	Exec:        runExec,
	UsageLine:   "exec [OPTIONS] SERVER COMMAND", // FIXME: add [ARGS...] support
	Description: "Run a command on a running server",
	Help:        "Run a command on a running server.",
}

func runExec(cmd *Command, args []string) {
	if len(args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: scw %s\n", cmd.UsageLine)
		os.Exit(1)
	}
	serverId := cmd.GetServer(args[0])
	command := args[1]
	server, err := cmd.API.GetServer(serverId)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to get server information for %s: %s\n", server, err)
	}
	execCmd := []string{}

	if os.Getenv("exec_secure") != "1" {
		execCmd = append(execCmd, "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no")
	}

	execCmd = append(execCmd, "-l", "root", server.PublicAddress.IP, "-t", "--", command)

	log.Debugf("Executing: ssh %s", strings.Join(execCmd, " "))
	spawn := exec.Command("ssh", execCmd...)
	spawn.Stdout = os.Stdout
	spawn.Stdin = os.Stdin
	spawn.Stderr = os.Stderr
	err = spawn.Run()
	if err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package editor

import (
	"github.com/gdamore/tcell"
)

type Painter interface {
	SetContent(x int, y int, ch rune, comb []rune, style tcell.Style)
	ShowCursor(x int, y int)
	Size() (int, int)
}

type Viewport struct {
	offx, offy    int
	width, height int
	view          Painter
	viewy         int
	viewx         int
}

func (v *Viewport) GetViewport() (lineStart, lineEnd int) {
	return v.viewy, v.viewy + v.height
}

func (v *Viewport) SetContent(x int, y int, ch rune, comb []rune, style tcell.Style) {
	v.view.SetContent(v.offx+x-v.viewx, v.offy+y-v.viewy, ch, comb, style)
}

func (v *Viewport) MakeVisibleY(y int) {
	if y >= v.viewy+v.height {
		v.viewy = y - (v.height - 1)
	}
	if y >= 0 && y < v.viewy {
		v.viewy = y
	}
	v.ValidateViewY()
}

func (v *Viewport) MakeVisibleX(x int) {
	if x >= v.viewx+v.width {
		v.viewx = x - (v.width - 1)
	}
	if x >= 0 && x < v.viewx {
		v.viewx = x
	}
	v.ValidateViewX()
}

func (v *Viewport) ShowCursor(x int, y int) {
	v.view.ShowCursor(v.offx+x, v.offy+y-v.viewy)
}

func (v *Viewport) ValidateViewY() {
	if v.viewy < 0 {
		v.viewy = 0
	}
}

func (v *Viewport) ValidateViewX() {
	if v.viewx < 0 {
		v.viewx = 0
	}
}

func (v *Viewport) ScrollUp(rows int) {
	v.viewy -= rows
	v.ValidateViewY()
}

func (v *Viewport) ScrollDown(rows int) {
	v.viewy += rows
	v.ValidateViewY()
}

func (v *Viewport) Size() (int, int) {
	return v.width, v.height
}

func (v *Viewport) FillParent() {
	width, height := v.view.Size()
	v.width = width
	v.height = height
}

func (v *Viewport) SetOffsetX(x int) {
	v.offx = x
}

func (v *Viewport) SetOffsetY(y int) {
	v.offy = y
}

func (v *Viewport) SetWidth(w int) {
	v.width = w
}

func (v *Viewport) SetHeight(h int) {
	v.height = h
}

func NewViewport(parent Painter, offx, offy int) *Viewport {
	width, height := parent.Size()
	return &Viewport{
		view:   parent,
		offx:   offx,
		offy:   offy,
		width:  width - offx,
		height: height - offy,
	}
}
<commit_msg>Scroll right kindof<commit_after>package editor

import (
	"github.com/gdamore/tcell"
)

type Painter interface {
	SetContent(x int, y int, ch rune, comb []rune, style tcell.Style)
	ShowCursor(x int, y int)
	Size() (int, int)
}

type Viewport struct {
	offx, offy    int
	width, height int
	view          Painter
	viewy, viewx  int
}

func (v *Viewport) GetViewport() (lineStart, lineEnd int) {
	return v.viewy, v.viewy + v.height
}

func (v *Viewport) SetContent(x int, y int, ch rune, comb []rune, style tcell.Style) {
	v.view.SetContent(v.offx+x-v.viewx, v.offy+y-v.viewy, ch, comb, style)
}

func (v *Viewport) MakeVisibleY(y int) {
	if y >= v.viewy+v.height {
		v.viewy = y - (v.height - 1)
	}
	if y >= 0 && y < v.viewy {
		v.viewy = y
	}
	v.ValidateViewY()
}

func (v *Viewport) MakeVisibleX(x int) {
	if x >= v.viewx+v.width {
		v.viewx = x - (v.width - 1)
	}
	if x >= 0 && x < v.viewx {
		v.viewx = x
	}
	v.ValidateViewX()
}

func (v *Viewport) ShowCursor(x int, y int) {
	v.view.ShowCursor(v.offx+x-v.viewx, v.offy+y-v.viewy)
}

func (v *Viewport) ValidateViewY() {
	if v.viewy < 0 {
		v.viewy = 0
	}
}

func (v *Viewport) ValidateViewX() {
	if v.viewx < 0 {
		v.viewx = 0
	}
}

func (v *Viewport) ScrollUp(rows int) {
	v.viewy -= rows
	v.ValidateViewY()
}

func (v *Viewport) ScrollDown(rows int) {
	v.viewy += rows
	v.ValidateViewY()
}

func (v *Viewport) Size() (int, int) {
	return v.width, v.height
}

func (v *Viewport) FillParent() {
	width, height := v.view.Size()
	v.width = width
	v.height = height
}

func (v *Viewport) SetOffsetX(x int) {
	v.offx = x
}

func (v *Viewport) SetOffsetY(y int) {
	v.offy = y
}

func (v *Viewport) SetWidth(w int) {
	v.width = w
}

func (v *Viewport) SetHeight(h int) {
	v.height = h
}

func NewViewport(parent Painter, offx, offy int) *Viewport {
	width, height := parent.Size()
	return &Viewport{
		view:   parent,
		offx:   offx,
		offy:   offy,
		width:  width - offx,
		height: height - offy,
	}
}
<|endoftext|>