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

// Package promlog defines standardised ways to initialize Go kit loggers
// across Prometheus components.
// It should typically only ever be imported by main packages.
package promlog

import (
	"os"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
)

// AllowedLevel is a settable identifier for the minimum level a log entry
// must be have.
type AllowedLevel struct {
	s string
	o level.Option
}

func (l *AllowedLevel) String() string {
	return l.s
}

// Set updates the value of the allowed level.
func (l *AllowedLevel) Set(s string) error {
	switch s {
	case "debug":
		l.o = level.AllowDebug()
	case "info":
		l.o = level.AllowInfo()
	case "warn":
		l.o = level.AllowWarn()
	case "error":
		l.o = level.AllowError()
	default:
		return errors.Errorf("unrecognized log level %q", s)
	}
	l.s = s
	return nil
}

// New returns a new leveled oklog logger in the logfmt format. Each logged line will be annotated
// with a timestamp. The output always goes to stderr.
func New(al AllowedLevel) log.Logger {
	l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
	return l
}
<commit_msg>Actually filter logs based on level.<commit_after>// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package promlog defines standardised ways to initialize Go kit loggers
// across Prometheus components.
// It should typically only ever be imported by main packages.
package promlog

import (
	"os"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
)

// AllowedLevel is a settable identifier for the minimum level a log entry
// must be have.
type AllowedLevel struct {
	s string
	o level.Option
}

func (l *AllowedLevel) String() string {
	return l.s
}

// Set updates the value of the allowed level.
func (l *AllowedLevel) Set(s string) error {
	switch s {
	case "debug":
		l.o = level.AllowDebug()
	case "info":
		l.o = level.AllowInfo()
	case "warn":
		l.o = level.AllowWarn()
	case "error":
		l.o = level.AllowError()
	default:
		return errors.Errorf("unrecognized log level %q", s)
	}
	l.s = s
	return nil
}

// New returns a new leveled oklog logger in the logfmt format. Each logged line will be annotated
// with a timestamp. The output always goes to stderr.
func New(al AllowedLevel) log.Logger {
	l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	l = level.NewFilter(l, al.o)
	l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
	return l
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	_ "github.com/lib/pq"
)

type Fataler interface {
	Fatal(...interface{})
}

func openTestDB(f Fataler) *sql.DB {
	dbname := os.Getenv("PGDATABASE")
	sslmode := os.Getenv("PGSSLMODE")
	timeout := os.Getenv("PGCONNECT_TIMEOUT")

	if dbname == "" {
		os.Setenv("PGDATABASE", "tablestruct_test")
	}

	if sslmode == "" {
		os.Setenv("PGSSLMODE", "disable")
	}

	if timeout == "" {
		os.Setenv("PGCONNECT_TIMEOUT", "10")
	}

	db, err := sql.Open("postgres", "")
	if err != nil {
		f.Fatal(err)
	}
	return db
}

func tempDir(f Fataler) string {
	name, err := ioutil.TempDir("", "tablestruct_test")
	if err != nil {
		f.Fatal(err)
	}
	return name
}

func tempFile(dir string, f Fataler) *os.File {
	file, err := ioutil.TempFile(dir, "tablestruct_test")
	if err != nil {
		f.Fatal(err)
	}
	return file
}

func tempGoFile(dir string, f Fataler) *os.File {
	file := tempFile(dir, f)
	os.Symlink(file.Name(), file.Name()+".go")
	return file
}

var get = CodeGenTest{
	CreateTableSQL: `CREATE TABLE t (id int, val int)`,
	CleanupSQL:     `DROP TABLE t`,
	TableSetupSQL:  `INSERT INTO t (SELECT generate_series(0, 10), generate_series(100, 110))`,
	Metadata: `
[
    {
        "struct": "T",
        "table": "t",
        "columns": [{
            "field": "Value",
            "column": "val",
            "type": "int"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type T struct {
    ID    int
    Value int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewTMapper(db)
    t, err := m.Get(8)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d\n", t.Value)
}
`,
	Expected: "108\n",
}

var all = CodeGenTest{
	CreateTableSQL: `CREATE TABLE zipcodes (id int, zipcode varchar)`,
	CleanupSQL:     `DROP TABLE zipcodes`,
	TableSetupSQL:  `INSERT INTO zipcodes (SELECT generate_series(0, 9), generate_series(21230, 21239)::varchar)`,
	Metadata: `
[
    {
        "struct": "ZIPCode",
        "table": "zipcodes",
        "columns": [{
            "field": "Z5",
            "column": "zipcode",
            "type": "varchar"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type ZIPCode struct {
    ID    int
    Z5    string
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewZIPCodeMapper(db)
    zips, err := m.All()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d\n", len(zips))
    for i := range zips {
        fmt.Printf("%s\n", zips[i].Z5)
    }
}
`,
	Expected: `10
21230
21231
21232
21233
21234
21235
21236
21237
21238
21239
`}

var insert = CodeGenTest{
	CreateTableSQL: `CREATE TABLE person (id int, name varchar, age int)`,
	CleanupSQL:     `DROP TABLE person`,
	Metadata: `
[
    {
        "struct": "Person",
        "table": "person",
        "columns": [{
            "field": "Name",
            "column": "name",
            "type": "varchar"
        }, {
            "field": "Age",
            "column": "age",
            "type": "int"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type Person struct {
    ID      int64
    Name    string
    Age     int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewPersonMapper(db)
    var before, after int
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&before); err != nil {
        log.Fatal(err)
    }
    p := Person{42, "Paul Smith", 37}
    if err = m.Insert(&p); err != nil {
        log.Fatal(err)
    }
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&after); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("delta: %d\n", after-before)
    dest := []interface{}{
        new(int64),
        new(string),
        new(int),
    }
    err = db.QueryRow("SELECT * FROM person WHERE id = 42").Scan(dest...)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d '%s' %d\n", *dest[0].(*int64), *dest[1].(*string), *dest[2].(*int))
}
`,
	Expected: `delta: 1
42 'Paul Smith' 37
`,
}

var update = CodeGenTest{
	CreateTableSQL: insert.CreateTableSQL,
	CleanupSQL:     insert.CleanupSQL,
	TableSetupSQL:  `INSERT INTO person VALUES (42, 'Paul Smith', 37)`,
	Metadata:       insert.Metadata,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type Person struct {
    ID      int64
    Name    string
    Age     int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewPersonMapper(db)
    p := Person{42, "Brian Eno", 66}
    if err = m.Update(&p); err != nil {
        log.Fatal(err)
    }
    dest := []interface{}{
        new(int64),
        new(string),
        new(int),
    }
    err = db.QueryRow("SELECT * FROM person WHERE id = 42").Scan(dest...)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d '%s' %d\n", *dest[0].(*int64), *dest[1].(*string), *dest[2].(*int))
}
`,
	Expected: "42 'Brian Eno' 66\n",
}

type CodeGenTest struct {
	CreateTableSQL string
	TableSetupSQL  string
	CleanupSQL     string
	Metadata       string
	DriverCode     string
	Expected       string
}

func testCodeGen(t *testing.T, test CodeGenTest) {
	db := openTestDB(t)
	defer db.Close()

	_, err := db.Exec(test.CreateTableSQL)
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if test.CleanupSQL != "" {
			db.Exec(test.CleanupSQL)
		}
	}()

	if test.TableSetupSQL != "" {
		_, err = db.Exec(test.TableSetupSQL)
		if err != nil {
			t.Fatal(err)
		}
	}

	mapper, err := NewMap(strings.NewReader(test.Metadata))
	if err != nil {
		t.Fatal(err)
	}

	dir := tempDir(t)
	genCodeFile := tempGoFile(dir, t)
	driverCodeFile := tempGoFile(dir, t)

	defer func() {
		genCodeFile.Close()
		driverCodeFile.Close()
		os.RemoveAll(dir)
	}()

	code := NewCode()
	code.Gen(mapper, "main", genCodeFile)

	if _, err := driverCodeFile.WriteString(test.DriverCode); err != nil {
		t.Fatal(err)
	}

	genCodeFile.Sync()
	driverCodeFile.Sync()

	goFiles, err := filepath.Glob(filepath.Join(dir, "*.go"))
	if err != nil {
		t.Fatal(err)
	}

	args := []string{"run"}
	for i := range goFiles {
		args = append(args, goFiles[i])
	}

	cmd := exec.Command("go", args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		t.Log(stderr.String())
		t.Fatalf("error running generated Go code: %s", err)
	}

	if actual := stdout.String(); actual != test.Expected {
		t.Errorf("want %q, got %q", test.Expected, actual)
	}
}

func TestCodeGen(t *testing.T) {
	var tests = map[string]CodeGenTest{
		"Get":    get,
		"All":    all,
		"Insert": insert,
		"Update": update,
	}
	for name, test := range tests {
		t.Log(name)
		testCodeGen(t, test)
	}
}
<commit_msg>Add unit test for insert many<commit_after>package main

import (
	"bytes"
	"database/sql"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	_ "github.com/lib/pq"
)

type Fataler interface {
	Fatal(...interface{})
}

func openTestDB(f Fataler) *sql.DB {
	dbname := os.Getenv("PGDATABASE")
	sslmode := os.Getenv("PGSSLMODE")
	timeout := os.Getenv("PGCONNECT_TIMEOUT")

	if dbname == "" {
		os.Setenv("PGDATABASE", "tablestruct_test")
	}

	if sslmode == "" {
		os.Setenv("PGSSLMODE", "disable")
	}

	if timeout == "" {
		os.Setenv("PGCONNECT_TIMEOUT", "10")
	}

	db, err := sql.Open("postgres", "")
	if err != nil {
		f.Fatal(err)
	}
	return db
}

func tempDir(f Fataler) string {
	name, err := ioutil.TempDir("", "tablestruct_test")
	if err != nil {
		f.Fatal(err)
	}
	return name
}

func tempFile(dir string, f Fataler) *os.File {
	file, err := ioutil.TempFile(dir, "tablestruct_test")
	if err != nil {
		f.Fatal(err)
	}
	return file
}

func tempGoFile(dir string, f Fataler) *os.File {
	file := tempFile(dir, f)
	os.Symlink(file.Name(), file.Name()+".go")
	return file
}

var get = CodeGenTest{
	CreateTableSQL: `CREATE TABLE t (id int, val int)`,
	CleanupSQL:     `DROP TABLE t`,
	TableSetupSQL:  `INSERT INTO t (SELECT generate_series(0, 10), generate_series(100, 110))`,
	Metadata: `
[
    {
        "struct": "T",
        "table": "t",
        "columns": [{
            "field": "Value",
            "column": "val",
            "type": "int"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type T struct {
    ID    int
    Value int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewTMapper(db)
    t, err := m.Get(8)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d\n", t.Value)
}
`,
	Expected: "108\n",
}

var all = CodeGenTest{
	CreateTableSQL: `CREATE TABLE zipcodes (id int, zipcode varchar)`,
	CleanupSQL:     `DROP TABLE zipcodes`,
	TableSetupSQL:  `INSERT INTO zipcodes (SELECT generate_series(0, 9), generate_series(21230, 21239)::varchar)`,
	Metadata: `
[
    {
        "struct": "ZIPCode",
        "table": "zipcodes",
        "columns": [{
            "field": "Z5",
            "column": "zipcode",
            "type": "varchar"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type ZIPCode struct {
    ID    int
    Z5    string
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewZIPCodeMapper(db)
    zips, err := m.All()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d\n", len(zips))
    for i := range zips {
        fmt.Printf("%s\n", zips[i].Z5)
    }
}
`,
	Expected: `10
21230
21231
21232
21233
21234
21235
21236
21237
21238
21239
`}

var insert = CodeGenTest{
	CreateTableSQL: `CREATE TABLE person (id int, name varchar, age int)`,
	CleanupSQL:     `DROP TABLE person`,
	Metadata: `
[
    {
        "struct": "Person",
        "table": "person",
        "columns": [{
            "field": "Name",
            "column": "name",
            "type": "varchar"
        }, {
            "field": "Age",
            "column": "age",
            "type": "int"
        }]
    }
]
`,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type Person struct {
    ID      int64
    Name    string
    Age     int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewPersonMapper(db)
    var before, after int
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&before); err != nil {
        log.Fatal(err)
    }
    p := Person{42, "Paul Smith", 37}
    if err = m.Insert(&p); err != nil {
        log.Fatal(err)
    }
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&after); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("delta: %d\n", after-before)
    dest := []interface{}{
        new(int64),
        new(string),
        new(int),
    }
    err = db.QueryRow("SELECT * FROM person WHERE id = 42").Scan(dest...)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d '%s' %d\n", *dest[0].(*int64), *dest[1].(*string), *dest[2].(*int))
}
`,
	Expected: `delta: 1
42 'Paul Smith' 37
`,
}

var update = CodeGenTest{
	CreateTableSQL: insert.CreateTableSQL,
	CleanupSQL:     insert.CleanupSQL,
	TableSetupSQL:  `INSERT INTO person VALUES (42, 'Paul Smith', 37)`,
	Metadata:       insert.Metadata,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type Person struct {
    ID      int64
    Name    string
    Age     int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewPersonMapper(db)
    p := Person{42, "Brian Eno", 66}
    if err = m.Update(&p); err != nil {
        log.Fatal(err)
    }
    dest := []interface{}{
        new(int64),
        new(string),
        new(int),
    }
    err = db.QueryRow("SELECT * FROM person WHERE id = 42").Scan(dest...)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d '%s' %d\n", *dest[0].(*int64), *dest[1].(*string), *dest[2].(*int))
}
`,
	Expected: "42 'Brian Eno' 66\n",
}

var insertMany = CodeGenTest{
	CreateTableSQL: insert.CreateTableSQL,
	CleanupSQL:     insert.CleanupSQL,
	TableSetupSQL:  insert.TableSetupSQL,
	Metadata:       insert.Metadata,
	DriverCode: `
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

type Person struct {
    ID      int64
    Name    string
    Age     int
}

func main() {
    db, err := sql.Open("postgres", "")
    if err != nil {
        log.Fatal(err)
    }
    m := NewPersonMapper(db)
    var before, after int
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&before); err != nil {
        log.Fatal(err)
    }
    people := []*Person{
        {42, "Paul Smith", 37},
        {43, "Brian Eno", 66},
        {44, "Ada Lovelace", 27},
    }
    if err = m.InsertMany(people); err != nil {
        log.Fatal(err)
    }
    if err := db.QueryRow("SELECT COUNT(*) FROM person").Scan(&after); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("delta: %d\n", after-before)
    /*
    dest := []interface{}{
        new(int64),
        new(string),
        new(int),
    }
    err = db.QueryRow("SELECT * FROM person WHERE id = 42").Scan(dest...)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d '%s' %d\n", *dest[0].(*int64), *dest[1].(*string), *dest[2].(*int))
    */
}
`,
	Expected: "delta: 3\n",
}

type CodeGenTest struct {
	CreateTableSQL string
	TableSetupSQL  string
	CleanupSQL     string
	Metadata       string
	DriverCode     string
	Expected       string
}

func testCodeGen(t *testing.T, test CodeGenTest) {
	db := openTestDB(t)
	defer db.Close()

	_, err := db.Exec(test.CreateTableSQL)
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if test.CleanupSQL != "" {
			db.Exec(test.CleanupSQL)
		}
	}()

	if test.TableSetupSQL != "" {
		_, err = db.Exec(test.TableSetupSQL)
		if err != nil {
			t.Fatal(err)
		}
	}

	mapper, err := NewMap(strings.NewReader(test.Metadata))
	if err != nil {
		t.Fatal(err)
	}

	dir := tempDir(t)
	genCodeFile := tempGoFile(dir, t)
	driverCodeFile := tempGoFile(dir, t)

	defer func() {
		genCodeFile.Close()
		driverCodeFile.Close()
		os.RemoveAll(dir)
	}()

	code := NewCode()
	code.Gen(mapper, "main", genCodeFile)

	if _, err := driverCodeFile.WriteString(test.DriverCode); err != nil {
		t.Fatal(err)
	}

	genCodeFile.Sync()
	driverCodeFile.Sync()

	goFiles, err := filepath.Glob(filepath.Join(dir, "*.go"))
	if err != nil {
		t.Fatal(err)
	}

	args := []string{"run"}
	for i := range goFiles {
		args = append(args, goFiles[i])
	}

	cmd := exec.Command("go", args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		t.Log(stderr.String())
		t.Fatalf("error running generated Go code: %s", err)
	}

	if actual := stdout.String(); actual != test.Expected {
		t.Errorf("want %q, got %q", test.Expected, actual)
	}
}

func TestCodeGen(t *testing.T) {
	var tests = map[string]CodeGenTest{
		"Get":        get,
		"All":        all,
		"Insert":     insert,
		"Update":     update,
		"InsertMany": insertMany,
	}
	for name, test := range tests {
		t.Log(name)
		testCodeGen(t, test)
	}
}
<|endoftext|>


<commit_before>package todo

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01 15:12:52"), email, false, html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/add", 302)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=? where name=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(todo), html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/edit", 302)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
		checkErr(err)
		http.Redirect(w, r, "/", 302)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/add", addHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/edit/{id}", editHandler)
	router.HandleFunc("/del/{id}", delHandler)

	router.HandleFunc("/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Minor fixes<commit_after>package todo

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01"), email, false, html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/add", 302)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=? where name=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(todo), html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/edit", 302)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
		checkErr(err)
		http.Redirect(w, r, "/", 302)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/add", addHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/edit/{id}", editHandler)
	router.HandleFunc("/del/{id}", delHandler)

	router.HandleFunc("/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package todo

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01"), email, false, html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/add", 302)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=? where name=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(todo), html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/edit", 302)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
		checkErr(err)
		http.Redirect(w, r, "/", 302)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/add", addHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/edit/{id}", editHandler)
	router.HandleFunc("/del/{id}", delHandler)

	router.HandleFunc("/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Rework api to make it fullcalendar compatible<commit_after>package todo

import (
	"database/sql"
	"encoding/json"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS    = ""
	ADDRESS = "https://todo.pantsu.cat"
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}
type Cal struct {
	Title       string `json:"title"`
	Allday      bool   `json:"allday"`
	Description string `json:"description"`
	Start       string `json:"start"`
	End         string `json:"end"`
	URL         string `json:"url"`
}

type CalPage struct {
	Cal []Cal `json:""`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	err := templates.ExecuteTemplate(w, "index.html", "")
	checkErr(err)
}

func calHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := CalPage{Cal: []Cal{}}

	for rows.Next() {
		res := Cal{}
		var name string
		rows.Scan(name, &res.Title, &res.Description, &res.Start, &res.End)
		res.URL = ADDRESS + "/todo/" + name

		b.Cal = append(b.Cal, res)
	}
	db.Close()

	checkErr(err)
	w.Header().Set("Content-Type", "application/json")
	err = json.NewEncoder(w).Encode(b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

func taskHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "task.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "task.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		due := r.FormValue("duetime")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, duetime, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(duetime), time.Now().Format("2016-02-01 15:12:52"), email, false, html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/add", 302)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=? where name=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(todo), html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)
		http.Redirect(w, r, "/edit", 302)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
		checkErr(err)
		http.Redirect(w, r, "/", 302)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", taskHandler)
	router.HandleFunc("/add", addHandler)
	router.HandleFunc("/api/cal", addHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/edit/{id}", editHandler)
	router.HandleFunc("/del/{id}", delHandler)

	router.HandleFunc("/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/gammons/todolist/todolist"
	"github.com/skratchdot/open-golang/open"
)

const (
	VERSION = "0.3.1"
)

func main() {
	if len(os.Args) <= 1 {
		usage()
		os.Exit(0)
	}
	input := strings.Join(os.Args[1:], " ")
	routeInput(os.Args[1], input)
}

func usage() {
	blue := color.New(color.FgBlue)
	cyan := color.New(color.FgCyan)
	yellow := color.New(color.FgYellow)

	blueBold := blue.Add(color.Bold)

	fmt.Printf("todo v%s, a simple, command line based, GTD-style todo manager\n", VERSION)

	blueBold.Println("\nAdding todos")
	fmt.Println("  the 'a' command adds todos.")
	fmt.Println("  You can also optionally specify a due date.")
	fmt.Println("  Specify a due date by putting 'due <date>' at the end, where <date> is in (tod|today|tom|tomorrow|mon|tue|wed|thu|fri|sat|sun)")
	fmt.Println("\n  Examples for adding a todo:")
	yellow.Println("\ttodo a Meeting with @bob about +importantPrject due today")
	yellow.Println("\ttodo a +work +verify did @john fix the build\\?")

	blueBold.Println("\nListing todos")
	fmt.Println("  When listing todos, you can filter and group the output.\n")

	fmt.Println("  todo l due (tod|today|tom|tomorrow|overdue|this week|next week|mon|tue|wed|thu|fri|sat|sun|none)")
	fmt.Println("  todo l overdue")

	cyan.Println("  Filtering by date:\n")
	yellow.Println("\ttodo l due tod")
	fmt.Println("\tlists all todos due today\n")
	yellow.Println("\ttodo l due tom")
	fmt.Println("\tlists all todos due tomorrow\n")
	yellow.Println("\ttodo l due mon")
	fmt.Println("\tlists all todos due monday\n")
	yellow.Println("\ttodo l overdue")
	fmt.Println("\tlists all todos where the due date is in the past\n")
	yellow.Println("\ttodo agenda")
	fmt.Println("\tlists all todos where the due date is today or in the past\n")

	cyan.Println("  Grouping:")
	fmt.Println("  You can group todos by context or project.")
	yellow.Println("\ttodo l by c")
	fmt.Println("\tlists all todos grouped by context\n")
	yellow.Println("\ttodo l by p")
	fmt.Println("\tlists all todos grouped by project\n")

	cyan.Println("  Grouping and filtering:")
	fmt.Println("  Of course, you can combine grouping and filtering to get a nice formatted list.\n")
	yellow.Println("\ttodo l due today by c")
	fmt.Println("\tlists all todos due today grouped by context\n")
	yellow.Println("\ttodo l +project due this week by c")
	fmt.Println("\tlists all todos due today for +project, grouped by context\n")
	yellow.Println("\ttodo l @frank due tom by p")
	fmt.Println("\tlists all todos due tomorrow concerining @frank for +project, grouped by project\n")

	blueBold.Println("\nCompleting and uncompleting ")
	fmt.Println("Complete and Uncomplete a todo by its Id:\n")
	yellow.Println("\ttodo c 33")
	fmt.Println("\tCompletes a todo with id 33\n")
	yellow.Println("\ttodo uc 33")
	fmt.Println("\tUncompletes a todo with id 33\n")

	blueBold.Println("\nArchiving")
	fmt.Println("You can archive todos once they are done, or if you might come back to them.")
	fmt.Println("By default, todo will only show unarchived todos.\n")
	yellow.Println("\ttodo ar 33")
	fmt.Println("\tArchives a todo with id 33\n")
	yellow.Println("\ttodo ac")
	fmt.Println("\tArchives all completed todos\n")
	yellow.Println("\ttodo l archived")
	fmt.Println("\tlist all archived todos\n")

	blueBold.Println("\nEditing due dates")
	yellow.Println("\ttodo e 33 due mon")
	fmt.Println("\tEdits the todo with 33 and sets the due date to this coming Monday\n")
	yellow.Println("\ttodo e 33 due none")
	fmt.Println("\tEdits the todo with 33 and removes the due date\n")

	blueBold.Println("\nDeleting")
	yellow.Println("\ttodo d 33")
	fmt.Println("\tDeletes a todo with id 33\n")
	fmt.Println("Todolist was lovingly crafted by Grant Ammons (https://twitter.com/gammons).")
	fmt.Println("For full documentation, please visit http://todolist.site")
}

func routeInput(command string, input string) {
	app := todolist.NewApp()
	switch command {
	case "l", "list", "agenda":
		app.ListTodos(input)
	case "a", "add":
		app.AddTodo(input)
	case "d", "del":
		app.DeleteTodo(input)
	case "c", "complete":
		app.CompleteTodo(input)
	case "uc", "uncomplete":
		app.UncompleteTodo(input)
	case "ar", "archive":
		app.ArchiveTodo(input)
	case "uar", "unarchive":
		app.UnarchiveTodo(input)
	case "ac":
		app.ArchiveCompleted()
	case "e", "edit":
		app.EditTodoDue(input)
	case "init":
		app.InitializeRepo()
	case "web":
		if err := app.Load(); err != nil {
			os.Exit(1)
		} else {
			web := todolist.NewWebapp()
			fmt.Println("Now serving todolist web.\nHead to http://localhost:7890 to see your todo list!")
			open.Start("http://localhost:7890")
			web.Run()
		}
	}
}
<commit_msg>Fix delete subcommand to match website<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/gammons/todolist/todolist"
	"github.com/skratchdot/open-golang/open"
)

const (
	VERSION = "0.3.1"
)

func main() {
	if len(os.Args) <= 1 {
		usage()
		os.Exit(0)
	}
	input := strings.Join(os.Args[1:], " ")
	routeInput(os.Args[1], input)
}

func usage() {
	blue := color.New(color.FgBlue)
	cyan := color.New(color.FgCyan)
	yellow := color.New(color.FgYellow)

	blueBold := blue.Add(color.Bold)

	fmt.Printf("todo v%s, a simple, command line based, GTD-style todo manager\n", VERSION)

	blueBold.Println("\nAdding todos")
	fmt.Println("  the 'a' command adds todos.")
	fmt.Println("  You can also optionally specify a due date.")
	fmt.Println("  Specify a due date by putting 'due <date>' at the end, where <date> is in (tod|today|tom|tomorrow|mon|tue|wed|thu|fri|sat|sun)")
	fmt.Println("\n  Examples for adding a todo:")
	yellow.Println("\ttodo a Meeting with @bob about +importantPrject due today")
	yellow.Println("\ttodo a +work +verify did @john fix the build\\?")

	blueBold.Println("\nListing todos")
	fmt.Println("  When listing todos, you can filter and group the output.\n")

	fmt.Println("  todo l due (tod|today|tom|tomorrow|overdue|this week|next week|mon|tue|wed|thu|fri|sat|sun|none)")
	fmt.Println("  todo l overdue")

	cyan.Println("  Filtering by date:\n")
	yellow.Println("\ttodo l due tod")
	fmt.Println("\tlists all todos due today\n")
	yellow.Println("\ttodo l due tom")
	fmt.Println("\tlists all todos due tomorrow\n")
	yellow.Println("\ttodo l due mon")
	fmt.Println("\tlists all todos due monday\n")
	yellow.Println("\ttodo l overdue")
	fmt.Println("\tlists all todos where the due date is in the past\n")
	yellow.Println("\ttodo agenda")
	fmt.Println("\tlists all todos where the due date is today or in the past\n")

	cyan.Println("  Grouping:")
	fmt.Println("  You can group todos by context or project.")
	yellow.Println("\ttodo l by c")
	fmt.Println("\tlists all todos grouped by context\n")
	yellow.Println("\ttodo l by p")
	fmt.Println("\tlists all todos grouped by project\n")

	cyan.Println("  Grouping and filtering:")
	fmt.Println("  Of course, you can combine grouping and filtering to get a nice formatted list.\n")
	yellow.Println("\ttodo l due today by c")
	fmt.Println("\tlists all todos due today grouped by context\n")
	yellow.Println("\ttodo l +project due this week by c")
	fmt.Println("\tlists all todos due today for +project, grouped by context\n")
	yellow.Println("\ttodo l @frank due tom by p")
	fmt.Println("\tlists all todos due tomorrow concerining @frank for +project, grouped by project\n")

	blueBold.Println("\nCompleting and uncompleting ")
	fmt.Println("Complete and Uncomplete a todo by its Id:\n")
	yellow.Println("\ttodo c 33")
	fmt.Println("\tCompletes a todo with id 33\n")
	yellow.Println("\ttodo uc 33")
	fmt.Println("\tUncompletes a todo with id 33\n")

	blueBold.Println("\nArchiving")
	fmt.Println("You can archive todos once they are done, or if you might come back to them.")
	fmt.Println("By default, todo will only show unarchived todos.\n")
	yellow.Println("\ttodo ar 33")
	fmt.Println("\tArchives a todo with id 33\n")
	yellow.Println("\ttodo ac")
	fmt.Println("\tArchives all completed todos\n")
	yellow.Println("\ttodo l archived")
	fmt.Println("\tlist all archived todos\n")

	blueBold.Println("\nEditing due dates")
	yellow.Println("\ttodo e 33 due mon")
	fmt.Println("\tEdits the todo with 33 and sets the due date to this coming Monday\n")
	yellow.Println("\ttodo e 33 due none")
	fmt.Println("\tEdits the todo with 33 and removes the due date\n")

	blueBold.Println("\nDeleting")
	yellow.Println("\ttodo d 33")
	fmt.Println("\tDeletes a todo with id 33\n")
	fmt.Println("Todolist was lovingly crafted by Grant Ammons (https://twitter.com/gammons).")
	fmt.Println("For full documentation, please visit http://todolist.site")
}

func routeInput(command string, input string) {
	app := todolist.NewApp()
	switch command {
	case "l", "list", "agenda":
		app.ListTodos(input)
	case "a", "add":
		app.AddTodo(input)
	case "d", "delete":
		app.DeleteTodo(input)
	case "c", "complete":
		app.CompleteTodo(input)
	case "uc", "uncomplete":
		app.UncompleteTodo(input)
	case "ar", "archive":
		app.ArchiveTodo(input)
	case "uar", "unarchive":
		app.UnarchiveTodo(input)
	case "ac":
		app.ArchiveCompleted()
	case "e", "edit":
		app.EditTodoDue(input)
	case "init":
		app.InitializeRepo()
	case "web":
		if err := app.Load(); err != nil {
			os.Exit(1)
		} else {
			web := todolist.NewWebapp()
			fmt.Println("Now serving todolist web.\nHead to http://localhost:7890 to see your todo list!")
			open.Start("http://localhost:7890")
			web.Run()
		}
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	dc "github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/terraform/helper/schema"
)

var (
	creationTime time.Time
)

func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error {
	var err error
	client := meta.(*dc.Client)

	var data Data
	if err := fetchLocalImages(&data, client); err != nil {
		return err
	}

	image := d.Get("image").(string)
	if _, ok := data.DockerImages[image]; !ok {
		if _, ok := data.DockerImages[image+":latest"]; !ok {
			return fmt.Errorf("Unable to find image %s", image)
		}
		image = image + ":latest"
	}

	// The awesome, wonderful, splendiferous, sensical
	// Docker API now lets you specify a HostConfig in
	// CreateContainerOptions, but in my testing it still only
	// actually applies HostConfig options set in StartContainer.
	// How cool is that?
	createOpts := dc.CreateContainerOptions{
		Name: d.Get("name").(string),
		Config: &dc.Config{
			Image:      image,
			Hostname:   d.Get("hostname").(string),
			Domainname: d.Get("domainname").(string),
		},
	}

	if v, ok := d.GetOk("env"); ok {
		createOpts.Config.Env = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("command"); ok {
		createOpts.Config.Cmd = stringListToStringSlice(v.([]interface{}))
	}

	if v, ok := d.GetOk("entrypoint"); ok {
		createOpts.Config.Entrypoint = stringListToStringSlice(v.([]interface{}))
	}

	exposedPorts := map[dc.Port]struct{}{}
	portBindings := map[dc.Port][]dc.PortBinding{}

	if v, ok := d.GetOk("ports"); ok {
		exposedPorts, portBindings = portSetToDockerPorts(v.(*schema.Set))
	}
	if len(exposedPorts) != 0 {
		createOpts.Config.ExposedPorts = exposedPorts
	}

	volumes := map[string]struct{}{}
	binds := []string{}
	volumesFrom := []string{}

	if v, ok := d.GetOk("volumes"); ok {
		volumes, binds, volumesFrom, err = volumeSetToDockerVolumes(v.(*schema.Set))
		if err != nil {
			return fmt.Errorf("Unable to parse volumes: %s", err)
		}
	}
	if len(volumes) != 0 {
		createOpts.Config.Volumes = volumes
	}

	if v, ok := d.GetOk("labels"); ok {
		createOpts.Config.Labels = mapTypeMapValsToString(v.(map[string]interface{}))
	}

	hostConfig := &dc.HostConfig{
		Privileged:      d.Get("privileged").(bool),
		PublishAllPorts: d.Get("publish_all_ports").(bool),
		RestartPolicy: dc.RestartPolicy{
			Name:              d.Get("restart").(string),
			MaximumRetryCount: d.Get("max_retry_count").(int),
		},
		LogConfig: dc.LogConfig{
			Type: d.Get("log_driver").(string),
		},
	}

	if len(portBindings) != 0 {
		hostConfig.PortBindings = portBindings
	}

	if len(binds) != 0 {
		hostConfig.Binds = binds
	}
	if len(volumesFrom) != 0 {
		hostConfig.VolumesFrom = volumesFrom
	}

	if v, ok := d.GetOk("dns"); ok {
		hostConfig.DNS = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("links"); ok {
		hostConfig.Links = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("memory"); ok {
		hostConfig.Memory = int64(v.(int)) * 1024 * 1024
	}

	if v, ok := d.GetOk("memory_swap"); ok {
		swap := int64(v.(int))
		if swap > 0 {
			swap = swap * 1024 * 1024
		}
		hostConfig.MemorySwap = swap
	}

	if v, ok := d.GetOk("cpu_shares"); ok {
		hostConfig.CPUShares = int64(v.(int))
	}

	if v, ok := d.GetOk("log_opts"); ok {
		hostConfig.LogConfig.Config = mapTypeMapValsToString(v.(map[string]interface{}))
	}

	createOpts.HostConfig = hostConfig

	var retContainer *dc.Container
	if retContainer, err = client.CreateContainer(createOpts); err != nil {
		return fmt.Errorf("Unable to create container: %s", err)
	}
	if retContainer == nil {
		return fmt.Errorf("Returned container is nil")
	}

	d.SetId(retContainer.ID)

	if v, ok := d.GetOk("networks"); ok {
		connectionOpts := &dc.NetworkConnectionOptions{Container: retContainer.ID}

		for _, network := range v.(*schema.Set).List() {
			client.ConnectNetwork(network.(string), connectionOpts)
		}
	}

	creationTime = time.Now()
	if err := client.StartContainer(retContainer.ID, hostConfig); err != nil {
		return fmt.Errorf("Unable to start container: %s", err)
	}

	return resourceDockerContainerRead(d, meta)
}

func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*dc.Client)

	apiContainer, err := fetchDockerContainer(d.Id(), client)
	if err != nil {
		return err
	}
	if apiContainer == nil {
		// This container doesn't exist anymore
		d.SetId("")
		return nil
	}

	var container *dc.Container

	loops := 1 // if it hasn't just been created, don't delay
	if !creationTime.IsZero() {
		loops = 30 // with 500ms spacing, 15 seconds; ought to be plenty
	}
	sleepTime := 500 * time.Millisecond

	for i := loops; i > 0; i-- {
		container, err = client.InspectContainer(apiContainer.ID)
		if err != nil {
			return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err)
		}

		if container.State.Running ||
			!container.State.Running && !d.Get("must_run").(bool) {
			break
		}

		if creationTime.IsZero() { // We didn't just create it, so don't wait around
			return resourceDockerContainerDelete(d, meta)
		}

		if container.State.FinishedAt.After(creationTime) {
			// It exited immediately, so error out so dependent containers
			// aren't started
			resourceDockerContainerDelete(d, meta)
			return fmt.Errorf("Container %s exited after creation, error was: %s", apiContainer.ID, container.State.Error)
		}

		time.Sleep(sleepTime)
	}

	// Handle the case of the for loop above running its course
	if !container.State.Running && d.Get("must_run").(bool) {
		resourceDockerContainerDelete(d, meta)
		return fmt.Errorf("Container %s failed to be in running state", apiContainer.ID)
	}

	// Read Network Settings
	if container.NetworkSettings != nil {
		d.Set("ip_address", container.NetworkSettings.IPAddress)
		d.Set("ip_prefix_length", container.NetworkSettings.IPPrefixLen)
		d.Set("gateway", container.NetworkSettings.Gateway)
		d.Set("bridge", container.NetworkSettings.Bridge)
	}

	return nil
}

func resourceDockerContainerUpdate(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*dc.Client)

	removeOpts := dc.RemoveContainerOptions{
		ID:            d.Id(),
		RemoveVolumes: true,
		Force:         true,
	}

	if err := client.RemoveContainer(removeOpts); err != nil {
		return fmt.Errorf("Error deleting container %s: %s", d.Id(), err)
	}

	d.SetId("")
	return nil
}

func stringListToStringSlice(stringList []interface{}) []string {
	ret := []string{}
	for _, v := range stringList {
		ret = append(ret, v.(string))
	}
	return ret
}

func stringSetToStringSlice(stringSet *schema.Set) []string {
	ret := []string{}
	if stringSet == nil {
		return ret
	}
	for _, envVal := range stringSet.List() {
		ret = append(ret, envVal.(string))
	}
	return ret
}

func mapTypeMapValsToString(typeMap map[string]interface{}) map[string]string {
	mapped := make(map[string]string, len(typeMap))
	for k, v := range typeMap {
		mapped[k] = v.(string)
	}
	return mapped
}

func fetchDockerContainer(ID string, client *dc.Client) (*dc.APIContainers, error) {
	apiContainers, err := client.ListContainers(dc.ListContainersOptions{All: true})

	if err != nil {
		return nil, fmt.Errorf("Error fetching container information from Docker: %s\n", err)
	}

	for _, apiContainer := range apiContainers {
		if apiContainer.ID == ID {
			return &apiContainer, nil
		}
	}

	return nil, nil
}

func portSetToDockerPorts(ports *schema.Set) (map[dc.Port]struct{}, map[dc.Port][]dc.PortBinding) {
	retExposedPorts := map[dc.Port]struct{}{}
	retPortBindings := map[dc.Port][]dc.PortBinding{}

	for _, portInt := range ports.List() {
		port := portInt.(map[string]interface{})
		internal := port["internal"].(int)
		protocol := port["protocol"].(string)

		exposedPort := dc.Port(strconv.Itoa(internal) + "/" + protocol)
		retExposedPorts[exposedPort] = struct{}{}

		external, extOk := port["external"].(int)
		ip, ipOk := port["ip"].(string)

		if extOk {
			portBinding := dc.PortBinding{
				HostPort: strconv.Itoa(external),
			}
			if ipOk {
				portBinding.HostIP = ip
			}
			retPortBindings[exposedPort] = append(retPortBindings[exposedPort], portBinding)
		}
	}

	return retExposedPorts, retPortBindings
}

func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []string, []string, error) {
	retVolumeMap := map[string]struct{}{}
	retHostConfigBinds := []string{}
	retVolumeFromContainers := []string{}

	for _, volumeInt := range volumes.List() {
		volume := volumeInt.(map[string]interface{})
		fromContainer := volume["from_container"].(string)
		containerPath := volume["container_path"].(string)
		hostPath := volume["host_path"].(string)
		readOnly := volume["read_only"].(bool)

		switch {
		case len(fromContainer) == 0 && len(containerPath) == 0:
			return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Volume entry without container path or source container")
		case len(fromContainer) != 0 && len(containerPath) != 0:
			return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Both a container and a path specified in a volume entry")
		case len(fromContainer) != 0:
			retVolumeFromContainers = append(retVolumeFromContainers, fromContainer)
		case len(hostPath) != 0:
			readWrite := "rw"
			if readOnly {
				readWrite = "ro"
			}
			retVolumeMap[containerPath] = struct{}{}
			retHostConfigBinds = append(retHostConfigBinds, hostPath+":"+containerPath+":"+readWrite)
		default:
			retVolumeMap[containerPath] = struct{}{}
		}
	}

	return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil
}
<commit_msg>Fix typo<commit_after>package docker

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	dc "github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/terraform/helper/schema"
)

var (
	creationTime time.Time
)

func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error {
	var err error
	client := meta.(*dc.Client)

	var data Data
	if err := fetchLocalImages(&data, client); err != nil {
		return err
	}

	image := d.Get("image").(string)
	if _, ok := data.DockerImages[image]; !ok {
		if _, ok := data.DockerImages[image+":latest"]; !ok {
			return fmt.Errorf("Unable to find image %s", image)
		}
		image = image + ":latest"
	}

	// The awesome, wonderful, splendiferous, sensical
	// Docker API now lets you specify a HostConfig in
	// CreateContainerOptions, but in my testing it still only
	// actually applies HostConfig options set in StartContainer.
	// How cool is that?
	createOpts := dc.CreateContainerOptions{
		Name: d.Get("name").(string),
		Config: &dc.Config{
			Image:      image,
			Hostname:   d.Get("hostname").(string),
			Domainname: d.Get("domainname").(string),
		},
	}

	if v, ok := d.GetOk("env"); ok {
		createOpts.Config.Env = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("command"); ok {
		createOpts.Config.Cmd = stringListToStringSlice(v.([]interface{}))
	}

	if v, ok := d.GetOk("entrypoint"); ok {
		createOpts.Config.Entrypoint = stringListToStringSlice(v.([]interface{}))
	}

	exposedPorts := map[dc.Port]struct{}{}
	portBindings := map[dc.Port][]dc.PortBinding{}

	if v, ok := d.GetOk("ports"); ok {
		exposedPorts, portBindings = portSetToDockerPorts(v.(*schema.Set))
	}
	if len(exposedPorts) != 0 {
		createOpts.Config.ExposedPorts = exposedPorts
	}

	volumes := map[string]struct{}{}
	binds := []string{}
	volumesFrom := []string{}

	if v, ok := d.GetOk("volumes"); ok {
		volumes, binds, volumesFrom, err = volumeSetToDockerVolumes(v.(*schema.Set))
		if err != nil {
			return fmt.Errorf("Unable to parse volumes: %s", err)
		}
	}
	if len(volumes) != 0 {
		createOpts.Config.Volumes = volumes
	}

	if v, ok := d.GetOk("labels"); ok {
		createOpts.Config.Labels = mapTypeMapValsToString(v.(map[string]interface{}))
	}

	hostConfig := &dc.HostConfig{
		Privileged:      d.Get("privileged").(bool),
		PublishAllPorts: d.Get("publish_all_ports").(bool),
		RestartPolicy: dc.RestartPolicy{
			Name:              d.Get("restart").(string),
			MaximumRetryCount: d.Get("max_retry_count").(int),
		},
		LogConfig: dc.LogConfig{
			Type: d.Get("log_driver").(string),
		},
	}

	if len(portBindings) != 0 {
		hostConfig.PortBindings = portBindings
	}

	if len(binds) != 0 {
		hostConfig.Binds = binds
	}
	if len(volumesFrom) != 0 {
		hostConfig.VolumesFrom = volumesFrom
	}

	if v, ok := d.GetOk("dns"); ok {
		hostConfig.DNS = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("links"); ok {
		hostConfig.Links = stringSetToStringSlice(v.(*schema.Set))
	}

	if v, ok := d.GetOk("memory"); ok {
		hostConfig.Memory = int64(v.(int)) * 1024 * 1024
	}

	if v, ok := d.GetOk("memory_swap"); ok {
		swap := int64(v.(int))
		if swap > 0 {
			swap = swap * 1024 * 1024
		}
		hostConfig.MemorySwap = swap
	}

	if v, ok := d.GetOk("cpu_shares"); ok {
		hostConfig.CPUShares = int64(v.(int))
	}

	if v, ok := d.GetOk("log_opts"); ok {
		hostConfig.LogConfig.Config = mapTypeMapValsToString(v.(map[string]interface{}))
	}

	createOpts.HostConfig = hostConfig

	var retContainer *dc.Container
	if retContainer, err = client.CreateContainer(createOpts); err != nil {
		return fmt.Errorf("Unable to create container: %s", err)
	}
	if retContainer == nil {
		return fmt.Errorf("Returned container is nil")
	}

	d.SetId(retContainer.ID)

	if v, ok := d.GetOk("networks"); ok {
		connectionOpts := dc.NetworkConnectionOptions{Container: retContainer.ID}

		for _, network := range v.(*schema.Set).List() {
			client.ConnectNetwork(network.(string), connectionOpts)
		}
	}

	creationTime = time.Now()
	if err := client.StartContainer(retContainer.ID, hostConfig); err != nil {
		return fmt.Errorf("Unable to start container: %s", err)
	}

	return resourceDockerContainerRead(d, meta)
}

func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*dc.Client)

	apiContainer, err := fetchDockerContainer(d.Id(), client)
	if err != nil {
		return err
	}
	if apiContainer == nil {
		// This container doesn't exist anymore
		d.SetId("")
		return nil
	}

	var container *dc.Container

	loops := 1 // if it hasn't just been created, don't delay
	if !creationTime.IsZero() {
		loops = 30 // with 500ms spacing, 15 seconds; ought to be plenty
	}
	sleepTime := 500 * time.Millisecond

	for i := loops; i > 0; i-- {
		container, err = client.InspectContainer(apiContainer.ID)
		if err != nil {
			return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err)
		}

		if container.State.Running ||
			!container.State.Running && !d.Get("must_run").(bool) {
			break
		}

		if creationTime.IsZero() { // We didn't just create it, so don't wait around
			return resourceDockerContainerDelete(d, meta)
		}

		if container.State.FinishedAt.After(creationTime) {
			// It exited immediately, so error out so dependent containers
			// aren't started
			resourceDockerContainerDelete(d, meta)
			return fmt.Errorf("Container %s exited after creation, error was: %s", apiContainer.ID, container.State.Error)
		}

		time.Sleep(sleepTime)
	}

	// Handle the case of the for loop above running its course
	if !container.State.Running && d.Get("must_run").(bool) {
		resourceDockerContainerDelete(d, meta)
		return fmt.Errorf("Container %s failed to be in running state", apiContainer.ID)
	}

	// Read Network Settings
	if container.NetworkSettings != nil {
		d.Set("ip_address", container.NetworkSettings.IPAddress)
		d.Set("ip_prefix_length", container.NetworkSettings.IPPrefixLen)
		d.Set("gateway", container.NetworkSettings.Gateway)
		d.Set("bridge", container.NetworkSettings.Bridge)
	}

	return nil
}

func resourceDockerContainerUpdate(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*dc.Client)

	removeOpts := dc.RemoveContainerOptions{
		ID:            d.Id(),
		RemoveVolumes: true,
		Force:         true,
	}

	if err := client.RemoveContainer(removeOpts); err != nil {
		return fmt.Errorf("Error deleting container %s: %s", d.Id(), err)
	}

	d.SetId("")
	return nil
}

func stringListToStringSlice(stringList []interface{}) []string {
	ret := []string{}
	for _, v := range stringList {
		ret = append(ret, v.(string))
	}
	return ret
}

func stringSetToStringSlice(stringSet *schema.Set) []string {
	ret := []string{}
	if stringSet == nil {
		return ret
	}
	for _, envVal := range stringSet.List() {
		ret = append(ret, envVal.(string))
	}
	return ret
}

func mapTypeMapValsToString(typeMap map[string]interface{}) map[string]string {
	mapped := make(map[string]string, len(typeMap))
	for k, v := range typeMap {
		mapped[k] = v.(string)
	}
	return mapped
}

func fetchDockerContainer(ID string, client *dc.Client) (*dc.APIContainers, error) {
	apiContainers, err := client.ListContainers(dc.ListContainersOptions{All: true})

	if err != nil {
		return nil, fmt.Errorf("Error fetching container information from Docker: %s\n", err)
	}

	for _, apiContainer := range apiContainers {
		if apiContainer.ID == ID {
			return &apiContainer, nil
		}
	}

	return nil, nil
}

func portSetToDockerPorts(ports *schema.Set) (map[dc.Port]struct{}, map[dc.Port][]dc.PortBinding) {
	retExposedPorts := map[dc.Port]struct{}{}
	retPortBindings := map[dc.Port][]dc.PortBinding{}

	for _, portInt := range ports.List() {
		port := portInt.(map[string]interface{})
		internal := port["internal"].(int)
		protocol := port["protocol"].(string)

		exposedPort := dc.Port(strconv.Itoa(internal) + "/" + protocol)
		retExposedPorts[exposedPort] = struct{}{}

		external, extOk := port["external"].(int)
		ip, ipOk := port["ip"].(string)

		if extOk {
			portBinding := dc.PortBinding{
				HostPort: strconv.Itoa(external),
			}
			if ipOk {
				portBinding.HostIP = ip
			}
			retPortBindings[exposedPort] = append(retPortBindings[exposedPort], portBinding)
		}
	}

	return retExposedPorts, retPortBindings
}

func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []string, []string, error) {
	retVolumeMap := map[string]struct{}{}
	retHostConfigBinds := []string{}
	retVolumeFromContainers := []string{}

	for _, volumeInt := range volumes.List() {
		volume := volumeInt.(map[string]interface{})
		fromContainer := volume["from_container"].(string)
		containerPath := volume["container_path"].(string)
		hostPath := volume["host_path"].(string)
		readOnly := volume["read_only"].(bool)

		switch {
		case len(fromContainer) == 0 && len(containerPath) == 0:
			return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Volume entry without container path or source container")
		case len(fromContainer) != 0 && len(containerPath) != 0:
			return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Both a container and a path specified in a volume entry")
		case len(fromContainer) != 0:
			retVolumeFromContainers = append(retVolumeFromContainers, fromContainer)
		case len(hostPath) != 0:
			readWrite := "rw"
			if readOnly {
				readWrite = "ro"
			}
			retVolumeMap[containerPath] = struct{}{}
			retHostConfigBinds = append(retHostConfigBinds, hostPath+":"+containerPath+":"+readWrite)
		default:
			retVolumeMap[containerPath] = struct{}{}
		}
	}

	return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil
}
<|endoftext|>