<commit_before>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"database/sql"
	"testing"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/pingcap/check"
)

func TestT(t *testing.T) {
	TestingT(t)
}

var regression = true

var dsn = "root@tcp(localhost:4001)/test?strict=true"

func runTests(c *C, dsn string, tests ...func(dbt *DBTest)) {
	db, err := sql.Open("mysql", dsn)
	c.Assert(err, IsNil, Commentf("Error connecting"))
	defer db.Close()

	db.Exec("DROP TABLE IF EXISTS test")

	dbt := &DBTest{c, db}
	for _, test := range tests {
		test(dbt)
		dbt.db.Exec("DROP TABLE IF EXISTS test")
	}
}

type DBTest struct {
	*C
	db *sql.DB
}

func (dbt *DBTest) fail(method, query string, err error) {
	if len(query) > 300 {
		query = "[query too large to print]"
	}
	dbt.Fatalf("Error on %s %s: %s", method, query, err.Error())
}

func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) {
	res, err := dbt.db.Exec(query, args...)
	dbt.Assert(err, IsNil, Commentf("Exec %s", query))
	return res
}

func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) {
	rows, err := dbt.db.Query(query, args...)
	dbt.Assert(err, IsNil, Commentf("Query %s", query))
	return rows
}

func (dbt *DBTest) mustQueryRows(query string, args ...interface{}) {
	rows := dbt.mustQuery(query, args...)
	dbt.Assert(rows.Next(), IsTrue)
	rows.Close()
}

func runTestRegression(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		// Create Table
		dbt.mustExec("CREATE TABLE test (val TINYINT)")

		// Test for unexpected data
		var out bool
		rows := dbt.mustQuery("SELECT * FROM test")
		dbt.Assert(rows.Next(), IsFalse, Commentf("unexpected data in empty table"))

		// Create Data
		res := dbt.mustExec("INSERT INTO test VALUES (1)")
		//		res := dbt.mustExec("INSERT INTO test VALUES (?)", 1)
		count, err := res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))
		id, err := res.LastInsertId()
		dbt.Assert(err, IsNil)
		dbt.Check(id, Equals, int64(0))

		// Read
		rows = dbt.mustQuery("SELECT val FROM test")
		if rows.Next() {
			rows.Scan(&out)
			dbt.Check(out, IsTrue)
			dbt.Check(rows.Next(), IsFalse, Commentf("unexpected data"))
		} else {
			dbt.Error("no data")
		}
		rows.Close()

		// Update
		res = dbt.mustExec("UPDATE test SET val = 0 WHERE val = ?", 1)
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))

		// Check Update
		rows = dbt.mustQuery("SELECT val FROM test")
		if rows.Next() {
			rows.Scan(&out)
			dbt.Check(out, IsFalse)
			dbt.Check(rows.Next(), IsFalse, Commentf("unexpected data"))
		} else {
			dbt.Error("no data")
		}
		rows.Close()

		// Delete
		res = dbt.mustExec("DELETE FROM test WHERE val = 0")
		//		res = dbt.mustExec("DELETE FROM test WHERE val = ?", 0)
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))

		// Check for unexpected rows
		res = dbt.mustExec("DELETE FROM test")
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(0))

		dbt.mustQueryRows("SELECT 1")

		b := []byte{}
		if err := dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil {
			dbt.Fatal(err)
		}
		if b == nil {
			dbt.Error("nil echo from non-nil input")
		}
	})
}

func runTestPrepareResultFieldType(t *C) {
	var param int64 = 83
	runTests(t, dsn, func(dbt *DBTest) {
		stmt, err := dbt.db.Prepare(`SELECT ?`)
		if err != nil {
			dbt.Fatal(err)
		}
		defer stmt.Close()
		row := stmt.QueryRow(param)
		var result int64
		err = row.Scan(&result)
		if err != nil {
			dbt.Fatal(err)
		}
		switch {
		case result != param:
			dbt.Fatal("Unexpected result value")
		}
	})
}

func runTestSpecialType(t *C) {
	runTests(t, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a decimal(10, 5), b datetime, c time)")
		dbt.mustExec("insert test values (1.4, '2012-12-21 12:12:12', '4:23:34')")
		rows := dbt.mustQuery("select * from test where a > ?", 0)
		t.Assert(rows.Next(), IsTrue)
		var outA float64
		var outB, outC string
		err := rows.Scan(&outA, &outB, &outC)
		t.Assert(err, IsNil)
		t.Assert(outA, Equals, 1.4)
		t.Assert(outB, Equals, "2012-12-21 12:12:12")
		t.Assert(outC, Equals, "04:23:34")
	})
}

func runTestPreparedString(t *C) {
	runTests(t, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a char(10), b char(10))")
		dbt.mustExec("insert test values (?, ?)", "abcdeabcde", "abcde")
		rows := dbt.mustQuery("select * from test where 1 = ?", 1)
		t.Assert(rows.Next(), IsTrue)
		var outA, outB string
		err := rows.Scan(&outA, &outB)
		t.Assert(err, IsNil)
		t.Assert(outA, Equals, "abcdeabcde")
		t.Assert(outB, Equals, "abcde")
	})
}

func runTestConcurrentUpdate(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a int, b int)")
		dbt.mustExec("insert test values (1, 1)")
		txn1, err := dbt.db.Begin()
		c.Assert(err, IsNil)

		txn2, err := dbt.db.Begin()
		c.Assert(err, IsNil)

		_, err = txn2.Exec("update test set a = a + 1 where b = 1")
		c.Assert(err, IsNil)
		err = txn2.Commit()
		c.Assert(err, IsNil)

		_, err = txn1.Exec("update test set a = a + 1 where b = 1")
		c.Assert(err, IsNil)

		err = txn1.Commit()
		c.Assert(err, IsNil)
	})
}

func runTestAuth(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		dbt.mustExec(`CREATE USER 'test'@'127.0.0.1' IDENTIFIED BY '123';`)
		dbt.mustExec(`CREATE USER 'test'@'localhost' IDENTIFIED BY '123';`)
		dbt.mustExec(`CREATE USER 'test'@'::1' IDENTIFIED BY '123';`)
	})
	newDsn := "test:123@tcp(localhost:4001)/test?strict=true"
	runTests(c, newDsn, func(dbt *DBTest) {
		dbt.mustExec(`USE mysql;`)
	})

	db, err := sql.Open("mysql", "test:456@tcp(localhost:4001)/test?strict=true")
	_, err = db.Query("USE mysql;")
	c.Assert(err, NotNil, Commentf("Wrong password should be failed"))
	db.Close()
}

func runTestIssues(c *C) {
	// For issue #263
	unExistsSchemaDsn := "root@tcp(localhost:4001)/unexists_schema?strict=true"
	db, err := sql.Open("mysql", unExistsSchemaDsn)
	c.Assert(db, NotNil)
	c.Assert(err, IsNil)
	// Open may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call Ping.
	err = db.Ping()
	c.Assert(err, NotNil, Commentf("Connecting to an unexists schema should be error"))
	db.Close()
}
<commit_msg>*: Fix auth fail when use fe80::1%lo0<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"database/sql"
	"testing"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/pingcap/check"
)

func TestT(t *testing.T) {
	TestingT(t)
}

var regression = true

var dsn = "root@tcp(localhost:4001)/test?strict=true"

func runTests(c *C, dsn string, tests ...func(dbt *DBTest)) {
	db, err := sql.Open("mysql", dsn)
	c.Assert(err, IsNil, Commentf("Error connecting"))
	defer db.Close()

	db.Exec("DROP TABLE IF EXISTS test")

	dbt := &DBTest{c, db}
	for _, test := range tests {
		test(dbt)
		dbt.db.Exec("DROP TABLE IF EXISTS test")
	}
}

type DBTest struct {
	*C
	db *sql.DB
}

func (dbt *DBTest) fail(method, query string, err error) {
	if len(query) > 300 {
		query = "[query too large to print]"
	}
	dbt.Fatalf("Error on %s %s: %s", method, query, err.Error())
}

func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) {
	res, err := dbt.db.Exec(query, args...)
	dbt.Assert(err, IsNil, Commentf("Exec %s", query))
	return res
}

func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) {
	rows, err := dbt.db.Query(query, args...)
	dbt.Assert(err, IsNil, Commentf("Query %s", query))
	return rows
}

func (dbt *DBTest) mustQueryRows(query string, args ...interface{}) {
	rows := dbt.mustQuery(query, args...)
	dbt.Assert(rows.Next(), IsTrue)
	rows.Close()
}

func runTestRegression(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		// Create Table
		dbt.mustExec("CREATE TABLE test (val TINYINT)")

		// Test for unexpected data
		var out bool
		rows := dbt.mustQuery("SELECT * FROM test")
		dbt.Assert(rows.Next(), IsFalse, Commentf("unexpected data in empty table"))

		// Create Data
		res := dbt.mustExec("INSERT INTO test VALUES (1)")
		//		res := dbt.mustExec("INSERT INTO test VALUES (?)", 1)
		count, err := res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))
		id, err := res.LastInsertId()
		dbt.Assert(err, IsNil)
		dbt.Check(id, Equals, int64(0))

		// Read
		rows = dbt.mustQuery("SELECT val FROM test")
		if rows.Next() {
			rows.Scan(&out)
			dbt.Check(out, IsTrue)
			dbt.Check(rows.Next(), IsFalse, Commentf("unexpected data"))
		} else {
			dbt.Error("no data")
		}
		rows.Close()

		// Update
		res = dbt.mustExec("UPDATE test SET val = 0 WHERE val = ?", 1)
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))

		// Check Update
		rows = dbt.mustQuery("SELECT val FROM test")
		if rows.Next() {
			rows.Scan(&out)
			dbt.Check(out, IsFalse)
			dbt.Check(rows.Next(), IsFalse, Commentf("unexpected data"))
		} else {
			dbt.Error("no data")
		}
		rows.Close()

		// Delete
		res = dbt.mustExec("DELETE FROM test WHERE val = 0")
		//		res = dbt.mustExec("DELETE FROM test WHERE val = ?", 0)
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(1))

		// Check for unexpected rows
		res = dbt.mustExec("DELETE FROM test")
		count, err = res.RowsAffected()
		dbt.Assert(err, IsNil)
		dbt.Check(count, Equals, int64(0))

		dbt.mustQueryRows("SELECT 1")

		b := []byte{}
		if err := dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil {
			dbt.Fatal(err)
		}
		if b == nil {
			dbt.Error("nil echo from non-nil input")
		}
	})
}

func runTestPrepareResultFieldType(t *C) {
	var param int64 = 83
	runTests(t, dsn, func(dbt *DBTest) {
		stmt, err := dbt.db.Prepare(`SELECT ?`)
		if err != nil {
			dbt.Fatal(err)
		}
		defer stmt.Close()
		row := stmt.QueryRow(param)
		var result int64
		err = row.Scan(&result)
		if err != nil {
			dbt.Fatal(err)
		}
		switch {
		case result != param:
			dbt.Fatal("Unexpected result value")
		}
	})
}

func runTestSpecialType(t *C) {
	runTests(t, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a decimal(10, 5), b datetime, c time)")
		dbt.mustExec("insert test values (1.4, '2012-12-21 12:12:12', '4:23:34')")
		rows := dbt.mustQuery("select * from test where a > ?", 0)
		t.Assert(rows.Next(), IsTrue)
		var outA float64
		var outB, outC string
		err := rows.Scan(&outA, &outB, &outC)
		t.Assert(err, IsNil)
		t.Assert(outA, Equals, 1.4)
		t.Assert(outB, Equals, "2012-12-21 12:12:12")
		t.Assert(outC, Equals, "04:23:34")
	})
}

func runTestPreparedString(t *C) {
	runTests(t, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a char(10), b char(10))")
		dbt.mustExec("insert test values (?, ?)", "abcdeabcde", "abcde")
		rows := dbt.mustQuery("select * from test where 1 = ?", 1)
		t.Assert(rows.Next(), IsTrue)
		var outA, outB string
		err := rows.Scan(&outA, &outB)
		t.Assert(err, IsNil)
		t.Assert(outA, Equals, "abcdeabcde")
		t.Assert(outB, Equals, "abcde")
	})
}

func runTestConcurrentUpdate(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		dbt.mustExec("create table test (a int, b int)")
		dbt.mustExec("insert test values (1, 1)")
		txn1, err := dbt.db.Begin()
		c.Assert(err, IsNil)

		txn2, err := dbt.db.Begin()
		c.Assert(err, IsNil)

		_, err = txn2.Exec("update test set a = a + 1 where b = 1")
		c.Assert(err, IsNil)
		err = txn2.Commit()
		c.Assert(err, IsNil)

		_, err = txn1.Exec("update test set a = a + 1 where b = 1")
		c.Assert(err, IsNil)

		err = txn1.Commit()
		c.Assert(err, IsNil)
	})
}

func runTestAuth(c *C) {
	runTests(c, dsn, func(dbt *DBTest) {
		dbt.mustExec(`CREATE USER 'test'@'127.0.0.1' IDENTIFIED BY '123';`)
		dbt.mustExec(`CREATE USER 'test'@'localhost' IDENTIFIED BY '123';`)
		dbt.mustExec(`CREATE USER 'test'@'::1' IDENTIFIED BY '123';`)
		dbt.mustExec(`CREATE USER 'test'@'fe80::1%lo0' IDENTIFIED BY '123';`)
	})
	newDsn := "test:123@tcp(localhost:4001)/test?strict=true"
	runTests(c, newDsn, func(dbt *DBTest) {
		dbt.mustExec(`USE mysql;`)
	})

	db, err := sql.Open("mysql", "test:456@tcp(localhost:4001)/test?strict=true")
	_, err = db.Query("USE mysql;")
	c.Assert(err, NotNil, Commentf("Wrong password should be failed"))
	db.Close()
}

func runTestIssues(c *C) {
	// For issue #263
	unExistsSchemaDsn := "root@tcp(localhost:4001)/unexists_schema?strict=true"
	db, err := sql.Open("mysql", unExistsSchemaDsn)
	c.Assert(db, NotNil)
	c.Assert(err, IsNil)
	// Open may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call Ping.
	err = db.Ping()
	c.Assert(err, NotNil, Commentf("Connecting to an unexists schema should be error"))
	db.Close()
}
<|endoftext|>


<commit_before>package api

import (
	"os/exec"
	"time"
	"net/http"
	"path/filepath"
	"github.com/gorilla/mux"
	"github.com/golang/glog"
	"github.com/espebra/filebin/app/config"
	"github.com/espebra/filebin/app/model"
	"github.com/espebra/filebin/app/output"
)

func triggerNewTagHandler(c string, tag string) error {
	glog.Info("Executing trigger-new-tag: " + c)
	cmd := exec.Command(c, tag)
	err := cmdHandler(cmd)
	return err
}

func triggerUploadedFileHandler(c string, tag string, filename string) error {
	glog.Info("Executing trigger-uploaded-file: " + c)
	cmd := exec.Command(c, tag, filename)
	err := cmdHandler(cmd)
	return err
}

func triggerExpiredTagHandler(c string, tag string) error {
	glog.Info("Executing trigger-expired-tag: " + c)
	cmd := exec.Command(c, tag)
	err := cmdHandler(cmd)
	return err
}

func cmdHandler(cmd *exec.Cmd) error {
	err := cmd.Start()
	if err != nil {
		glog.Error("Trigger command failed: ", err)
	}
	return err
}

func Upload(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	f := model.ExtendedFile { }

	// Extract the tag from the request
	if (r.Header.Get("tag") == "") {
		err = f.GenerateTagID()
	} else {
		err = f.SetTagID(r.Header.Get("tag"))
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest);
		glog.Info(err)
		return
	}
	f.SetTagDir(cfg.Filedir)

	// Write the request body to a temporary file
	err = f.WriteTempfile(r.Body, cfg.Tempdir)
	if err != nil {
		glog.Error("Unable to write tempfile: ", err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError);
		return
	}

	// Calculate and verify the checksum
	err = f.VerifySHA256(r.Header.Get("content-sha256"))
	if err != nil {
		http.Error(w, "Checksum did not match", http.StatusConflict);
		return
	}

	// Create the tag directory if it does not exist
	err = f.EnsureTagDirectoryExists()
	if err != nil {
		glog.Error("Unable to create tag directory: ", f.TagDir)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError);
		return
	}

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	// Extract the filename from the request
	if (r.Header.Get("filename") == "") {
		glog.Info("Using the checksum " + f.Checksum + " as the " +
			"filename")
		f.SetFilename(f.Checksum)
	} else {
		err = f.SetFilename(r.Header.Get("filename"))
		if err != nil {
			glog.Info(err)
			http.Error(w, "Invalid filename specified. It contains illegal characters or is too short.",
				http.StatusBadRequest);
			return
		}
	}

	// Promote file from tempdir to the published tagdir
	f.Publish()

	// Clean up by removing the tempfile
	f.ClearTemp()

	err = f.DetectMIME()
	if err != nil {
		glog.Error("Unable to detect MIME: ", err)
	}

	err = f.Info()
	if err != nil {
		http.Error(w,"Internal Server Error", 500)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	f.RemoteAddr = r.RemoteAddr
	f.UserAgent = r.Header.Get("User-Agent")
	f.CreatedAt = time.Now().UTC()
	//f.ExpiresAt = time.Now().UTC().Add(24 * 7 * 4 * time.Hour)

	if cfg.TriggerUploadedFile != "" {
		triggerUploadedFileHandler(cfg.TriggerUploadedFile, f.TagID, f.Filename)
	}

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 201
	output.JSONresponse(w, status, headers, f)
}

func FetchFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	f := model.File {}
	f.SetFilename(params["filename"])
	if err != nil {
		http.Error(w,"Invalid filename specified. It contains illegal characters or is too short.", 400)
		return
	}
	err = f.SetTagID(params["tag"])
	if err != nil {
		http.Error(w,"Invalid tag specified. It contains illegal characters or is too short.", 400)
		return
	}
	f.SetTagDir(cfg.Filedir)

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	path := filepath.Join(f.TagDir, f.Filename)

	w.Header().Set("Cache-Control", "max-age=15")
	http.ServeFile(w, r, path)
}

func DeleteFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	f := model.File {}
	f.SetFilename(params["filename"])
	if err != nil {
		http.Error(w,"Invalid filename specified. It contains illegal characters or is too short.", 400)
		return
	}
	err = f.SetTagID(params["tag"])
	if err != nil {
		http.Error(w,"Invalid tag specified. It contains illegal characters or is too short.", 400)
		return
	}
	f.SetTagDir(cfg.Filedir)

	if f.Exists() == false {
		http.Error(w,"File Not Found", 404)
		return
	}

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	err = f.DetectMIME()
	if err != nil {
		glog.Error("Unable to detect MIME: ", err)
	}

	err = f.Info()
	if err != nil {
		http.Error(w,"Internal Server Error", 500)
		return
	}

	err = f.Remove()
 	if err != nil {
		glog.Error("Unable to delete file: ", err)
		http.Error(w,"Internal Server Error", 500)
		return
	}

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 200
	output.JSONresponse(w, status, headers, f)
	return
}

func FetchTag(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	t := model.ExtendedTag {}
	err = t.SetTagID(params["tag"])
	if err != nil {
		http.Error(w, "Invalid tag", 400)
		return
	}

	t.SetTagDir(cfg.Filedir)
	if t.Exists() == false {
		http.Error(w, "Tag not found", 404)
		return
	}

	t.CalculateExpiration(cfg.Expiration)
	expired, err := t.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	err = t.Info()
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		return
	}

	err = t.List(cfg.Baseurl)
	if err != nil {
		http.Error(w,"Some error.", 404)
		return
	}

	//t.GenerateLinks(cfg.Baseurl)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Cache-Control"] = "max-age=15"

	var status = 200
	output.JSONresponse(w, status, headers, t)
}

func ViewIndex(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	http.Error(w, "", 200)
	return
}
<commit_msg>Accept only file uploads that are >0 bytes<commit_after>package api

import (
	"os/exec"
	"time"
	"net/http"
	"path/filepath"
	"github.com/gorilla/mux"
	"github.com/golang/glog"
	"github.com/espebra/filebin/app/config"
	"github.com/espebra/filebin/app/model"
	"github.com/espebra/filebin/app/output"
)

func triggerNewTagHandler(c string, tag string) error {
	glog.Info("Executing trigger-new-tag: " + c)
	cmd := exec.Command(c, tag)
	err := cmdHandler(cmd)
	return err
}

func triggerUploadedFileHandler(c string, tag string, filename string) error {
	glog.Info("Executing trigger-uploaded-file: " + c)
	cmd := exec.Command(c, tag, filename)
	err := cmdHandler(cmd)
	return err
}

func triggerExpiredTagHandler(c string, tag string) error {
	glog.Info("Executing trigger-expired-tag: " + c)
	cmd := exec.Command(c, tag)
	err := cmdHandler(cmd)
	return err
}

func cmdHandler(cmd *exec.Cmd) error {
	err := cmd.Start()
	if err != nil {
		glog.Error("Trigger command failed: ", err)
	}
	return err
}

func Upload(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	f := model.ExtendedFile { }

	// Extract the tag from the request
	if (r.Header.Get("tag") == "") {
		err = f.GenerateTagID()
	} else {
		err = f.SetTagID(r.Header.Get("tag"))
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest);
		glog.Info(err)
		return
	}
	f.SetTagDir(cfg.Filedir)

	// Write the request body to a temporary file
	err = f.WriteTempfile(r.Body, cfg.Tempdir)
	if err != nil {
		glog.Error("Unable to write tempfile: ", err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError);
		return
	}

	// Do not accept files that are 0 bytes
	if f.Bytes == 0 {
		// Clean up by removing the tempfile
		f.ClearTemp()

		http.Error(w, "No content. The file size must be more than " +
			"0 bytes.", http.StatusBadRequest);
		return
	}

	// Calculate and verify the checksum
	err = f.VerifySHA256(r.Header.Get("content-sha256"))
	if err != nil {
		http.Error(w, "Checksum did not match", http.StatusConflict);
		return
	}

	// Create the tag directory if it does not exist
	err = f.EnsureTagDirectoryExists()
	if err != nil {
		glog.Error("Unable to create tag directory: ", f.TagDir)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError);
		return
	}

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	// Extract the filename from the request
	if (r.Header.Get("filename") == "") {
		glog.Info("Using the checksum " + f.Checksum + " as the " +
			"filename")
		f.SetFilename(f.Checksum)
	} else {
		err = f.SetFilename(r.Header.Get("filename"))
		if err != nil {
			glog.Info(err)
			http.Error(w, "Invalid filename specified. It contains illegal characters or is too short.",
				http.StatusBadRequest);
			return
		}
	}

	// Promote file from tempdir to the published tagdir
	f.Publish()

	// Clean up by removing the tempfile
	f.ClearTemp()

	err = f.DetectMIME()
	if err != nil {
		glog.Error("Unable to detect MIME: ", err)
	}

	err = f.Info()
	if err != nil {
		http.Error(w,"Internal Server Error", 500)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	f.RemoteAddr = r.RemoteAddr
	f.UserAgent = r.Header.Get("User-Agent")
	f.CreatedAt = time.Now().UTC()
	//f.ExpiresAt = time.Now().UTC().Add(24 * 7 * 4 * time.Hour)

	if cfg.TriggerUploadedFile != "" {
		triggerUploadedFileHandler(cfg.TriggerUploadedFile, f.TagID, f.Filename)
	}

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 201
	output.JSONresponse(w, status, headers, f)
}

func FetchFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	f := model.File {}
	f.SetFilename(params["filename"])
	if err != nil {
		http.Error(w,"Invalid filename specified. It contains illegal characters or is too short.", 400)
		return
	}
	err = f.SetTagID(params["tag"])
	if err != nil {
		http.Error(w,"Invalid tag specified. It contains illegal characters or is too short.", 400)
		return
	}
	f.SetTagDir(cfg.Filedir)

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	path := filepath.Join(f.TagDir, f.Filename)

	w.Header().Set("Cache-Control", "max-age=15")
	http.ServeFile(w, r, path)
}

func DeleteFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	f := model.File {}
	f.SetFilename(params["filename"])
	if err != nil {
		http.Error(w,"Invalid filename specified. It contains illegal characters or is too short.", 400)
		return
	}
	err = f.SetTagID(params["tag"])
	if err != nil {
		http.Error(w,"Invalid tag specified. It contains illegal characters or is too short.", 400)
		return
	}
	f.SetTagDir(cfg.Filedir)

	if f.Exists() == false {
		http.Error(w,"File Not Found", 404)
		return
	}

	f.CalculateExpiration(cfg.Expiration)
	expired, err := f.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	err = f.DetectMIME()
	if err != nil {
		glog.Error("Unable to detect MIME: ", err)
	}

	err = f.Info()
	if err != nil {
		http.Error(w,"Internal Server Error", 500)
		return
	}

	err = f.Remove()
 	if err != nil {
		glog.Error("Unable to delete file: ", err)
		http.Error(w,"Internal Server Error", 500)
		return
	}

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 200
	output.JSONresponse(w, status, headers, f)
	return
}

func FetchTag(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	var err error
	params := mux.Vars(r)
	t := model.ExtendedTag {}
	err = t.SetTagID(params["tag"])
	if err != nil {
		http.Error(w, "Invalid tag", 400)
		return
	}

	t.SetTagDir(cfg.Filedir)
	if t.Exists() == false {
		http.Error(w, "Tag not found", 404)
		return
	}

	t.CalculateExpiration(cfg.Expiration)
	expired, err := t.IsExpired(cfg.Expiration)
	if err != nil {
		http.Error(w,"Internal server error", 500)
		return
	}
	if expired {
		http.Error(w,"This tag has expired.", 410)
		return
	}

	err = t.Info()
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		return
	}

	err = t.List(cfg.Baseurl)
	if err != nil {
		http.Error(w,"Some error.", 404)
		return
	}

	//t.GenerateLinks(cfg.Baseurl)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Cache-Control"] = "max-age=15"

	var status = 200
	output.JSONresponse(w, status, headers, t)
}

func ViewIndex(w http.ResponseWriter, r *http.Request, cfg config.Configuration) {
	http.Error(w, "", 200)
	return
}
<|endoftext|>


<commit_before>// Package chalk lets you colour your terminal string
// styles. There are three things you can do so far:
//
//
// Change the string's colour
//
// There are eight colours: black, red, green, yellow,
// blue, magenta, cyan and white. They are extremely easy
// to use:
//
//   fmt.Println(chalk.Blue("This is blue text!"))
//
//
// Change the string's background colour
//
// There are the same eight background colours. They can
// be used by doing:
//
//   fmt.Println(chalk.BgBlue("The background of this text is blue."))
//
//
// Underline
//
// You can easily underline some text by doing:
//
//   fmt.Println(chalk.Underline("Here is some underlined text."))
//
// That's it! It's pretty simple, but I hope to add more
// more styles and options in the near future.
//
package chalk

type colourable interface {
	Black(s string) string
	Red(s string) string
	Green(s string) string
	Yellow(s string) string
	Blue(s string) string
	Magenta(s string) string
	Cyan(s string) string
	White(s string) string
}

// Black colours your string black
func Black(s string) string {
	return "\033[30m" + s + "\033[0m"
}

// Red colours your string red
func Red(s string) string {
	return "\033[31m" + s + "\033[0m"
}

// Green colours your string green
func Green(s string) string {
	return "\033[32m" + s + "\033[0m"
}

// Yellow colours your string yellow
func Yellow(s string) string {
	return "\033[33m" + s + "\033[0m"
}

// Blue colours your string blue
func Blue(s string) string {
	return "\033[34m" + s + "\033[0m"
}

// Magenta colours your string magenta
func Magenta(s string) string {
	return "\033[35m" + s + "\033[0m"
}

// Cyan colours your string cyan
func Cyan(s string) string {
	return "\033[36m" + s + "\033[0m"
}

// White colours your string white
func White(s string) string {
	return "\033[37m" + s + "\033[0m"
}

// Underline places an underline under your string
func Underline(s string) string {
	return "\u001b[4m" + s + "\u001b[24m"
}

// BgBlack colours the background of your string black
func BgBlack(s string) string {
	return "\u001b[40m" + s + "\u001b[49m"
}

// BgRed colours the background of your string red
func BgRed(s string) string {
	return "\u001b[41m" + s + "\u001b[49m"
}

// BgGreen colours the background of your strinYellowreen
func BgGreen(s string) string {
	return "\u001b[42m" + s + "\u001b[49m"
}

// BgYellow colours the background of your string yellow
func BgYellow(s string) string {
	return "\u001b[43m" + s + "\u001b[49m"
}

// BgBlue colours the background of your string blue
func BgBlue(s string) string {
	return "\u001b[44m" + s + "\u001b[49m"
}

// BgMagenta colours the background of your string magenta
func BgMagenta(s string) string {
	return "\u001b[45m" + s + "\u001b[49m"
}

// BgCyan colours the background of your string cyan
func BgCyan(s string) string {
	return "\u001b[46m" + s + "\u001b[49m"
}

// BgWhite colours the background of your string white
func BgWhite(s string) string {
	return "\u001b[47m" + s + "\u001b[49m"
}
<commit_msg>Attempt idea<commit_after>// Package chalk lets you colour your terminal string
// styles. There are three things you can do so far:
//
//
// Change the string's colour
//
// There are eight colours: black, red, green, yellow,
// blue, magenta, cyan and white. They are extremely easy
// to use:
//
//   fmt.Println(chalk.Blue("This is blue text!"))
//
//
// Change the string's background colour
//
// There are the same eight background colours. They can
// be used by doing:
//
//   fmt.Println(chalk.BgBlue("The background of this text is blue."))
//
//
// Underline
//
// You can easily underline some text by doing:
//
//   fmt.Println(chalk.Underline("Here is some underlined text."))
//
// That's it! It's pretty simple, but I hope to add more
// more styles and options in the near future.
//
package chalk

type fg string
type bg string
type ul string

func (f *fg) BgRed(s string) string {
	return BgRed(Red(s))
}

// Black colours your string black
func Black(s string) string {
	return "\033[30m" + s + "\033[0m"
}

// Red colours your string red
func Red(s string) string {
	return "\033[31m" + s + "\033[0m"
}

// Green colours your string green
func Green(s string) string {
	return "\033[32m" + s + "\033[0m"
}

// Yellow colours your string yellow
func Yellow(s string) string {
	return "\033[33m" + s + "\033[0m"
}

// Blue colours your string blue
func Blue(s string) string {
	return "\033[34m" + s + "\033[0m"
}

// Magenta colours your string magenta
func Magenta(s string) string {
	return "\033[35m" + s + "\033[0m"
}

// Cyan colours your string cyan
func Cyan(s string) string {
	return "\033[36m" + s + "\033[0m"
}

// White colours your string white
func White(s string) string {
	return "\033[37m" + s + "\033[0m"
}

// Underline places an underline under your string
func Underline(s string) string {
	return "\u001b[4m" + s + "\u001b[24m"
}

// BgBlack colours the background of your string black
func BgBlack(s string) string {
	return "\u001b[40m" + s + "\u001b[49m"
}

// BgRed colours the background of your string red
func BgRed(s string) string {
	return "\u001b[41m" + s + "\u001b[49m"
}

// BgGreen colours the background of your strinYellowreen
func BgGreen(s string) string {
	return "\u001b[42m" + s + "\u001b[49m"
}

// BgYellow colours the background of your string yellow
func BgYellow(s string) string {
	return "\u001b[43m" + s + "\u001b[49m"
}

// BgBlue colours the background of your string blue
func BgBlue(s string) string {
	return "\u001b[44m" + s + "\u001b[49m"
}

// BgMagenta colours the background of your string magenta
func BgMagenta(s string) string {
	return "\u001b[45m" + s + "\u001b[49m"
}

// BgCyan colours the background of your string cyan
func BgCyan(s string) string {
	return "\u001b[46m" + s + "\u001b[49m"
}

// BgWhite colours the background of your string white
func BgWhite(s string) string {
	return "\u001b[47m" + s + "\u001b[49m"
}
<|endoftext|>


<commit_before>package derpiapi

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
)

type Image struct {
	Imgid    int
	Url      string
	Filename string
	Score    int
	//	Hashval     string
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.Url = "http:" + dat["image"].(string)
	//	imgdata.Hashval = dat["sha512_hash"].(string)
	imgdata.Filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.Imgid = int(dat["id_number"].(float64))
	imgdata.Score = int(dat["score"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	//	fmt.Println(imgdata.Url)
	//	fmt.Println(imgdata.Hashval)
	//	fmt.Println(imgdata.Filename)

	imgchan <- imgdata
}

func ParseImg(imgchan chan<- Image, imgid string, KEY string, elog *log.Logger) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if KEY != "" {
		source = source + "&key=" + KEY
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool, elog *log.Logger, IMGDIR string) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if !more { //checking that there is an image in channel
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly
		}

		if imgdata.Filename == "" {
			elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, not a cause to die, going to the next image
		} else {

			log.Println("Saving as", imgdata.Filename)

			func() { // To not hold all the files open when there is no need. All pointers to files are in the scope of this function.

				output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.Filename) //And now, THE FILE!
				if err != err {
					elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.Imgid))
					elog.Println(err) //Either we got no permisson or no space, end of line
					return
				}
				defer output.Close() //Not forgetting to deal with it after completing download

				response, err := http.Get(imgdata.Url)
				if err != nil {
					elog.Println("Error when getting image", imgdata.Imgid)
					elog.Println(err)
					return
				}
				defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

				io.Copy(output, response.Body) //	Writing things we got from Derpibooru into the file and into hasher

			}()
		}

		//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

	}
}

func ParseTag(imgchan chan<- Image, tag string, KEY string, STARTPAGE int, STOPPAGE int, elog *log.Logger) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if KEY != "" {
		source = source + "&key=" + KEY
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working = true
	i := STARTPAGE
	for working {
		func() { //I suspect that all those returns could be dealt with in some way. But lazy.
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			err = json.Unmarshal(body, &dats) //transforming json into native slice of maps

			if err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over") //Does not mean that process is over.
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}
<commit_msg>Bugfix to download svg and debugging print<commit_after>package derpiapi

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

type Image struct {
	Imgid    int
	Url      string
	Filename string
	Score    int
	//	Hashval     string
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.Url = "http:" + dat["image"].(string)
	//	imgdata.Hashval = dat["sha512_hash"].(string)
	if (dat["original_format"].(string) == "svg") {
		imgdata.Url = "https://derpicdn.net/img/download/" + strings.Join(strings.Split("/",dat["image"].(string)[6:8]),  "/") + "/" + strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + ".svg"
	}
	imgdata.Filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.Imgid = int(dat["id_number"].(float64))
	imgdata.Score = int(dat["score"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	fmt.Println(imgdata.Url)
	//	fmt.Println(imgdata.Hashval)
	//	fmt.Println(imgdata.Filename)

	imgchan <- imgdata
}

func ParseImg(imgchan chan<- Image, imgid string, KEY string, elog *log.Logger) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if KEY != "" {
		source = source + "&key=" + KEY
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool, elog *log.Logger, IMGDIR string) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if !more { //checking that there is an image in channel
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly
		}

		if imgdata.Filename == "" {
			elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, not a cause to die, going to the next image
		} else {

			log.Println("Saving as", imgdata.Filename)

			func() { // To not hold all the files open when there is no need. All pointers to files are in the scope of this function.

				output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.Filename) //And now, THE FILE!
				if err != err {
					elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.Imgid))
					elog.Println(err) //Either we got no permisson or no space, end of line
					return
				}
				defer output.Close() //Not forgetting to deal with it after completing download

				response, err := http.Get(imgdata.Url)
				if err != nil {
					elog.Println("Error when getting image", imgdata.Imgid)
					elog.Println(err)
					return
				}
				defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

				io.Copy(output, response.Body) //	Writing things we got from Derpibooru into the file and into hasher

			}()
		}

		//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

	}
}

func ParseTag(imgchan chan<- Image, tag string, KEY string, STARTPAGE int, STOPPAGE int, elog *log.Logger) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if KEY != "" {
		source = source + "&key=" + KEY
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working = true
	i := STARTPAGE
	for working {
		func() { //I suspect that all those returns could be dealt with in some way. But lazy.
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			err = json.Unmarshal(body, &dats) //transforming json into native slice of maps

			if err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over") //Does not mean that process is over.
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}
<|endoftext|>


<commit_before>package dotquotedetect

import (
	"github.com/reiver/go-whitespace"

	"unicode/utf8"
)

var (
	errNotKeyLenLessThanTwo = newNotKeyComplainer("length less that 2")
)

// DetectKey detect for a dotquote key in the dotquote data in a []byte, and returns
// the beginning index and the ending index.
func DetectKey(b []byte) (int, int, error) {
	if nil == b {
		return -1, -1, errNilBytes
	}

	lenb := len(b)

	if 0 >= lenb {
		return -1, -1, errEmptyRequest
	}

	if 2 > lenb {
		return -1, -1, errNotKeyLenLessThanTwo
	}

	{
		r0, _ := utf8.DecodeRune(b)
		if utf8.RuneError == r0 {
			return -1, -1, errNotUTF8
		}

		if '"' != r0 {
			return -1, -1, newNotKeyComplainer("first rune not a double quote (i.e,. \"): %q %d", string(r0), r0)
		}
	}

	p := b

	begin, end, err := DetectQuote(p)
	if nil != err {
		return -1, -1, newNotKeyComplainer("could not detect quoted part of key: %s", err)
	}
	p = p[end:]

	for {
		if 0 >= len(p) {
			return -1, -1, errEmptyRequest
		}

		r0, size := utf8.DecodeRune(p)
		if utf8.RuneError == r0 {
			return -1, -1, errNotUTF8
		}
		if '=' == r0 {
			break
		}
		if whitespace.IsWhitespace(r0) {
			break
		}

		if '.' != r0 {
			return -1, -1, newNotKeyComplainer("not a dot at index %d of |||||%s|||||: %q (%d)", end, string(b), string(r0), r0)
		}

		p = p[size:]
		end += size

		_, finish, err := DetectQuote(p)
		if nil != err {
			return -1, -1, newNotKeyComplainer("could not detect quoted part of key: %s", err)
		}

		end += finish

                p = p[finish:]
	}

	return begin, end, nil
}
<commit_msg>added some docs<commit_after>package dotquotedetect

import (
	"github.com/reiver/go-whitespace"

	"unicode/utf8"
)

var (
	errNotKeyLenLessThanTwo = newNotKeyComplainer("length less that 2")
)

// DetectKey looks for a dotquote key in the dotquote data in a []byte, and returns
// the beginning index and the ending index.
func DetectKey(b []byte) (int, int, error) {
	if nil == b {
		return -1, -1, errNilBytes
	}

	lenb := len(b)

	if 0 >= lenb {
		return -1, -1, errEmptyRequest
	}

	if 2 > lenb {
		return -1, -1, errNotKeyLenLessThanTwo
	}

	{
		r0, _ := utf8.DecodeRune(b)
		if utf8.RuneError == r0 {
			return -1, -1, errNotUTF8
		}

		if '"' != r0 {
			return -1, -1, newNotKeyComplainer("first rune not a double quote (i.e,. \"): %q %d", string(r0), r0)
		}
	}

	p := b

	begin, end, err := DetectQuote(p)
	if nil != err {
		return -1, -1, newNotKeyComplainer("could not detect quoted part of key: %s", err)
	}
	p = p[end:]

	for {
		if 0 >= len(p) {
			return -1, -1, errEmptyRequest
		}

		r0, size := utf8.DecodeRune(p)
		if utf8.RuneError == r0 {
			return -1, -1, errNotUTF8
		}
		if '=' == r0 {
			break
		}
		if whitespace.IsWhitespace(r0) {
			break
		}

		if '.' != r0 {
			return -1, -1, newNotKeyComplainer("not a dot at index %d of |||||%s|||||: %q (%d)", end, string(b), string(r0), r0)
		}

		p = p[size:]
		end += size

		_, finish, err := DetectQuote(p)
		if nil != err {
			return -1, -1, newNotKeyComplainer("could not detect quoted part of key: %s", err)
		}

		end += finish

                p = p[finish:]
	}

	return begin, end, nil
}
<|endoftext|>


<commit_before>package scamp

type ActionOptions struct {
	Verify bool
	Privs  []int
	// Location of the ticket_verify_public_key.pem
	TicketVerifyPublicKey string
}

func DefaultActionOptions() ActionOptions {
	return ActionOptions{
		// TODO: This should probably be defaulted to true later, but we need to fix
		// the testrunner to send tickets on all SOA actions then.
		Verify:                false,
		Privs:                 []int{},
		TicketVerifyPublicKey: "",
	}
}

type ServiceOptionsFunc struct {
	callback ServiceActionFunc
	options  ActionOptions
}

func (function ServiceOptionsFunc) Call(message *Message, client *Client) {
	if function.options.Verify || len(function.options.Privs) > 0 {
		ticket, err := VerifyTicket(message.Ticket, function.options.TicketVerifyPublicKey)
		if err != nil {
			ReplyOnError(message, client, "verification", err)
			return
		}

		err = ticket.CheckPrivs(function.options.Privs)
		if err != nil {
			ReplyOnError(message, client, "verification", err)
			return
		}
	}

	function.callback.Call(message, client)
}

func ReplyOnError(message *Message, client *Client, errorCode string, err error) {
	if client == nil {
		Info.Println("did not reply to client, missing client")
		return
	}

	if message == nil {
		Info.Println("did not reply to client, missing message")
		return
	}

	if err == nil {
		Info.Println("did not reply to client, missing err")
		return
	}

	respMsg := NewResponseMessage()
	respMsg.SetRequestID(message.RequestID)
	respMsg.SetErrorCode(errorCode)
	respMsg.SetError(err.Error())

	_, clientErr := client.Send(respMsg)
	if clientErr != nil {
		Error.Printf("(messageID: %v, messageAction: %v) send error: %v\n", message.RequestID, message.Action, clientErr)
	}
}
<commit_msg>added godoc comments for exported types and methods<commit_after>package scamp

// ActionOptions struct that configuration options related to ticket verification
// which are passed to Service.Register() function
type ActionOptions struct {
	Verify bool
	Privs  []int
	// Location of the ticket_verify_public_key.pem
	TicketVerifyPublicKey string
}

// DefaultActionOptions initializes and returns an ActionOptions struct with default nil values
func DefaultActionOptions() ActionOptions {
	return ActionOptions{
		// TODO: This should probably be defaulted to true later, but we need to fix
		// the testrunner to send tickets on all SOA actions then.
		Verify:                false,
		Privs:                 []int{},
		TicketVerifyPublicKey: "",
	}
}

// ServiceOptionsFunc struct contains the callback and action options for registered service actions
type ServiceOptionsFunc struct {
	callback ServiceActionFunc
	options  ActionOptions
}

// Call calls a registered service action and verifies scamp auth ticket and associated privs
// if the options are not nil
func (function ServiceOptionsFunc) Call(message *Message, client *Client) {
	if function.options.Verify || len(function.options.Privs) > 0 {
		ticket, err := VerifyTicket(message.Ticket, function.options.TicketVerifyPublicKey)
		if err != nil {
			ReplyOnError(message, client, "verification", err)
			return
		}

		err = ticket.CheckPrivs(function.options.Privs)
		if err != nil {
			ReplyOnError(message, client, "verification", err)
			return
		}
	}

	function.callback.Call(message, client)
}

// ReplyOnError simplifies responding to scamp requests with an error state
func ReplyOnError(message *Message, client *Client, errorCode string, err error) {
	if client == nil {
		Info.Println("did not reply to client, missing client")
		return
	}

	if message == nil {
		Info.Println("did not reply to client, missing message")
		return
	}

	if err == nil {
		Info.Println("did not reply to client, missing err")
		return
	}

	respMsg := NewResponseMessage()
	respMsg.SetRequestID(message.RequestID)
	respMsg.SetErrorCode(errorCode)
	respMsg.SetError(err.Error())

	_, clientErr := client.Send(respMsg)
	if clientErr != nil {
		Error.Printf("(messageID: %v, messageAction: %v) send error: %v\n", message.RequestID, message.Action, clientErr)
	}
}
<|endoftext|>


<commit_before>package air

// Group is a set of sub-routes for a specified route. It can be used for inner
// routes that share a common gas or functionality that should be separate
// from the parent `Air` instance while still inheriting from it.
type Group struct {
	prefix string
	gases  []GasFunc
	air    *Air
}

// NewGroup returns a new router group with prefix and optional group-level gases.
func NewGroup(prefix string, a *Air, gases ...GasFunc) *Group {
	g := &Group{prefix: prefix, air: a}
	g.Contain(gases...)
	return g
}

// NewSubGroup creates a new sub-group with prefix and optional sub-group-level gases.
func (g *Group) NewSubGroup(prefix string, gases ...GasFunc) *Group {
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	return NewGroup(g.prefix+prefix, g.air, gs...)
}

// Contain implements `Air#Contain()`.
func (g *Group) Contain(gases ...GasFunc) {
	g.gases = append(g.gases, gases...)
	// Allow all requests to reach the group as they might get dropped if router
	// doesn't find a match, making none of the group gas process.
	for _, m := range methods {
		g.air.add(m, g.prefix+"*", func(c *Context) error {
			return ErrNotFound
		}, g.gases...)
	}
}

// Get implements `Air#Get()`.
func (g *Group) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(GET, path, handler, gases...)
}

// Post implements `Air#Post()`.
func (g *Group) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(POST, path, handler, gases...)
}

// Put implements `Air#Put()`.
func (g *Group) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(PUT, path, handler, gases...)
}

// Delete implements `Air#Delete()`.
func (g *Group) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(DELETE, path, handler, gases...)
}

// Static implements `Air#Static()`.
func (g *Group) Static(prefix, root string) {
	g.air.Static(g.prefix+prefix, root)
}

// File implements `Air#File()`.
func (g *Group) File(path, file string) {
	g.air.File(g.prefix+path, file)
}

// add implements `Air#add()`.
func (g *Group) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	// Combine into a new slice, to avoid accidentally passing the same
	// slice for multiple routes, which would lead to later add() calls overwriting
	// the gas from earlier calls
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	g.air.add(method, g.prefix+path, handler, gs...)
}
<commit_msg>fix: group gas not applied to Static or File routes<commit_after>package air

import "path"

// Group is a set of sub-routes for a specified route. It can be used for inner
// routes that share a common gas or functionality that should be separate
// from the parent `Air` instance while still inheriting from it.
type Group struct {
	prefix string
	gases  []GasFunc
	air    *Air
}

// NewGroup returns a new router group with prefix and optional group-level gases.
func NewGroup(prefix string, a *Air, gases ...GasFunc) *Group {
	g := &Group{prefix: prefix, air: a}
	g.Contain(gases...)
	return g
}

// NewSubGroup creates a new sub-group with prefix and optional sub-group-level gases.
func (g *Group) NewSubGroup(prefix string, gases ...GasFunc) *Group {
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	return NewGroup(g.prefix+prefix, g.air, gs...)
}

// Contain implements `Air#Contain()`.
func (g *Group) Contain(gases ...GasFunc) {
	g.gases = append(g.gases, gases...)
	// Allow all requests to reach the group as they might get dropped if router
	// doesn't find a match, making none of the group gas process.
	for _, m := range methods {
		g.air.add(m, g.prefix+"*", func(c *Context) error {
			return ErrNotFound
		}, g.gases...)
	}
}

// Get implements `Air#Get()`.
func (g *Group) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(GET, path, handler, gases...)
}

// Post implements `Air#Post()`.
func (g *Group) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(POST, path, handler, gases...)
}

// Put implements `Air#Put()`.
func (g *Group) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(PUT, path, handler, gases...)
}

// Delete implements `Air#Delete()`.
func (g *Group) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(DELETE, path, handler, gases...)
}

// Static implements `Air#Static()`.
func (g *Group) Static(prefix, root string) {
	g.Get(g.prefix+prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Params[c.ParamNames[0]]))
	})
}

// File implements `Air#File()`.
func (g *Group) File(path, file string) {
	g.Get(g.prefix+path, func(c *Context) error {
		return c.File(file)
	})
}

// add implements `Air#add()`.
func (g *Group) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	// Combine into a new slice to avoid accidentally passing the same slice for
	// multiple routes, which would lead to later add() calls overwriting the
	// gas from earlier calls.
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	g.air.add(method, g.prefix+path, handler, gs...)
}
<|endoftext|>


<commit_before>package main

import (
	"bitmm/bitfinex"
	"fmt"
	"github.com/grd/stat"
	"log"
	"math"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"
)

// Trade inputs
const (
	SYMBOL    = "ltcusd" // Instrument to trade
	MINCHANGE = 0.0025   // Minumum change required to update prices
	TRADENUM  = 25       // Number of trades to use in calculations
	MINO      = 0.5      // Min order size
)

var (
	api        = bitfinex.New(os.Getenv("BITFINEX_KEY"), os.Getenv("BITFINEX_SECRET"))
	apiErrors  = false // Set to true on any error
	liveOrders = false // Set to true on any order
	orderTheo  = 0.0   // Theo value on which the live orders are based
	orderPos   = 0.0   // Position on which the live orders are based
	// Fed in as OS args:
	maxPos      float64 // Maximum Position size
	minEdge     float64 // Minimum edge for position entry
	stdMult     float64 // Multiplier for standard deviation
	exitPercent float64 // Percent of edge for position exit
)

func main() {
	if len(os.Args) < 5 {
		fmt.Printf("usage: %s <size> <minimum edge> <stdev multiplier> <exit percent edge>\n",
			filepath.Base(os.Args[0]))
		os.Exit(1)
	}

	fmt.Println("\nInitializing...")

	// Get maxPos, minEdge, exitPercent from user input
	getArgs()

	// Check for input to break loop
	inputChan := make(chan rune)
	go checkStdin(inputChan)

	// Run loop until user input is received
	runMainLoop(inputChan)
}

func getArgs() {
	var err error
	if maxPos, err = strconv.ParseFloat(os.Args[1], 64); err != nil {
		log.Fatal(err)
	}
	if minEdge, err = strconv.ParseFloat(os.Args[2], 64); err != nil {
		log.Fatal(err)
	}
	if stdMult, err = strconv.ParseFloat(os.Args[3], 64); err != nil {
		log.Fatal(err)
	}
	if exitPercent, err = strconv.ParseFloat(os.Args[4], 64); err != nil {
		log.Fatal(err)
	}
}

// Check for any user input
func checkStdin(inputChan chan<- rune) {
	var ch rune
	fmt.Scanf("%c", &ch)
	inputChan <- ch
}

// Infinite loop
func runMainLoop(inputChan <-chan rune) {
	positionChan := make(chan float64)

	var (
		trades    bitfinex.Trades
		orders    bitfinex.Orders
		start     time.Time
		position  float64
		theo      float64
		stdev     float64
		lastTrade int
	)

	for {
		// Record time for each iteration
		start = time.Now()

		// Cancel orders and exit if anything entered by user
		select {
		case <-inputChan:
			exit()
			return
		default: // Continue if nothing on chan
		}

		trades = getTrades()
		if !apiErrors && trades[0].TID != lastTrade { // If new trades
			go checkPosition(positionChan)
			// Do calcs on trade data while waiting for position data
			theo = calculateTheo(trades)
			stdev = calculateStdev(trades)
			position = <-positionChan
			if (math.Abs(theo-orderTheo) >= MINCHANGE || math.Abs(position-
				orderPos) >= MINO || !liveOrders) && !apiErrors {
				orders = sendOrders(theo, position, stdev)
			}
		}

		if !apiErrors {
			printResults(orders, position, stdev, theo, start)
			// Reset for next iteration
			lastTrade = trades[0].TID
		}

		// Reset for next iteration
		apiErrors = false
	}
}

// Send orders to the exchange
func sendOrders(theo, position, stdev float64) bitfinex.Orders {
	if liveOrders {
		cancelAll()
	}

	// Send new order request to the exchange
	params := calculateOrderParams(position, theo, stdev)
	orders, err := api.MultipleNewOrders(params)
	checkErr(err)
	if err == nil {
		liveOrders = true
		orderTheo = theo
		orderPos = position
	}
	return orders
}

func calculateOrderParams(position, theo, stdev float64) []bitfinex.OrderParams {
	var params []bitfinex.OrderParams

	if math.Abs(position) < MINO { // No position
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, maxPos, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	} else if position < (-1*maxPos)+MINO { // Max short postion
		params = []bitfinex.OrderParams{
			{SYMBOL, -1 * position, theo - stdev*exitPercent, "bitfinex", "buy", "limit"},
		}
	} else if position > maxPos-MINO { // Max long postion
		params = []bitfinex.OrderParams{
			{SYMBOL, position, theo + stdev*exitPercent, "bitfinex", "sell", "limit"},
		}
	} else if (-1*maxPos)+MINO <= position && position <= -1*MINO { // Partial short
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, -1 * position, theo - stdev*exitPercent, "bitfinex", "buy", "limit"},
			{SYMBOL, maxPos + position, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	} else if MINO <= position && position <= maxPos-MINO { // Partial long
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos - position, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, position, theo + stdev*exitPercent, "bitfinex", "sell", "limit"},
			{SYMBOL, maxPos, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	}

	return params
}

func checkPosition(positionChan chan<- float64) {
	var position float64
	posSlice, err := api.ActivePositions()
	checkErr(err)
	for _, pos := range posSlice {
		if pos.Symbol == SYMBOL {
			position = pos.Amount
		}
	}

	positionChan <- position
}

// Get trade data
func getTrades() bitfinex.Trades {
	trades, err := api.Trades(SYMBOL, TRADENUM)
	checkErr(err)

	return trades
}

// Calculate a volume and time weighted average of traded prices
func calculateTheo(trades bitfinex.Trades) float64 {
	weightDuration := 60 // number of seconds back for a 50% weight relative to most recent
	mostRecent := trades[0].Timestamp
	var weight, timeDivisor, sum, weightTotal float64

	for _, trade := range trades {
		timeDivisor = float64(mostRecent - trade.Timestamp + weightDuration)
		weight = trade.Amount / timeDivisor
		sum += trade.Price * weight
		weightTotal += weight
	}

	return sum / weightTotal
}

func calculateStdev(trades bitfinex.Trades) float64 {
	x := make(stat.Float64Slice, len(trades)-1)
	for i := 1; i < len(trades); i++ {
		x[i-1] = trades[i-1].Price - trades[i].Price
	}
	return stdMult * stat.Sd(x)
}

// Called on any error
func checkErr(err error) {
	if err != nil {
		cancelAll()
		log.Println(err)
		apiErrors = true
	}
}

// Call on exit
func exit() {
	cancelAll()
	fmt.Println("\nCancelled all orders.")
}

// Cancel all orders
func cancelAll() {
	cancelled := false
	for !cancelled {
		cancelled, _ = api.CancelAll()
	}
	liveOrders = false
}

// Print results
func printResults(orders bitfinex.Orders, position, stdev, theo float64, start time.Time) {

	clearScreen()

	fmt.Printf("\nPosition: %.2f\n", position)
	fmt.Printf("Stdev:    %.4f\n", stdev)
	fmt.Printf("Theo:     %.4f\n", theo)

	fmt.Println("\nActive orders:")
	for _, order := range orders.Orders {
		fmt.Printf("%7.2f %s @ %6.4f\n", order.Amount, SYMBOL, order.Price)
	}

	fmt.Printf("\n%v processing time...", time.Since(start))
}

// Clear the terminal between prints
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}
<commit_msg>redo minEdge on exit<commit_after>package main

import (
	"bitmm/bitfinex"
	"fmt"
	"github.com/grd/stat"
	"log"
	"math"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"
)

// Trade inputs
const (
	SYMBOL    = "ltcusd" // Instrument to trade
	MINCHANGE = 0.0025   // Minumum change required to update prices
	TRADENUM  = 25       // Number of trades to use in calculations
	MINO      = 0.5      // Min order size
)

var (
	api        = bitfinex.New(os.Getenv("BITFINEX_KEY"), os.Getenv("BITFINEX_SECRET"))
	apiErrors  = false // Set to true on any error
	liveOrders = false // Set to true on any order
	orderTheo  = 0.0   // Theo value on which the live orders are based
	orderPos   = 0.0   // Position on which the live orders are based
	// Fed in as OS args:
	maxPos      float64 // Maximum Position size
	minEdge     float64 // Minimum edge for position entry
	stdMult     float64 // Multiplier for standard deviation
	exitPercent float64 // Percent of edge for position exit
)

func main() {
	if len(os.Args) < 5 {
		fmt.Printf("usage: %s <size> <minimum edge> <stdev multiplier> <exit percent edge>\n",
			filepath.Base(os.Args[0]))
		os.Exit(1)
	}

	fmt.Println("\nInitializing...")

	// Get maxPos, minEdge, exitPercent from user input
	getArgs()

	// Check for input to break loop
	inputChan := make(chan rune)
	go checkStdin(inputChan)

	// Run loop until user input is received
	runMainLoop(inputChan)
}

func getArgs() {
	var err error
	if maxPos, err = strconv.ParseFloat(os.Args[1], 64); err != nil {
		log.Fatal(err)
	}
	if minEdge, err = strconv.ParseFloat(os.Args[2], 64); err != nil {
		log.Fatal(err)
	}
	if stdMult, err = strconv.ParseFloat(os.Args[3], 64); err != nil {
		log.Fatal(err)
	}
	if exitPercent, err = strconv.ParseFloat(os.Args[4], 64); err != nil {
		log.Fatal(err)
	}
}

// Check for any user input
func checkStdin(inputChan chan<- rune) {
	var ch rune
	fmt.Scanf("%c", &ch)
	inputChan <- ch
}

// Infinite loop
func runMainLoop(inputChan <-chan rune) {
	positionChan := make(chan float64)

	var (
		trades    bitfinex.Trades
		orders    bitfinex.Orders
		start     time.Time
		position  float64
		theo      float64
		stdev     float64
		lastTrade int
	)

	for {
		// Record time for each iteration
		start = time.Now()

		// Cancel orders and exit if anything entered by user
		select {
		case <-inputChan:
			exit()
			return
		default: // Continue if nothing on chan
		}

		trades = getTrades()
		if !apiErrors && trades[0].TID != lastTrade { // If new trades
			go checkPosition(positionChan)
			// Do calcs on trade data while waiting for position data
			theo = calculateTheo(trades)
			stdev = calculateStdev(trades)
			position = <-positionChan
			if (math.Abs(theo-orderTheo) >= MINCHANGE || math.Abs(position-
				orderPos) >= MINO || !liveOrders) && !apiErrors {
				orders = sendOrders(theo, position, stdev)
			}
		}

		if !apiErrors {
			printResults(orders, position, stdev, theo, start)
			// Reset for next iteration
			lastTrade = trades[0].TID
		}

		// Reset for next iteration
		apiErrors = false
	}
}

// Send orders to the exchange
func sendOrders(theo, position, stdev float64) bitfinex.Orders {
	if liveOrders {
		cancelAll()
	}

	// Send new order request to the exchange
	params := calculateOrderParams(position, theo, stdev)
	orders, err := api.MultipleNewOrders(params)
	checkErr(err)
	if err == nil {
		liveOrders = true
		orderTheo = theo
		orderPos = position
	}
	return orders
}

func calculateOrderParams(position, theo, stdev float64) []bitfinex.OrderParams {
	var params []bitfinex.OrderParams

	if math.Abs(position) < MINO { // No position
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, maxPos, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	} else if position < (-1*maxPos)+MINO { // Max short postion
		params = []bitfinex.OrderParams{
			{SYMBOL, -1 * position, theo - math.Max(stdev, minEdge)*exitPercent, "bitfinex", "buy", "limit"},
		}
	} else if position > maxPos-MINO { // Max long postion
		params = []bitfinex.OrderParams{
			{SYMBOL, position, theo + math.Max(stdev, minEdge)*exitPercent, "bitfinex", "sell", "limit"},
		}
	} else if (-1*maxPos)+MINO <= position && position <= -1*MINO { // Partial short
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, -1 * position, theo - math.Max(stdev, minEdge)*exitPercent, "bitfinex", "buy", "limit"},
			{SYMBOL, maxPos + position, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	} else if MINO <= position && position <= maxPos-MINO { // Partial long
		params = []bitfinex.OrderParams{
			{SYMBOL, maxPos - position, theo - math.Max(stdev, minEdge), "bitfinex", "buy", "limit"},
			{SYMBOL, position, theo + math.Max(stdev, minEdge)*exitPercent, "bitfinex", "sell", "limit"},
			{SYMBOL, maxPos, theo + math.Max(stdev, minEdge), "bitfinex", "sell", "limit"},
		}
	}

	return params
}

func checkPosition(positionChan chan<- float64) {
	var position float64
	posSlice, err := api.ActivePositions()
	checkErr(err)
	for _, pos := range posSlice {
		if pos.Symbol == SYMBOL {
			position = pos.Amount
		}
	}

	positionChan <- position
}

// Get trade data
func getTrades() bitfinex.Trades {
	trades, err := api.Trades(SYMBOL, TRADENUM)
	checkErr(err)

	return trades
}

// Calculate a volume and time weighted average of traded prices
func calculateTheo(trades bitfinex.Trades) float64 {
	weightDuration := 60 // number of seconds back for a 50% weight relative to most recent
	mostRecent := trades[0].Timestamp
	var weight, timeDivisor, sum, weightTotal float64

	for _, trade := range trades {
		timeDivisor = float64(mostRecent - trade.Timestamp + weightDuration)
		weight = trade.Amount / timeDivisor
		sum += trade.Price * weight
		weightTotal += weight
	}

	return sum / weightTotal
}

func calculateStdev(trades bitfinex.Trades) float64 {
	x := make(stat.Float64Slice, len(trades)-1)
	for i := 1; i < len(trades); i++ {
		x[i-1] = trades[i-1].Price - trades[i].Price
	}
	return stdMult * stat.Sd(x)
}

// Called on any error
func checkErr(err error) {
	if err != nil {
		cancelAll()
		log.Println(err)
		apiErrors = true
	}
}

// Call on exit
func exit() {
	cancelAll()
	fmt.Println("\nCancelled all orders.")
}

// Cancel all orders
func cancelAll() {
	cancelled := false
	for !cancelled {
		cancelled, _ = api.CancelAll()
	}
	liveOrders = false
}

// Print results
func printResults(orders bitfinex.Orders, position, stdev, theo float64, start time.Time) {

	clearScreen()

	fmt.Printf("\nPosition: %.2f\n", position)
	fmt.Printf("Stdev:    %.4f\n", stdev)
	fmt.Printf("Theo:     %.4f\n", theo)

	fmt.Println("\nActive orders:")
	for _, order := range orders.Orders {
		fmt.Printf("%7.2f %s @ %6.4f\n", order.Amount, SYMBOL, order.Price)
	}

	fmt.Printf("\n%v processing time...", time.Since(start))
}

// Clear the terminal between prints
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

type SiteSpec struct {
	Addr   string `json:"addr"`
	Stime  string `json:"service-time,omitempty"`
	Qdelay string `json:"queuing-delay,omitempty"`
}

func (self *SiteSpec) Name() string {
	return self.Addr
}

func sleepAndDelay(duration string) {
	if len(duration) > 0 {
		d, e := time.ParseDuration(duration)
		if e == nil {
			time.Sleep(d)
		}
	}
}

func (self *SiteSpec) String() string {
	return fmt.Sprintf(" addr: %v\n service-time: %v\n queuing-delay: %v\n", self.Addr, self.Stime, self.Qdelay)
}

func (self *SiteSpec) DelayBySleeping() {
	sleepAndDelay(self.Stime)
	sleepAndDelay(self.Qdelay)
}

type PathSpec struct {
	Sites  []*SiteSpec `json:"sites"`
	PathId string      `json:"id"`
}

func (self *PathSpec) forwardFromFirst() (resp []byte, err error) {
	if len(self.Sites) == 0 {
		resp = []byte(fmt.Sprintf("finished path %v\n", self.PathId))
		err = nil
		return
	}
	addr := self.Sites[0].Addr
	jbytes, err := json.Marshal(self)
	if err != nil {
		return
	}
	bodyReader := bytes.NewBuffer(jbytes)
	res, err := http.Post(addr, "text/json", bodyReader)
	if err != nil {
		return
	}
	defer res.Body.Close()

	resp, err = ioutil.ReadAll(res.Body)
	return
}

func (self *PathSpec) String() string {
	buf := new(bytes.Buffer)
	buf.WriteString(fmt.Sprintf("id: %v\n", self.PathId))

	for _, site := range self.Sites {
		buf.WriteString(fmt.Sprintf("%v\n", site))
	}
	return buf.String()
}

func (self *PathSpec) Delay() error {
	site := self.Sites[0]
	site.DelayBySleeping()
	return nil
}

func (self *PathSpec) Forward() ([]byte, error) {
	path := new(PathSpec)
	path.Sites = self.Sites[1:]
	path.PathId = self.PathId
	return path.forwardFromFirst()
}
<commit_msg>we don't need to specify the queuing delay.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

type Distribution struct {
	Name       string             `json:"type"`
	Parameters map[string]float64 `json:"parameters,omitempty"`
}

type SiteSpec struct {
	Addr  string        `json:"addr"`
	Stime string        `json:"service-time,omitempty"`
	Sdist *Distribution `json:"service-time-distribution,omitempty"`
}

func (self *SiteSpec) Name() string {
	return self.Addr
}

func sleepAndDelay(duration string) {
	if len(duration) > 0 {
		d, e := time.ParseDuration(duration)
		if e == nil {
			time.Sleep(d)
		}
	}
}

func (self *SiteSpec) DelayBySleeping() {
	sleepAndDelay(self.Stime)
}

type PathSpec struct {
	Sites  []*SiteSpec `json:"sites"`
	PathId string      `json:"id"`
}

func (self *PathSpec) forwardFromFirst() (resp []byte, err error) {
	if len(self.Sites) == 0 {
		resp = []byte(fmt.Sprintf("finished path %v\n", self.PathId))
		err = nil
		return
	}
	addr := self.Sites[0].Addr
	jbytes, err := json.Marshal(self)
	if err != nil {
		return
	}
	bodyReader := bytes.NewBuffer(jbytes)
	res, err := http.Post(addr, "text/json", bodyReader)
	if err != nil {
		return
	}
	defer res.Body.Close()

	resp, err = ioutil.ReadAll(res.Body)
	return
}

func (self *PathSpec) String() string {
	buf := new(bytes.Buffer)
	buf.WriteString(fmt.Sprintf("id: %v\n", self.PathId))

	for _, site := range self.Sites {
		buf.WriteString(fmt.Sprintf("%v\n", site))
	}
	return buf.String()
}

func (self *PathSpec) Delay() error {
	site := self.Sites[0]
	site.DelayBySleeping()
	return nil
}

func (self *PathSpec) Forward() ([]byte, error) {
	path := new(PathSpec)
	path.Sites = self.Sites[1:]
	path.PathId = self.PathId
	return path.forwardFromFirst()
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"log/syslog"
	"os"
	"sync"
)

// derive logger
func getLogger(useSyslog bool) (logger io.Writer, err error) {
	if useSyslog {
		logger, err = syslog.New(syslog.LOG_NOTICE, monitoringEvent)
	} else {
		logger = os.Stderr
		log.SetPrefix(monitoringEvent + ": ")
	}
	if err == nil {
		log.SetOutput(logger)
	}
	return &LineWriter{w: logger}, err
}

// pipe r to logger in the background
func logStream(r io.Reader, logger io.Writer, wg *sync.WaitGroup) {
	wg.Add(1)

	go func() {
		for {
			if _, err := io.Copy(logger, r); err != nil {
				break
			}
		}
		wg.Done()
	}()
}
<commit_msg>Set syslog facility to daemon; no longer write to kernel facility<commit_after>package main

import (
	"io"
	"log"
	"log/syslog"
	"os"
	"sync"
)

// With Go 1.1, we can specify the syslog facility in addition to the priority:
// https://code.google.com/p/go/source/browse/src/pkg/log/syslog/syslog.go?name=go1.1
// To stay compatible with older Go versions, set the facility by hand for now.
const LOG_DAEMON = (3 << 3)

// derive logger
func getLogger(useSyslog bool) (logger io.Writer, err error) {
	if useSyslog {
		logger, err = syslog.New(LOG_DAEMON|syslog.LOG_NOTICE, monitoringEvent)
	} else {
		logger = os.Stderr
		log.SetPrefix(monitoringEvent + ": ")
	}
	if err == nil {
		log.SetOutput(logger)
	}
	return &LineWriter{w: logger}, err
}

// pipe r to logger in the background
func logStream(r io.Reader, logger io.Writer, wg *sync.WaitGroup) {
	wg.Add(1)

	go func() {
		for {
			if _, err := io.Copy(logger, r); err != nil {
				break
			}
		}
		wg.Done()
	}()
}
<|endoftext|>


<commit_before>package nagiosplugin

import (
	"fmt"
	"os"
	"strings"
)

const (
	messageSeparator = ", "
)

// Standalone Exit function for simple checks without multiple results
// or perfdata.
func Exit(status Status, message string) {
	fmt.Printf("%v: %s\n", status, message)
	os.Exit(int(status))
}

// Represents the state of a Nagios check.
type Check struct {
	results  []Result
	perfdata []PerfDatum
	status   Status
}

// NewCheck returns an empty Check object.
func NewCheck() *Check {
	c := new(Check)
	return c
}

// AddResult adds a check result. This will not terminate the check. If
// status is the highest yet reported, this will update the check's
// final return status.
func (c *Check) AddResult(status Status, message string) {
	var result Result
	result.status = status
	result.message = message
	c.results = append(c.results, result)
	if result.status > c.status {
		c.status = result.status
	}
}

// AddPerfDatum adds a metric to the set output by the check. unit must
// be a valid Nagios unit of measurement (UOM): "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string. UOMs are
// not case-sensitive.
//
// Zero or more of the thresholds min, max, warn and crit may be
// supplied; these must be of the same UOM as the value.
//
// A threshold may be positive or negative infinity, in which case it
// will be omitted in the check output. A value may not be either
// infinity.
//
// Returns error on invalid parameters.
func (c *Check) AddPerfDatum(label, unit string, value float64, thresholds ...float64) error {
	datum, err := NewPerfDatum(label, unit, value, thresholds...)
	if err != nil {
		return err
	}
	c.perfdata = append(c.perfdata, *datum)
	return nil
}

// exitInfoText returns the most important result text, formatted for
// the first line of plugin output.
//
// Returns joined string of (messageSeparator-separated) info text from
// results which have a status of at least c.status.
func (c Check) exitInfoText() string {
	importantMessages := make([]string, 0)
	for _, result := range c.results {
		if result.status == c.status {
			importantMessages = append(importantMessages, result.message)
		}
	}
	return strings.Join(importantMessages, messageSeparator)
}

// String representation of the check results, suitable for output and
// parsing by Nagios.
func (c Check) String() string {
	value := fmt.Sprintf("%v: %s", c.status, c.exitInfoText())
	value += RenderPerfdata(c.perfdata)
	return value
}

// Finish ends the check, prints its output (to stdout), and exits with
// the correct status.
func (c *Check) Finish() {
	if r := recover(); r != nil {
		c.Exitf(CRITICAL, "check panicked: %v", r)
	}
	if len(c.results) == 0 {
		c.AddResult(UNKNOWN, "no check result specified")
	}
	fmt.Println(c)
	os.Exit(int(c.status))
}

// Exitf takes a status plus a format string, and a list of
// parameters to pass to Sprintf. It then immediately outputs and exits.
func (c *Check) Exitf(status Status, format string, v ...interface{}) {
	info := fmt.Sprintf(format, v...)
	c.AddResult(status, info)
	c.Finish()
}

// Criticalf is a shorthand function which calls Exitf with
// nagiosplugin.CRITICAL and the supplied format string.
func (c *Check) Criticalf(format string, v ...interface{}) {
	c.Exitf(CRITICAL, format, v...)
}
<commit_msg>Add check.Unknownf shorthand function<commit_after>package nagiosplugin

import (
	"fmt"
	"os"
	"strings"
)

const (
	messageSeparator = ", "
)

// Standalone Exit function for simple checks without multiple results
// or perfdata.
func Exit(status Status, message string) {
	fmt.Printf("%v: %s\n", status, message)
	os.Exit(int(status))
}

// Represents the state of a Nagios check.
type Check struct {
	results  []Result
	perfdata []PerfDatum
	status   Status
}

// NewCheck returns an empty Check object.
func NewCheck() *Check {
	c := new(Check)
	return c
}

// AddResult adds a check result. This will not terminate the check. If
// status is the highest yet reported, this will update the check's
// final return status.
func (c *Check) AddResult(status Status, message string) {
	var result Result
	result.status = status
	result.message = message
	c.results = append(c.results, result)
	if result.status > c.status {
		c.status = result.status
	}
}

// AddPerfDatum adds a metric to the set output by the check. unit must
// be a valid Nagios unit of measurement (UOM): "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string. UOMs are
// not case-sensitive.
//
// Zero or more of the thresholds min, max, warn and crit may be
// supplied; these must be of the same UOM as the value.
//
// A threshold may be positive or negative infinity, in which case it
// will be omitted in the check output. A value may not be either
// infinity.
//
// Returns error on invalid parameters.
func (c *Check) AddPerfDatum(label, unit string, value float64, thresholds ...float64) error {
	datum, err := NewPerfDatum(label, unit, value, thresholds...)
	if err != nil {
		return err
	}
	c.perfdata = append(c.perfdata, *datum)
	return nil
}

// exitInfoText returns the most important result text, formatted for
// the first line of plugin output.
//
// Returns joined string of (messageSeparator-separated) info text from
// results which have a status of at least c.status.
func (c Check) exitInfoText() string {
	importantMessages := make([]string, 0)
	for _, result := range c.results {
		if result.status == c.status {
			importantMessages = append(importantMessages, result.message)
		}
	}
	return strings.Join(importantMessages, messageSeparator)
}

// String representation of the check results, suitable for output and
// parsing by Nagios.
func (c Check) String() string {
	value := fmt.Sprintf("%v: %s", c.status, c.exitInfoText())
	value += RenderPerfdata(c.perfdata)
	return value
}

// Finish ends the check, prints its output (to stdout), and exits with
// the correct status.
func (c *Check) Finish() {
	if r := recover(); r != nil {
		c.Exitf(CRITICAL, "check panicked: %v", r)
	}
	if len(c.results) == 0 {
		c.AddResult(UNKNOWN, "no check result specified")
	}
	fmt.Println(c)
	os.Exit(int(c.status))
}

// Exitf takes a status plus a format string, and a list of
// parameters to pass to Sprintf. It then immediately outputs and exits.
func (c *Check) Exitf(status Status, format string, v ...interface{}) {
	info := fmt.Sprintf(format, v...)
	c.AddResult(status, info)
	c.Finish()
}

// Criticalf is a shorthand function which exits the check with status
// CRITICAL and the message provided.
// nagiosplugin.CRITICAL and the supplied format string.
func (c *Check) Criticalf(format string, v ...interface{}) {
	c.Exitf(CRITICAL, format, v...)
}

// Unknownf is a shorthand function which exits the check with status
// UNKNOWN and the message provided.
func (c *Check) Unknownf(format string, v ...interface{}) {
	c.Exitf(UNKNOWN, format, v...)
}
<|endoftext|>


<commit_before>package mailck

import (
	"context"
	"fmt"
	"net"
	"net/smtp"
	"regexp"
	"strings"
)

var emailRexp = regexp.MustCompile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$")

// Check checks the syntax and if valid, it checks the mailbox by connecting to
// the target mailserver
// The fromEmail is used as from address in the communication to the foreign mailserver.
func Check(fromEmail, checkEmail string) (result Result, err error) {
	if !CheckSyntax(checkEmail) {
		return InvalidSyntax, nil
	}

	if CheckDisposable(checkEmail) {
		return Disposable, nil
	}
	return CheckMailbox(fromEmail, checkEmail)
}

func CheckWithContext(ctx context.Context, fromEmail, checkEmail string) (result Result, err error) {
	if !CheckSyntax(checkEmail) {
		return InvalidSyntax, nil
	}

	if CheckDisposable(checkEmail) {
		return Disposable, nil
	}
	return CheckMailboxWithContext(ctx, fromEmail, checkEmail)
}

// CheckSyntax returns true for a valid email, false otherwise
func CheckSyntax(checkEmail string) bool {
	return emailRexp.Match([]byte(checkEmail))
}

var noContext = context.Background()
var defaultResolver = net.DefaultResolver
var defaultDialer = net.Dialer{}

// CheckMailbox checks the checkEmail by connecting to the target mailbox and returns the result.
// The fromEmail is used as from address in the communication to the foreign mailserver.
func CheckMailbox(fromEmail, checkEmail string) (result Result, err error) {
	mxList, err := net.LookupMX(hostname(checkEmail))
	// TODO: Distinguish between usual network errors
	if err != nil || len(mxList) == 0 {
		return InvalidDomain, nil
	}
	return checkMailbox(noContext, fromEmail, checkEmail, mxList, 25)
}

func CheckMailboxWithContext(ctx context.Context, fromEmail, checkEmail string) (result Result, err error) {
	mxList, err := defaultResolver.LookupMX(ctx, hostname(checkEmail))
	// TODO: Distinguish between usual network errors
	if err != nil || len(mxList) == 0 {
		return InvalidDomain, nil
	}
	return checkMailbox(ctx, fromEmail, checkEmail, mxList, 25)
}

type checkRv struct {
	res Result
	err error
}

func checkMailbox(ctx context.Context, fromEmail, checkEmail string, mxList []*net.MX, port int) (result Result, err error) {
	// try to connect to one mx
	var c *smtp.Client
	for _, mx := range mxList {
		var conn net.Conn
		conn, err = defaultDialer.DialContext(ctx, "tcp", fmt.Sprintf("%v:%v", mx.Host, port))
		if t, ok := err.(*net.OpError); ok {
			if t.Timeout() {
				return TimeoutError, err
			}
			return NetworkError, err
		} else if err != nil {
			return MailserverError, err
		}
		c, err = smtp.NewClient(conn, mx.Host)
		if err == nil {
			break
		}
	}
	if err != nil {
		return MailserverError, err
	}
	//	if c == nil {
	//		return MailserverError, fmt.Errorf("can't obtain connection for %v", checkEmail)
	//	}

	resChan := make(chan checkRv, 1)

	go func() {
		defer c.Close()
		defer c.Quit() // defer ist LIFO
		// HELO
		err = c.Hello(hostname(fromEmail))
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		// MAIL FROM
		err = c.Mail(fromEmail)
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		// RCPT TO
		id, err := c.Text.Cmd("RCPT TO:<%s>", checkEmail)
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}
		c.Text.StartResponse(id)
		code, _, err := c.Text.ReadResponse(25)
		c.Text.EndResponse(id)
		if code == 550 {
			resChan <- checkRv{MailboxUnavailable, nil}
			return
		}

		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		resChan <- checkRv{Valid, nil}

	}()
	select {
	case <-ctx.Done():
		return TimeoutError, ctx.Err()
	case q := <-resChan:
		return q.res, q.err
	}
}

func hostname(mail string) string {
	return mail[strings.Index(mail, "@")+1:]
}
<commit_msg>sanity check<commit_after>package mailck

import (
	"context"
	"fmt"
	"net"
	"net/smtp"
	"regexp"
	"strings"
)

var emailRexp = regexp.MustCompile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$")

// Check checks the syntax and if valid, it checks the mailbox by connecting to
// the target mailserver
// The fromEmail is used as from address in the communication to the foreign mailserver.
func Check(fromEmail, checkEmail string) (result Result, err error) {
	if !CheckSyntax(checkEmail) {
		return InvalidSyntax, nil
	}

	if CheckDisposable(checkEmail) {
		return Disposable, nil
	}
	return CheckMailbox(fromEmail, checkEmail)
}

func CheckWithContext(ctx context.Context, fromEmail, checkEmail string) (result Result, err error) {
	if !CheckSyntax(checkEmail) {
		return InvalidSyntax, nil
	}

	if CheckDisposable(checkEmail) {
		return Disposable, nil
	}
	return CheckMailboxWithContext(ctx, fromEmail, checkEmail)
}

// CheckSyntax returns true for a valid email, false otherwise
func CheckSyntax(checkEmail string) bool {
	return emailRexp.Match([]byte(checkEmail))
}

var noContext = context.Background()
var defaultResolver = net.DefaultResolver
var defaultDialer = net.Dialer{}

// CheckMailbox checks the checkEmail by connecting to the target mailbox and returns the result.
// The fromEmail is used as from address in the communication to the foreign mailserver.
func CheckMailbox(fromEmail, checkEmail string) (result Result, err error) {
	mxList, err := net.LookupMX(hostname(checkEmail))
	// TODO: Distinguish between usual network errors
	if err != nil || len(mxList) == 0 {
		return InvalidDomain, nil
	}
	return checkMailbox(noContext, fromEmail, checkEmail, mxList, 25)
}

func CheckMailboxWithContext(ctx context.Context, fromEmail, checkEmail string) (result Result, err error) {
	mxList, err := defaultResolver.LookupMX(ctx, hostname(checkEmail))
	// TODO: Distinguish between usual network errors
	if err != nil || len(mxList) == 0 {
		return InvalidDomain, nil
	}
	return checkMailbox(ctx, fromEmail, checkEmail, mxList, 25)
}

type checkRv struct {
	res Result
	err error
}

func checkMailbox(ctx context.Context, fromEmail, checkEmail string, mxList []*net.MX, port int) (result Result, err error) {
	// try to connect to one mx
	var c *smtp.Client
	for _, mx := range mxList {
		var conn net.Conn
		conn, err = defaultDialer.DialContext(ctx, "tcp", fmt.Sprintf("%v:%v", mx.Host, port))
		if t, ok := err.(*net.OpError); ok {
			if t.Timeout() {
				return TimeoutError, err
			}
			return NetworkError, err
		} else if err != nil {
			return MailserverError, err
		}
		c, err = smtp.NewClient(conn, mx.Host)
		if err == nil {
			break
		}
	}
	if err != nil {
		return MailserverError, err
	}
	if c == nil {
		// just to get very sure, that we have a connection
		// this code line should never be reached!
		return MailserverError, fmt.Errorf("can't obtain connection for %v", checkEmail)
	}

	resChan := make(chan checkRv, 1)

	go func() {
		defer c.Close()
		defer c.Quit() // defer ist LIFO
		// HELO
		err = c.Hello(hostname(fromEmail))
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		// MAIL FROM
		err = c.Mail(fromEmail)
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		// RCPT TO
		id, err := c.Text.Cmd("RCPT TO:<%s>", checkEmail)
		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}
		c.Text.StartResponse(id)
		code, _, err := c.Text.ReadResponse(25)
		c.Text.EndResponse(id)
		if code == 550 {
			resChan <- checkRv{MailboxUnavailable, nil}
			return
		}

		if err != nil {
			resChan <- checkRv{MailserverError, err}
			return
		}

		resChan <- checkRv{Valid, nil}

	}()
	select {
	case <-ctx.Done():
		return TimeoutError, ctx.Err()
	case q := <-resChan:
		return q.res, q.err
	}
}

func hostname(mail string) string {
	return mail[strings.Index(mail, "@")+1:]
}
<|endoftext|>