<commit_before>// +build go1.2

// Copyright 2014 Unknwon
//
// 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.

// Switch is a server that provides versioning caching and delivering Go packages service.
package main

import (
	"fmt"
	"net/http"
	"runtime"
	"strings"

	"github.com/Unknwon/macaron"
	"github.com/macaron-contrib/i18n"
	"github.com/macaron-contrib/pongo2"
	"github.com/macaron-contrib/session"

	"github.com/gpmgo/switch/modules/log"
	"github.com/gpmgo/switch/modules/middleware"
	_ "github.com/gpmgo/switch/modules/qiniu"
	"github.com/gpmgo/switch/modules/setting"
	"github.com/gpmgo/switch/routers"
	"github.com/gpmgo/switch/routers/admin"
	"github.com/gpmgo/switch/routers/api/v1"
)

const APP_VER = "0.6.4.0228"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	setting.AppVer = APP_VER
}

func main() {
	log.Info("%s %s", setting.AppName, APP_VER)
	log.Info("Run Mode: %s", strings.Title(macaron.Env))

	m := macaron.New()
	m.Use(macaron.Logger())
	m.Use(macaron.Recovery())
	m.Use(macaron.Static("public", macaron.StaticOptions{
		SkipLogging: true,
	}))
	m.Use(pongo2.Pongoers(pongo2.Options{
		Directory:  "templates/web",
		IndentJSON: macaron.Env != macaron.PROD,
	}, "templates/admin"))
	m.Use(i18n.I18n())
	m.Use(session.Sessioner())
	m.Use(middleware.Contexter())

	// Routes.
	m.Get("/", routers.Home)
	m.Route("/download", "GET,POST", routers.Download)
	m.Get("/favicon.ico", func(ctx *middleware.Context) {
		ctx.Redirect("/img/favicon.png")
	})
	// m.Get("/search", routers.Search)
	// m.Get("/about", routers.About)

	// Package.
	m.Get("/*", routers.Package)
	m.Get("/badge/*", routers.Badge)

	// Admin.
	m.Post("/admin/auth", admin.AuthPost)
	m.Group("/admin", func() {
		m.Get("", admin.Dashboard)

		m.Group("/packages", func() {
			m.Get("", admin.Revisions)
			m.Get("/larges", admin.LargeRevisions)
		})

		m.Group("/blocks", func() {
			m.Get("", admin.Blocks)
			m.Combo("/new").Get(admin.BlockPackage).Post(admin.BlockPackagePost)
			m.Get("/:id:int/delete", admin.UnblockPackage)

			m.Group("/rules", func() {
				m.Get("", admin.BlockRules)
				m.Combo("/new").Get(admin.NewBlockRule).Post(admin.NewBlockRulePost)
				m.Get("/:id:int/run", admin.RunRule)
				m.Get("/:id:int/delete", admin.DeleteBlockRule)
			})
		})
	}, admin.Auth)

	// API.
	m.Group("/api", func() {
		m.Group("/v1", func() {
			m.Group("", func() {
				m.Get("/download", v1.Download)
				m.Get("/revision", v1.GetRevision)
			}, v1.PackageFilter())
		})
	})

	// Robots.txt
	m.Get("/robots.txt", func() string {
		return `User-agent: *
Disallow: /api/
Disallow: /download`
	})

	m.NotFound(routers.NotFound)

	listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HttpPort)
	log.Info("Listen: http://%s", listenAddr)
	if err := http.ListenAndServe(listenAddr, m); err != nil {
		log.Fatal(4, "Fail to start server: %v", err)
	}
}
<commit_msg>update version<commit_after>// +build go1.2

// Copyright 2014 Unknwon
//
// 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.

// Switch is a server that provides versioning caching and delivering Go packages service.
package main

import (
	"fmt"
	"net/http"
	"runtime"
	"strings"

	"github.com/Unknwon/macaron"
	"github.com/macaron-contrib/i18n"
	"github.com/macaron-contrib/pongo2"
	"github.com/macaron-contrib/session"

	"github.com/gpmgo/switch/modules/log"
	"github.com/gpmgo/switch/modules/middleware"
	_ "github.com/gpmgo/switch/modules/qiniu"
	"github.com/gpmgo/switch/modules/setting"
	"github.com/gpmgo/switch/routers"
	"github.com/gpmgo/switch/routers/admin"
	"github.com/gpmgo/switch/routers/api/v1"
)

const APP_VER = "0.6.4.0324"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	setting.AppVer = APP_VER
}

func main() {
	log.Info("%s %s", setting.AppName, APP_VER)
	log.Info("Run Mode: %s", strings.Title(macaron.Env))

	m := macaron.New()
	m.Use(macaron.Logger())
	m.Use(macaron.Recovery())
	m.Use(macaron.Static("public", macaron.StaticOptions{
		SkipLogging: true,
	}))
	m.Use(pongo2.Pongoers(pongo2.Options{
		Directory:  "templates/web",
		IndentJSON: macaron.Env != macaron.PROD,
	}, "templates/admin"))
	m.Use(i18n.I18n())
	m.Use(session.Sessioner())
	m.Use(middleware.Contexter())

	// Routes.
	m.Get("/", routers.Home)
	m.Route("/download", "GET,POST", routers.Download)
	m.Get("/favicon.ico", func(ctx *middleware.Context) {
		ctx.Redirect("/img/favicon.png")
	})
	// m.Get("/search", routers.Search)
	// m.Get("/about", routers.About)

	// Package.
	m.Get("/*", routers.Package)
	m.Get("/badge/*", routers.Badge)

	// Admin.
	m.Post("/admin/auth", admin.AuthPost)
	m.Group("/admin", func() {
		m.Get("", admin.Dashboard)

		m.Group("/packages", func() {
			m.Get("", admin.Revisions)
			m.Get("/larges", admin.LargeRevisions)
		})

		m.Group("/blocks", func() {
			m.Get("", admin.Blocks)
			m.Combo("/new").Get(admin.BlockPackage).Post(admin.BlockPackagePost)
			m.Get("/:id:int/delete", admin.UnblockPackage)

			m.Group("/rules", func() {
				m.Get("", admin.BlockRules)
				m.Combo("/new").Get(admin.NewBlockRule).Post(admin.NewBlockRulePost)
				m.Get("/:id:int/run", admin.RunRule)
				m.Get("/:id:int/delete", admin.DeleteBlockRule)
			})
		})
	}, admin.Auth)

	// API.
	m.Group("/api", func() {
		m.Group("/v1", func() {
			m.Group("", func() {
				m.Get("/download", v1.Download)
				m.Get("/revision", v1.GetRevision)
			}, v1.PackageFilter())
		})
	})

	// Robots.txt
	m.Get("/robots.txt", func() string {
		return `User-agent: *
Disallow: /api/
Disallow: /download`
	})

	m.NotFound(routers.NotFound)

	listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HttpPort)
	log.Info("Listen: http://%s", listenAddr)
	if err := http.ListenAndServe(listenAddr, m); err != nil {
		log.Fatal(4, "Fail to start server: %v", err)
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"

	"encoding/json"

	"github.com/gorilla/mux"
	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/statements"
)

// GetTables list all (or filter) tables
func GetTables(w http.ResponseWriter, r *http.Request) {
	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if order == "" {
		order = statements.TablesOrderBy
	}

	sqlTables := fmt.Sprint(
		statements.TablesSelect,
		statements.TablesWhere)

	if requestWhere != "" {
		sqlTables = fmt.Sprintf("%s AND %s", sqlTables, requestWhere)
	}

	sqlTables = fmt.Sprint(sqlTables, order)

	object, err := postgres.Query(sqlTables, values...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// GetTablesByDatabaseAndSchema list all (or filter) tables based on database and schema
func GetTablesByDatabaseAndSchema(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	requestWhere, values, err := postgres.WhereByRequest(r, 3)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables := fmt.Sprint(
		statements.SchemaTablesSelect,
		statements.SchemaTablesWhere)

	if requestWhere != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " AND ", requestWhere)
	}

	order, _ := postgres.OrderByRequest(r)
	if order != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, order)
	} else {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, statements.SchemaTablesOrderBy)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		http.Error(w, "Paging error", http.StatusBadRequest)
		return
	}

	sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " ", page)

	valuesAux := make([]interface{}, 0)
	valuesAux = append(valuesAux, database)
	valuesAux = append(valuesAux, schema)
	valuesAux = append(valuesAux, values...)

	object, err := postgres.Query(sqlSchemaTables, valuesAux...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// SelectFromTables perform select in database
func SelectFromTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	permission := postgres.TablePermissions(table, "read")
	if !permission {
		log.Println("You don't have permission for this action.")
		http.Error(w, "Unable to parse table in URI", http.StatusMethodNotAllowed)
		return
	}

	// get selected columns, "*" if empty "_columns"
	cols := ColumnsByRequest(r)
	fmt.Println(cols)
	fmt.Println(len(cols))

	cols = postgres.FieldsPermissions(table, cols, "read")
	if len(cols) == 0 {
		log.Println("You don't have permission for this action. Please check the permitted fields for this table.")
		http.Error(w, "You don't have permission for this action. Please check the permitted fields for this table.", http.StatusUnauthorized)
		return
	}

	selectStr, _ := postgres.SelectFields(cols)
	query := fmt.Sprintf("%s %s.%s.%s", selectStr, database, schema, table)

	countQuery := postgres.CountByRequest(r)
	if countQuery != "" {
		query = fmt.Sprintf("%s %s.%s.%s", countQuery, database, schema, table)
	}

	joinValues, err := postgres.JoinByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	for _, j := range joinValues {
		query = fmt.Sprint(query, j)
	}

	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSelect := query
	if requestWhere != "" {
		sqlSelect = fmt.Sprint(
			query,
			" WHERE ",
			requestWhere)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if len(order) > 0 {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, order)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		http.Error(w, "Paging error", http.StatusBadRequest)
		return
	}
	sqlSelect = fmt.Sprint(sqlSelect, " ", page)

	runQuery := postgres.Query
	if countQuery != "" {
		runQuery = postgres.QueryCount
	}

	object, err := runQuery(sqlSelect, values...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// InsertInTables perform insert in specific table
func InsertInTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}
	req := api.Request{}
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		log.Println("InsertInTables:", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	object, err := postgres.Insert(database, schema, table, req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// DeleteFromTable perform delete sql
func DeleteFromTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	object, err := postgres.Delete(database, schema, table, where, values)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// UpdateTable perform update table
func UpdateTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	req := api.Request{}
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	object, err := postgres.Update(database, schema, table, where, values, req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

func ColumnsByRequest(r *http.Request) []string {
	u, _ := url.Parse(r.URL.String())
	columnsArr := u.Query()["_select"]
	var columns []string

	for _, j := range columnsArr {
		cArgs := strings.Split(j, ",")
		for _, columnName := range cArgs {
			if len(columnName) > 0 {
				columns = append(columns, columnName)
			}
		}
	}
	if len(columns) == 0 {
		return []string{"*"}
	}
	return columns
}
<commit_msg>removed debug fmt.println's<commit_after>package controllers

import (
	"fmt"
	"log"
	"net/http"
	"strings"

	"encoding/json"

	"github.com/gorilla/mux"
	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/statements"
)

// GetTables list all (or filter) tables
func GetTables(w http.ResponseWriter, r *http.Request) {
	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if order == "" {
		order = statements.TablesOrderBy
	}

	sqlTables := fmt.Sprint(
		statements.TablesSelect,
		statements.TablesWhere)

	if requestWhere != "" {
		sqlTables = fmt.Sprintf("%s AND %s", sqlTables, requestWhere)
	}

	sqlTables = fmt.Sprint(sqlTables, order)

	object, err := postgres.Query(sqlTables, values...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// GetTablesByDatabaseAndSchema list all (or filter) tables based on database and schema
func GetTablesByDatabaseAndSchema(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	requestWhere, values, err := postgres.WhereByRequest(r, 3)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables := fmt.Sprint(
		statements.SchemaTablesSelect,
		statements.SchemaTablesWhere)

	if requestWhere != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " AND ", requestWhere)
	}

	order, _ := postgres.OrderByRequest(r)
	if order != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, order)
	} else {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, statements.SchemaTablesOrderBy)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		http.Error(w, "Paging error", http.StatusBadRequest)
		return
	}

	sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " ", page)

	valuesAux := make([]interface{}, 0)
	valuesAux = append(valuesAux, database)
	valuesAux = append(valuesAux, schema)
	valuesAux = append(valuesAux, values...)

	object, err := postgres.Query(sqlSchemaTables, valuesAux...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// SelectFromTables perform select in database
func SelectFromTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	permission := postgres.TablePermissions(table, "read")
	if !permission {
		log.Println("You don't have permission for this action.")
		http.Error(w, "Unable to parse table in URI", http.StatusMethodNotAllowed)
		return
	}

	// get selected columns, "*" if empty "_columns"
	cols := ColumnsByRequest(r)
	cols = postgres.FieldsPermissions(table, cols, "read")

	if len(cols) == 0 {
		log.Println("You don't have permission for this action. Please check the permitted fields for this table.")
		http.Error(w, "You don't have permission for this action. Please check the permitted fields for this table.", http.StatusUnauthorized)
		return
	}

	selectStr, _ := postgres.SelectFields(cols)
	query := fmt.Sprintf("%s %s.%s.%s", selectStr, database, schema, table)

	countQuery := postgres.CountByRequest(r)
	if countQuery != "" {
		query = fmt.Sprintf("%s %s.%s.%s", countQuery, database, schema, table)
	}

	joinValues, err := postgres.JoinByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	for _, j := range joinValues {
		query = fmt.Sprint(query, j)
	}

	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSelect := query
	if requestWhere != "" {
		sqlSelect = fmt.Sprint(
			query,
			" WHERE ",
			requestWhere)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if len(order) > 0 {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, order)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		http.Error(w, "Paging error", http.StatusBadRequest)
		return
	}
	sqlSelect = fmt.Sprint(sqlSelect, " ", page)

	runQuery := postgres.Query
	if countQuery != "" {
		runQuery = postgres.QueryCount
	}

	object, err := runQuery(sqlSelect, values...)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// InsertInTables perform insert in specific table
func InsertInTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}
	req := api.Request{}
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		log.Println("InsertInTables:", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	object, err := postgres.Insert(database, schema, table, req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// DeleteFromTable perform delete sql
func DeleteFromTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	object, err := postgres.Delete(database, schema, table, where, values)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

// UpdateTable perform update table
func UpdateTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database, ok := vars["database"]
	if !ok {
		log.Println("Unable to parse database in URI")
		http.Error(w, "Unable to parse database in URI", http.StatusInternalServerError)
		return
	}
	schema, ok := vars["schema"]
	if !ok {
		log.Println("Unable to parse schema in URI")
		http.Error(w, "Unable to parse schema in URI", http.StatusInternalServerError)
		return
	}
	table, ok := vars["table"]
	if !ok {
		log.Println("Unable to parse table in URI")
		http.Error(w, "Unable to parse table in URI", http.StatusInternalServerError)
		return
	}

	req := api.Request{}
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	object, err := postgres.Update(database, schema, table, where, values, req)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(object)
}

func ColumnsByRequest(r *http.Request) []string {
	u, _ := r.URL.Parse(r.URL.String())
	columnsArr := u.Query()["_select"]
	var columns []string

	for _, j := range columnsArr {
		cArgs := strings.Split(j, ",")
		for _, columnName := range cArgs {
			if len(columnName) > 0 {
				columns = append(columns, columnName)
			}
		}
	}
	if len(columns) == 0 {
		return []string{"*"}
	}
	return columns
}
<|endoftext|>


<commit_before>// Frodo implements an Event Source that supports multiple channels.
//
// Examples
//
// Basic usage of Frodo.
//
//    	es := frodo.NewEventSource()
//
//    	defer es.Shutdown()
//
//    	http.Handle("/{channel:[a-z0-9-_/]+}", es)
//
//		es.SendMessage("channel", "Hello world!")
package frodo

import (
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"
)

type Log interface {
	Info(message string, args ...interface{})
}

type defaultLog struct {
}

func (d *defaultLog) Info(message string, args ...interface{}) {}

type Client struct {
	channel string
	ip      string
	send    chan string
}

type eventMessage struct {
	channel string
	message string
}

type Channel struct {
	mutex            *sync.Mutex
	lastMessage      string
	clientsConnected map[*Client]bool
	expiration       int64
}

type EventSource struct {
	allowCors      bool
	channels       map[string]*Channel
	addClient      chan *Client
	removeClient   chan *Client
	sendMessage    chan *eventMessage
	shutdown       chan bool
	closeChannel   chan string
	log            Log
	UseLastMessage bool
}

func getIP(request *http.Request) string {
	ip := request.Header.Get("X-Forwarded-For")

	if len(ip) > 0 {
		return strings.Split(ip, ",")[0]
	}

	if ip, _, err := net.SplitHostPort(request.RemoteAddr); err == nil {
		return ip
	}

	return ""
}

func (ch *Channel) GetLastMessage() string {
	return ch.lastMessage
}

func (ch *Channel) SetLastMessage(message string) {
	ch.lastMessage = message
}

func (ch *Channel) UpdateExpiration() {
	ch.mutex.Lock()
	ch.expiration = time.Now().UnixNano()
	ch.mutex.Unlock()
}

func (ch *Channel) Expired() bool {
	if ch.expiration == 0 {
		return false
	}
	ch.mutex.Lock()
	defer ch.mutex.Unlock()
	return (time.Now().UnixNano() - ch.expiration) > (24 * int64(time.Hour))
}

// SendMessage sends a message to client.
func (c *Client) SendMessage(message string) {
	c.send <- message
}

// Channel returns the channel where this client is subscribe to.
func (c *Client) Channel() string {
	return c.channel
}

// IP return the client IP.
func (c *Client) IP() string {
	return c.ip
}

// NewEventSource creates a new Event Source with config.
func NewEventSourceConfig(allowCors, lastMessage bool, log Log) *EventSource {
	es := &EventSource{
		allowCors,
		make(map[string]*Channel),
		make(chan *Client),
		make(chan *Client),
		make(chan *eventMessage),
		make(chan bool),
		make(chan string),
		log,
		lastMessage,
	}

	go es.dispatch()

	if es.UseLastMessage {
		go es.ClearChannels()
	}

	return es
}

// NewEventSource creates a new Event Source.
func NewEventSource() *EventSource {
	return NewEventSourceConfig(true, false, &defaultLog{})
}

func (es *EventSource) ClearChannels() {
	ticker := time.NewTicker(5 * time.Minute)
	for {
		select {
		case <-ticker.C:
			es.DeleteExpired()
		}
	}
}

func (es *EventSource) DeleteExpired() {
	for key, ch := range es.channels {
		if ch.Expired() {
			es.log.Info("Channel '%s' has just expired.", key)
			es.internalCloseChannel(key)
		}
	}
}

func (es *EventSource) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	flusher, ok := response.(http.Flusher)

	if !ok {
		http.Error(response, "Streaming unsupported.", http.StatusInternalServerError)
		return
	}

	h := response.Header()

	if es.allowCors {
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("Access-Control-Allow-Methods", "GET, OPTIONS")
		h.Set("Access-Control-Allow-Headers", "Keep-Alive,X-Requested-With,Cache-Control,Content-Type,Last-Event-ID")
	}

	if request.Method == "GET" {
		h.Set("Content-Type", "text/event-stream")
		h.Set("Cache-Control", "no-cache")
		h.Set("Connection", "keep-alive")

		c := &Client{
			request.URL.Path, // Channel name is the full path.
			getIP(request),
			make(chan string),
		}

		es.addClient <- c

		notify := response.(http.CloseNotifier).CloseNotify()

		done := make(chan bool)

		go func() {
			<-notify
			es.removeClient <- c
			done <- true
		}()

		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case msg := <-c.send:
				{
					fmt.Fprintf(response, "data: %s\n\n", msg)
					flusher.Flush()
				}
			case <-ticker.C:
				{
					fmt.Fprintf(response, ": keep-alive\n\n")
					flusher.Flush()
				}
			case <-done:
				return
			}
		}
	}
}

// SendMessage broadcast a message to all clients in a channel.
func (es *EventSource) SendMessage(channel, message string) {
	msg := &eventMessage{
		channel,
		message,
	}

	es.log.Info("Adding message '%s' to the sendMessage channel '%s'.", message, channel)

	es.sendMessage <- msg

	es.log.Info("Added message '%s' to the sendMessage channel '%s'.", message, channel)
}

// Channels returns all opened channels name, or an empty array if none open.
func (es *EventSource) Channels() []string {
	channels := make([]string, 0)

	for channel, _ := range es.channels {
		channels = append(channels, channel)
	}

	return channels
}

// Channels returns true if a channel exists, false otherwise.
func (es *EventSource) ChannelExists(channel string) bool {
	_, ok := es.channels[channel]
	return ok
}

// CloseChannel closes an especific channel and all clients in it.
func (es *EventSource) CloseChannel(channel string) {
	es.closeChannel <- channel
}

// CloseChannels closes all channels and clients.
func (es *EventSource) CloseChannels() {
	for channel, _ := range es.channels {
		es.closeChannel <- channel
	}
}

// ConnectionCount returns the connection count in Event Source.
func (es *EventSource) ConnectionCount() int {
	i := 0

	for _, channel := range es.channels {
		i += len(channel.clientsConnected)
	}

	return i
}

// ConnectionCountPerChannel returns the connection count in selected channel.
func (es *EventSource) ConnectionCountPerChannel(channel string) int {
	if ch, ok := es.channels[channel]; ok {
		return len(ch.clientsConnected)
	}

	return 0
}

// Shutdown performs a graceful shutdown of Event Source.
func (es *EventSource) Shutdown() {
	es.shutdown <- true
}

func (es *EventSource) internalCloseChannel(channel string) {
	es.log.Info("Internal close channel '%s'.", channel)

	if ch, exists := es.channels[channel]; exists {
		es.log.Info("Channel deleted '%s'.", channel)

		delete(es.channels, channel)

		// Kick all clients of this channel.
		for c, _ := range ch.clientsConnected {
			ch.clientsConnected[c] = false
			delete(ch.clientsConnected, c)
			close(c.send)
		}

	}
}

// dispatch holds all Event Source channels logic.
func (es *EventSource) dispatch() {
	for {
		select {

		// New client connected.
		case c := <-es.addClient:
			ch, exists := es.channels[c.channel]

			if !exists {
				ch = &Channel{
					new(sync.Mutex),
					"",
					make(map[*Client]bool),
					0,
				}
				es.channels[c.channel] = ch
				es.log.Info("New channel '%s' created.", c.channel)
			}

			ch.UpdateExpiration()

			ch.clientsConnected[c] = true
			es.log.Info("Client '%s' connected to channel '%s'.", c.ip, c.channel)
			lastMessage := ch.GetLastMessage()

			if es.UseLastMessage && (len(lastMessage) > 0) {
				c.send <- ch.lastMessage
			}

		// Client disconnected.
		case c := <-es.removeClient:
			if ch, exists := es.channels[c.channel]; exists {
				ch.UpdateExpiration()

				if _, ok := ch.clientsConnected[c]; ok {
					delete(ch.clientsConnected, c)
					close(c.send)
				}

				es.log.Info("Client '%s' disconnected from channel '%s'.", c.ip, c.channel)
				if len(ch.clientsConnected) == 0 {
					es.log.Info("Channel '%s' has no clients. Closing channel.", c.channel)
					es.internalCloseChannel(c.channel)
				}
			}

		// Broadcast message to all clients in channel.
		case msg := <-es.sendMessage:
			es.log.Info("Broadcasting to '%s'", msg.channel)
			if ch, ok := es.channels[msg.channel]; ok {
				for c, open := range ch.clientsConnected {
					if open {
						c.send <- msg.message
					}
				}

				if es.UseLastMessage {
					ch.UpdateExpiration()

					ch.SetLastMessage(msg.message)
				}
				es.log.Info("Message sent to %d clients on channel '%s'.", len(ch.clientsConnected), msg.channel)
			} else {
				es.log.Info("Channel '%s' doesn't exists. Message not sent.", msg.channel)
			}

		// Close channel and all clients in it.
		case channel := <-es.closeChannel:
			es.log.Info("Channel '%s' about to get closed.", channel)
			es.internalCloseChannel(channel)

		// Event Source shutdown.
		case <-es.shutdown:
			es.CloseChannels()
			close(es.addClient)
			close(es.removeClient)
			close(es.sendMessage)
			close(es.shutdown)
			es.log.Info("Event Source server stoped.")
			return
		}
	}
}
<commit_msg>Logging to how many open connections the message was sent<commit_after>// Frodo implements an Event Source that supports multiple channels.
//
// Examples
//
// Basic usage of Frodo.
//
//    	es := frodo.NewEventSource()
//
//    	defer es.Shutdown()
//
//    	http.Handle("/{channel:[a-z0-9-_/]+}", es)
//
//		es.SendMessage("channel", "Hello world!")
package frodo

import (
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"
)

type Log interface {
	Info(message string, args ...interface{})
}

type defaultLog struct {
}

func (d *defaultLog) Info(message string, args ...interface{}) {}

type Client struct {
	channel string
	ip      string
	send    chan string
}

type eventMessage struct {
	channel string
	message string
}

type Channel struct {
	mutex            *sync.Mutex
	lastMessage      string
	clientsConnected map[*Client]bool
	expiration       int64
}

type EventSource struct {
	allowCors      bool
	channels       map[string]*Channel
	addClient      chan *Client
	removeClient   chan *Client
	sendMessage    chan *eventMessage
	shutdown       chan bool
	closeChannel   chan string
	log            Log
	UseLastMessage bool
}

func getIP(request *http.Request) string {
	ip := request.Header.Get("X-Forwarded-For")

	if len(ip) > 0 {
		return strings.Split(ip, ",")[0]
	}

	if ip, _, err := net.SplitHostPort(request.RemoteAddr); err == nil {
		return ip
	}

	return ""
}

func (ch *Channel) GetLastMessage() string {
	return ch.lastMessage
}

func (ch *Channel) SetLastMessage(message string) {
	ch.lastMessage = message
}

func (ch *Channel) UpdateExpiration() {
	ch.mutex.Lock()
	ch.expiration = time.Now().UnixNano()
	ch.mutex.Unlock()
}

func (ch *Channel) Expired() bool {
	if ch.expiration == 0 {
		return false
	}
	ch.mutex.Lock()
	defer ch.mutex.Unlock()
	return (time.Now().UnixNano() - ch.expiration) > (24 * int64(time.Hour))
}

// SendMessage sends a message to client.
func (c *Client) SendMessage(message string) {
	c.send <- message
}

// Channel returns the channel where this client is subscribe to.
func (c *Client) Channel() string {
	return c.channel
}

// IP return the client IP.
func (c *Client) IP() string {
	return c.ip
}

// NewEventSource creates a new Event Source with config.
func NewEventSourceConfig(allowCors, lastMessage bool, log Log) *EventSource {
	es := &EventSource{
		allowCors,
		make(map[string]*Channel),
		make(chan *Client),
		make(chan *Client),
		make(chan *eventMessage),
		make(chan bool),
		make(chan string),
		log,
		lastMessage,
	}

	go es.dispatch()

	if es.UseLastMessage {
		go es.ClearChannels()
	}

	return es
}

// NewEventSource creates a new Event Source.
func NewEventSource() *EventSource {
	return NewEventSourceConfig(true, false, &defaultLog{})
}

func (es *EventSource) ClearChannels() {
	ticker := time.NewTicker(5 * time.Minute)
	for {
		select {
		case <-ticker.C:
			es.DeleteExpired()
		}
	}
}

func (es *EventSource) DeleteExpired() {
	for key, ch := range es.channels {
		if ch.Expired() {
			es.log.Info("Channel '%s' has just expired.", key)
			es.internalCloseChannel(key)
		}
	}
}

func (es *EventSource) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	flusher, ok := response.(http.Flusher)

	if !ok {
		http.Error(response, "Streaming unsupported.", http.StatusInternalServerError)
		return
	}

	h := response.Header()

	if es.allowCors {
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("Access-Control-Allow-Methods", "GET, OPTIONS")
		h.Set("Access-Control-Allow-Headers", "Keep-Alive,X-Requested-With,Cache-Control,Content-Type,Last-Event-ID")
	}

	if request.Method == "GET" {
		h.Set("Content-Type", "text/event-stream")
		h.Set("Cache-Control", "no-cache")
		h.Set("Connection", "keep-alive")

		c := &Client{
			request.URL.Path, // Channel name is the full path.
			getIP(request),
			make(chan string),
		}

		es.addClient <- c

		notify := response.(http.CloseNotifier).CloseNotify()

		done := make(chan bool)

		go func() {
			<-notify
			es.removeClient <- c
			done <- true
		}()

		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case msg := <-c.send:
				{
					fmt.Fprintf(response, "data: %s\n\n", msg)
					flusher.Flush()
				}
			case <-ticker.C:
				{
					fmt.Fprintf(response, ": keep-alive\n\n")
					flusher.Flush()
				}
			case <-done:
				return
			}
		}
	}
}

// SendMessage broadcast a message to all clients in a channel.
func (es *EventSource) SendMessage(channel, message string) {
	msg := &eventMessage{
		channel,
		message,
	}

	es.log.Info("Adding message '%s' to the sendMessage channel '%s'.", message, channel)

	es.sendMessage <- msg

	es.log.Info("Added message '%s' to the sendMessage channel '%s'.", message, channel)
}

// Channels returns all opened channels name, or an empty array if none open.
func (es *EventSource) Channels() []string {
	channels := make([]string, 0)

	for channel, _ := range es.channels {
		channels = append(channels, channel)
	}

	return channels
}

// Channels returns true if a channel exists, false otherwise.
func (es *EventSource) ChannelExists(channel string) bool {
	_, ok := es.channels[channel]
	return ok
}

// CloseChannel closes an especific channel and all clients in it.
func (es *EventSource) CloseChannel(channel string) {
	es.closeChannel <- channel
}

// CloseChannels closes all channels and clients.
func (es *EventSource) CloseChannels() {
	for channel, _ := range es.channels {
		es.closeChannel <- channel
	}
}

// ConnectionCount returns the connection count in Event Source.
func (es *EventSource) ConnectionCount() int {
	i := 0

	for _, channel := range es.channels {
		i += len(channel.clientsConnected)
	}

	return i
}

// ConnectionCountPerChannel returns the connection count in selected channel.
func (es *EventSource) ConnectionCountPerChannel(channel string) int {
	if ch, ok := es.channels[channel]; ok {
		return len(ch.clientsConnected)
	}

	return 0
}

// Shutdown performs a graceful shutdown of Event Source.
func (es *EventSource) Shutdown() {
	es.shutdown <- true
}

func (es *EventSource) internalCloseChannel(channel string) {
	es.log.Info("Internal close channel '%s'.", channel)

	if ch, exists := es.channels[channel]; exists {
		es.log.Info("Channel deleted '%s'.", channel)

		delete(es.channels, channel)

		// Kick all clients of this channel.
		for c, _ := range ch.clientsConnected {
			ch.clientsConnected[c] = false
			delete(ch.clientsConnected, c)
			close(c.send)
		}

	}
}

// dispatch holds all Event Source channels logic.
func (es *EventSource) dispatch() {
	for {
		select {

		// New client connected.
		case c := <-es.addClient:
			ch, exists := es.channels[c.channel]

			if !exists {
				ch = &Channel{
					new(sync.Mutex),
					"",
					make(map[*Client]bool),
					0,
				}
				es.channels[c.channel] = ch
				es.log.Info("New channel '%s' created.", c.channel)
			}

			ch.UpdateExpiration()

			ch.clientsConnected[c] = true
			es.log.Info("Client '%s' connected to channel '%s'.", c.ip, c.channel)
			lastMessage := ch.GetLastMessage()

			if es.UseLastMessage && (len(lastMessage) > 0) {
				c.send <- ch.lastMessage
			}

		// Client disconnected.
		case c := <-es.removeClient:
			if ch, exists := es.channels[c.channel]; exists {
				ch.UpdateExpiration()

				if _, ok := ch.clientsConnected[c]; ok {
					delete(ch.clientsConnected, c)
					close(c.send)
				}

				es.log.Info("Client '%s' disconnected from channel '%s'.", c.ip, c.channel)
				if len(ch.clientsConnected) == 0 {
					es.log.Info("Channel '%s' has no clients. Closing channel.", c.channel)
					es.internalCloseChannel(c.channel)
				}
			}

		// Broadcast message to all clients in channel.
		case msg := <-es.sendMessage:
			es.log.Info("Broadcasting '%s' to '%s'",msg.message, msg.channel)
			openClients := 0
			if ch, ok := es.channels[msg.channel]; ok {
				for c, open := range ch.clientsConnected {
					if open {
						openClients++
						c.send <- msg.message
					}
				}

				if es.UseLastMessage {
					ch.UpdateExpiration()
					ch.SetLastMessage(msg.message)
				}

				es.log.Info("Message sent to %d open of %d connected clients on channel '%s'.", openClients, len(ch.clientsConnected), msg.channel)
			} else {
				es.log.Info("Channel '%s' doesn't exists. Message '%s' not sent.", msg.channel, msg.message)
			}

		// Close channel and all clients in it.
		case channel := <-es.closeChannel:
			es.log.Info("Channel '%s' about to get closed.", channel)
			es.internalCloseChannel(channel)

		// Event Source shutdown.
		case <-es.shutdown:
			es.CloseChannels()
			close(es.addClient)
			close(es.removeClient)
			close(es.sendMessage)
			close(es.shutdown)
			es.log.Info("Event Source server stoped.")
			return
		}
	}
}
<|endoftext|>


<commit_before>package node

import (
	"golang.org/x/crypto/ssh"
)

type SSHConnection struct {
	address NodeAddress
	session *ssh.Session
}

func (s SSHConnection) SendMap(ReachabilityMap) error {
}

func (s SSHConnection) ReachabilityMaps() <-chan ReachabilityMap {
}

func (s SSHConnection) SendReceipts() <-chan PacketReceipt {
}

func (s SSHConnection) PacketReceipts() <-chan PacketReceipt {
}

func (s SSHConnection) SendPayment(Payment) error {
}

func (s SSHConnection) Payments() <-chan Payment {
}

func (s SSHConnectoin) SendPacket(Packet) error {
}

func (s SSHConnection) Packets() <-chan Packet {
}

func (s SSHConnection) Key() PublicKey {
}

func (s SSHConnection) Close() error {
	for i := 0; i < len(ssessions); i++ {
		s.sessions[i].Close()
	}
}

func EstablishSSH(addresses []node.NodeAddress, key NodeAddress) []*SSHConnection {
	username := "node-username"
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(key),
		},
	}

	var connections []*SSHConnection
	for i := 0; i < len(addresses); i++ {
		client, err := ssh.Dial("tcp", fmt.Sprintf(string(addresses[i]), ":22"), config)
		if err != nil {
			panic("Failed to dial: " + err.Error())
		}
		session, err := client.NewSession()
		if err != nil {
			panic("Failed to create session: " + err.Error())
		}
		connection := SSHConnection{address: addresses[i], session: session}
		connections = append(connections, &connection)
	}
	return connections
}
<commit_msg>Created skeleton for SSHConnection interface, fixed SSH key authentication by creating a signer, and fixed Close() to be consistent with SSHConnection being a singular session.<commit_after>package node

import (
	"fmt"
	"golang.org/x/crypto/ssh"
)

type SSHConnection struct {
	address NodeAddress
	session *ssh.Session
}

func (s SSHConnection) SendMap(ReachabilityMap) error {
	return nil
}

func (s SSHConnection) ReachabilityMaps() <-chan ReachabilityMap {
	return nil
}

func (s SSHConnection) SendReceipts() <-chan PacketReceipt {
	return nil
}

func (s SSHConnection) PacketReceipts() <-chan PacketReceipt {
	return nil
}

func (s SSHConnection) SendPayment(Payment) error {
	return nil
}

func (s SSHConnection) Payments() <-chan Payment {
	return nil
}

func (s SSHConnection) SendPacket(Packet) error {
	return nil
}

func (s SSHConnection) Packets() <-chan Packet {
	return nil
}

func (s SSHConnection) Key() PublicKey {
	return nil
}

func (s SSHConnection) Close() error {
	err := s.session.Close()
	return err
}

func EstablishSSH(addresses []NodeAddress, key PrivateKey) []*SSHConnection {
	username := "node-username"
	signer, err := ssh.NewSignerFromKey(key)
	if err != nil {
		panic("Failed to create signer from key")
	}
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
	}

	var connections []*SSHConnection
	for i := 0; i < len(addresses); i++ {
		client, err := ssh.Dial("tcp", fmt.Sprintf(string(addresses[i]), ":22"), config)
		if err != nil {
			panic("Failed to dial: " + err.Error())
		}
		session, err := client.NewSession()
		if err != nil {
			panic("Failed to create session: " + err.Error())
		}
		connection := SSHConnection{address: addresses[i], session: session}
		connections = append(connections, &connection)
	}
	return connections
}
<|endoftext|>


<commit_before>// Package tmi is a simple library for connecting to Twitch's pseudo-IRC servers, aka Twitch Message Interface
package tmi

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"time"
)

var (
	dbg = log.New(os.Stdout, "TMI: ", log.LstdFlags)
)

const (
	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

// Send sends messages to the TMI server
func (tmi *Connection) Send(s string) {
	if !tmi.Stopped() {
		tmi.send <- s
	} else {
		dbg.Printf("unable to send %s on closed connection \n", s)
	}
}

// Sendf sends a message, with format and params, wrapper around fmt.Sprintf
func (tmi *Connection) Sendf(format string, a ...interface{}) {
	tmi.Send(fmt.Sprintf(format, a...))
}

// ReadMessage reads an incoming message from the server, blocking until a message is recieved
func (tmi *Connection) ReadMessage() (*Message, error) {
	evt, ok := <-tmi.MessageChan
	var err error
	if !ok {
		err = errors.New("read message channel closed")
	} else {
		err = nil
	}
	return evt, err
}

// Disconnect from the server
func (tmi *Connection) Disconnect() {
	if !tmi.Stopped() {
		tmi.setStopped(true)
		close(tmi.end)
		tmi.Wait()
		close(tmi.send)
		tmi.socket.Close()
		tmi.socket = nil
		close(tmi.MessageChan)
		dbg.Println("Disconnected!")
		//tmi.Error <- errors.New("Disconnect Called")
	}
}

// Reconnect to a connected server
func (tmi *Connection) Reconnect() error {
	tmi.Disconnect()
	return tmi.Connect(tmi.Nick, tmi.Token)
}

// Stopped tells us wether the client is stopped or not
func (tmi *Connection) Stopped() bool {
	tmi.Lock()
	defer tmi.Unlock()
	return tmi.stopped
}

// Update the stopped status when starting or stopping the client
func (tmi *Connection) setStopped(value bool) {
	tmi.Lock()
	tmi.stopped = value
	tmi.Unlock()
}

// The main loop to read messages from the server, runs as a goroutine.
func (tmi *Connection) readLoop() {
	defer func() {
		log.Println("Reader closed")
		tmi.Done()
	}()
	br := bufio.NewReaderSize(tmi.socket, maxMessageSize)

	for {
		select {
		case <-tmi.end:
			return
		default:
			if tmi.socket != nil {
				tmi.socket.SetReadDeadline(time.Now().Add(tmi.Timeout*2 + tmi.KeepAlive))
			}
			msg, err := br.ReadString('\n')
			if err != nil {
				tmi.Error <- err
				return
			}
			if tmi.socket != nil {
				var zero time.Time
				tmi.socket.SetReadDeadline(zero)
			}
			tmi.lastMessage = time.Now()

			if tmi.Debug {
				dbg.Print("< ", msg)
			}

			message := ParseMessage(msg)
			if message.Command == "PING" {
				tmi.Send("PONG " + message.Trailing)
				continue
			}
			tmi.MessageChan <- message
		}
	}
}

func (tmi *Connection) writeLoop() {
	defer func() {
		log.Println("Writer closed")
		tmi.Done()
	}()
	for {
		select {
		case s, ok := <-tmi.send:
			if !ok {
				tmi.Error <- errors.New("send channel closed")
				return
			}
			if tmi.socket == nil {
				tmi.Error <- errors.New("no socket to write to")
				return
			}
			if s == "" {
				continue
			}
			if tmi.Debug {
				dbg.Printf("> %s\n", s)
			}
			tmi.socket.SetWriteDeadline(time.Now().Add(tmi.Timeout))
			_, err := fmt.Fprintf(tmi.socket, "%s\r\n", s)
			var zero time.Time
			tmi.socket.SetWriteDeadline(zero)

			if err != nil {
				tmi.Error <- err
				return
			}
		case <-tmi.end:
			return
		}
	}
}

func (tmi *Connection) pingLoop() {
	defer func() {
		log.Println("Pinger stopped")
		tmi.Done()
	}()
	ticker := time.NewTicker(tmi.Timeout) // Tick for monitoring
	for {
		select {
		case _, ok := <-ticker.C:
			if !ok {
				// The ticker has been closed, probably shouldn't happen before tmi.end is closed
				log.Println("ticker not ok")
				return
			}

			//Ping if we haven't received anything from the server within the keep alive period
			if time.Since(tmi.lastMessage) >= tmi.KeepAlive {
				tmi.Sendf("PING %d", time.Now().UnixNano())
			}
		case <-tmi.end:
			ticker.Stop()
			return
		}
	}
}

func (tmi *Connection) controlLoop() {
	for !tmi.Stopped() {
		err := <-tmi.Error
		if tmi.Stopped() {
			// If one of the goroutines errors when we've already stopped,
			// There's no need to handle the error.
			continue
		}
		log.Printf("Error, disconnecting: %s\n", err)
		tmi.Done()
		log.Println("controlLoop done, waiting for disconnect")
		tmi.Disconnect()
	}
	log.Println("controlloop terminated")
}

// Join joins a channel
func (tmi *Connection) Join(channel string) {
	tmi.Send("JOIN " + channel)
}

// Connect connects to the server, starts reading and authenticates
func (tmi *Connection) Connect(nick, token string) (err error) {
	if len(nick) == 0 {
		return errors.New("empty 'nick'")
	}
	if len(token) == 0 {
		return errors.New("empty 'token'")
	}
	tmi.Nick = nick
	tmi.Token = token

	if tmi.Token[0:6] == "oauth:" {
		tmi.Token = tmi.Token[6:]
	}

	tmi.socket, err = net.DialTimeout("tcp", tmi.Server+":"+tmi.Port, tmi.Timeout)
	if err != nil {
		return err
	}

	log.Printf("Connected to TMI server %s (%s)\n", tmi.Server, tmi.socket.RemoteAddr())
	tmi.Lock()
	tmi.end = make(chan bool)
	tmi.send = make(chan string, 10)
	tmi.MessageChan = make(chan *Message, 50)
	tmi.stopped = false
	tmi.Unlock()
	tmi.Add(4)
	go tmi.readLoop()
	go tmi.writeLoop()
	go tmi.pingLoop()
	go tmi.controlLoop()

	//Authenticate
	tmi.Send("PASS oauth:" + tmi.Token)
	tmi.Send("NICK " + tmi.Nick)
	tmi.Send("CAP REQ :twitch.tv/tags twitch.tv/commands")

	return nil
}

// New returns a new connection object, ready to connect
func New() *Connection {
	tmi := &Connection{
		Server:    "irc.twitch.tv",
		Port:      "6667",
		Debug:     false,
		socket:    nil,
		stopped:   true,
		Timeout:   15 * time.Second,
		KeepAlive: 30 * time.Second,
		Error:     make(chan error, 3),
	}
	return tmi
}
<commit_msg>Convenience method for quick connecting<commit_after>// Package tmi is a simple library for connecting to Twitch's pseudo-IRC servers, aka Twitch Message Interface
package tmi

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"time"
)

var (
	dbg = log.New(os.Stdout, "TMI: ", log.LstdFlags)
)

const (
	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

// Send sends messages to the TMI server
func (tmi *Connection) Send(s string) {
	if !tmi.Stopped() {
		tmi.send <- s
	} else {
		dbg.Printf("unable to send %s on closed connection \n", s)
	}
}

// Sendf sends a message, with format and params, wrapper around fmt.Sprintf
func (tmi *Connection) Sendf(format string, a ...interface{}) {
	tmi.Send(fmt.Sprintf(format, a...))
}

// ReadMessage reads an incoming message from the server, blocking until a message is recieved
func (tmi *Connection) ReadMessage() (*Message, error) {
	evt, ok := <-tmi.MessageChan
	var err error
	if !ok {
		err = errors.New("read message channel closed")
	} else {
		err = nil
	}
	return evt, err
}

// Disconnect from the server
func (tmi *Connection) Disconnect() {
	if !tmi.Stopped() {
		tmi.setStopped(true)
		close(tmi.end)
		tmi.Wait()
		close(tmi.send)
		tmi.socket.Close()
		tmi.socket = nil
		close(tmi.MessageChan)
		dbg.Println("Disconnected!")
		//tmi.Error <- errors.New("Disconnect Called")
	}
}

// Reconnect to a connected server
func (tmi *Connection) Reconnect() error {
	tmi.Disconnect()
	return tmi.Connect(tmi.Nick, tmi.Token)
}

// Stopped tells us wether the client is stopped or not
func (tmi *Connection) Stopped() bool {
	tmi.Lock()
	defer tmi.Unlock()
	return tmi.stopped
}

// Update the stopped status when starting or stopping the client
func (tmi *Connection) setStopped(value bool) {
	tmi.Lock()
	tmi.stopped = value
	tmi.Unlock()
}

// The main loop to read messages from the server, runs as a goroutine.
func (tmi *Connection) readLoop() {
	defer func() {
		log.Println("Reader closed")
		tmi.Done()
	}()
	br := bufio.NewReaderSize(tmi.socket, maxMessageSize)

	for {
		select {
		case <-tmi.end:
			return
		default:
			if tmi.socket != nil {
				tmi.socket.SetReadDeadline(time.Now().Add(tmi.Timeout*2 + tmi.KeepAlive))
			}
			msg, err := br.ReadString('\n')
			if err != nil {
				tmi.Error <- err
				return
			}
			if tmi.socket != nil {
				var zero time.Time
				tmi.socket.SetReadDeadline(zero)
			}
			tmi.lastMessage = time.Now()

			if tmi.Debug {
				dbg.Print("< ", msg)
			}

			message := ParseMessage(msg)
			if message.Command == "PING" {
				tmi.Send("PONG " + message.Trailing)
				continue
			}
			tmi.MessageChan <- message
		}
	}
}

func (tmi *Connection) writeLoop() {
	defer func() {
		log.Println("Writer closed")
		tmi.Done()
	}()
	for {
		select {
		case s, ok := <-tmi.send:
			if !ok {
				tmi.Error <- errors.New("send channel closed")
				return
			}
			if tmi.socket == nil {
				tmi.Error <- errors.New("no socket to write to")
				return
			}
			if s == "" {
				continue
			}
			if tmi.Debug {
				dbg.Printf("> %s\n", s)
			}
			tmi.socket.SetWriteDeadline(time.Now().Add(tmi.Timeout))
			_, err := fmt.Fprintf(tmi.socket, "%s\r\n", s)
			var zero time.Time
			tmi.socket.SetWriteDeadline(zero)

			if err != nil {
				tmi.Error <- err
				return
			}
		case <-tmi.end:
			return
		}
	}
}

func (tmi *Connection) pingLoop() {
	defer func() {
		log.Println("Pinger stopped")
		tmi.Done()
	}()
	ticker := time.NewTicker(tmi.Timeout) // Tick for monitoring
	for {
		select {
		case _, ok := <-ticker.C:
			if !ok {
				// The ticker has been closed, probably shouldn't happen before tmi.end is closed
				log.Println("ticker not ok")
				return
			}

			//Ping if we haven't received anything from the server within the keep alive period
			if time.Since(tmi.lastMessage) >= tmi.KeepAlive {
				tmi.Sendf("PING %d", time.Now().UnixNano())
			}
		case <-tmi.end:
			ticker.Stop()
			return
		}
	}
}

func (tmi *Connection) controlLoop() {
	for !tmi.Stopped() {
		err := <-tmi.Error
		if tmi.Stopped() {
			// If one of the goroutines errors when we've already stopped,
			// There's no need to handle the error.
			continue
		}
		log.Printf("Error, disconnecting: %s\n", err)
		tmi.Done()
		log.Println("controlLoop done, waiting for disconnect")
		tmi.Disconnect()
	}
	log.Println("controlloop terminated")
}

// Join joins a channel
func (tmi *Connection) Join(channel string) {
	tmi.Send("JOIN " + channel)
}

// Connect connects to the server, starts reading and authenticates
func (tmi *Connection) Connect(nick, token string) (err error) {
	if len(nick) == 0 {
		return errors.New("empty 'nick'")
	}
	if len(token) == 0 {
		return errors.New("empty 'token'")
	}
	tmi.Nick = nick
	tmi.Token = token

	if tmi.Token[0:6] == "oauth:" {
		tmi.Token = tmi.Token[6:]
	}

	tmi.socket, err = net.DialTimeout("tcp", tmi.Server+":"+tmi.Port, tmi.Timeout)
	if err != nil {
		return err
	}

	log.Printf("Connected to TMI server %s (%s)\n", tmi.Server, tmi.socket.RemoteAddr())
	tmi.Lock()
	tmi.end = make(chan bool)
	tmi.send = make(chan string, 10)
	tmi.MessageChan = make(chan *Message, 50)
	tmi.stopped = false
	tmi.Unlock()
	tmi.Add(4)
	go tmi.readLoop()
	go tmi.writeLoop()
	go tmi.pingLoop()
	go tmi.controlLoop()

	//Authenticate
	tmi.Send("PASS oauth:" + tmi.Token)
	tmi.Send("NICK " + tmi.Nick)
	tmi.Send("CAP REQ :twitch.tv/tags twitch.tv/commands")

	return nil
}

// New returns a new connection object, ready to connect
func New() *Connection {
	tmi := &Connection{
		Server:    "irc.twitch.tv",
		Port:      "6667",
		Debug:     false,
		socket:    nil,
		stopped:   true,
		Timeout:   15 * time.Second,
		KeepAlive: 30 * time.Second,
		Error:     make(chan error, 3),
	}
	return tmi
}
// Connect is a shortcut to creating a new connection, connecting to it and returns the connection
func Connect(nick, token string) *Connection {
	new := New()
	new.Connect(nick, token)
	return new
}<|endoftext|>


<commit_before>package main

// URL matching and regular expressions

import (
	"log"
	"net/url"
	"regexp"
	"strings"
)

type regexRule struct {
	rule
	*regexp.Regexp
}

// A regexMap is a set of regular-expression rules.
// As an optimization, it uses Aho-Corasick string matching find which regular
// expressions might match—instead of trying them all.
type regexMap struct {
	prefixList phraseList
	rules      map[string][]regexRule
}

func newRegexMap() *regexMap {
	return &regexMap{
		prefixList: newPhraseList(),
		rules:      make(map[string][]regexRule),
	}
}

func (rm *regexMap) findMatches(s string, tally map[rule]int) {
	tried := map[string]bool{}
	scanner := newPhraseScanner(rm.prefixList, func(prefix string) {
		if tried[prefix] {
			return
		}
		for _, r := range rm.rules[prefix] {
			if r.MatchString(s) {
				tally[r.rule] = 1
			}
		}
		tried[prefix] = true
	})

	for i := 0; i < len(s); i++ {
		scanner.scanByte(s[i])
	}

	// Now try the regexes that have no literal string prefix.
	for _, r := range rm.rules[""] {
		if r.MatchString(s) {
			tally[r.rule] = 1
		}
	}
}

// addRule adds a rule to the map.
func (rm *regexMap) addRule(r rule) {
	s := r.content

	re, err := regexp.Compile(s)
	if err != nil {
		log.Printf("Error parsing URL regular expression %s: %v", r, err)
		return
	}

	prefix, _ := re.LiteralPrefix()
	if prefix != "" {
		rm.prefixList.addPhrase(prefix)
	}
	rm.rules[prefix] = append(rm.rules[prefix], regexRule{r, re})
}

type URLMatcher struct {
	fragments    map[string]rule // a set of domain or domain+path URL fragments to test against
	regexes      *regexMap       // to match whole URL
	hostRegexes  *regexMap       // to match hostname only
	pathRegexes  *regexMap
	queryRegexes *regexMap
}

// finalize should be called after all rules have been added, but before
// using the URLMatcher.
func (m *URLMatcher) finalize() {
	m.regexes.prefixList.findFallbackNodes(0, nil)
	m.hostRegexes.prefixList.findFallbackNodes(0, nil)
	m.pathRegexes.prefixList.findFallbackNodes(0, nil)
	m.queryRegexes.prefixList.findFallbackNodes(0, nil)
}

func newURLMatcher() *URLMatcher {
	m := new(URLMatcher)
	m.fragments = make(map[string]rule)
	m.regexes = newRegexMap()
	m.hostRegexes = newRegexMap()
	m.pathRegexes = newRegexMap()
	m.queryRegexes = newRegexMap()
	return m
}

// AddRule adds a rule to the matcher (unless it's already there).
func (m *URLMatcher) AddRule(r rule) {
	switch r.t {
	case urlMatch:
		m.fragments[r.content] = r
	case urlRegex:
		m.regexes.addRule(r)
	case hostRegex:
		m.hostRegexes.addRule(r)
	case pathRegex:
		m.pathRegexes.addRule(r)
	case queryRegex:
		m.queryRegexes.addRule(r)
	}
}

// MatchingRules returns a list of the rules that u matches.
// For consistency with phrase matching, it is a map with rules for keys
// and with all values equal to 1.
func (m *URLMatcher) MatchingRules(u *url.URL) map[rule]int {
	result := make(map[rule]int)

	host := strings.ToLower(u.Host)
	path := strings.ToLower(u.Path)

	// strip off the port number, if present
	colon := strings.LastIndex(host, ":")
	if colon != -1 {
		host = host[:colon]
	}

	m.regexes.findMatches(strings.ToLower(u.String()), result)
	m.hostRegexes.findMatches(strings.ToLower(u.Scheme) + "://" + host, result)
	m.pathRegexes.findMatches(path, result)
	m.queryRegexes.findMatches(strings.ToLower(u.RawQuery), result)

	// Test for matches of the host and of the domains it belongs to.
	s := host
	for {
		// Test for matches with the path.
		s2 := s + path
		for {
			if r, ok := m.fragments[s2]; ok {
				result[r] = 1
			}
			slash := strings.LastIndex(s2[:len(s2)-1], "/")
			if slash == -1 {
				break
			}
			s2 = s2[:slash+1]
		}

		if r, ok := m.fragments[s]; ok {
			result[r] = 1
		}
		dot := strings.Index(s, ".")
		if dot == -1 {
			break
		}
		s = s[dot+1:]
	}

	return result
}
<commit_msg>Support internationalized domain names.<commit_after>package main

// URL matching and regular expressions

import (
	"code.google.com/p/go-idn/idna/punycode"
	"log"
	"net/url"
	"regexp"
	"strings"
)

type regexRule struct {
	rule
	*regexp.Regexp
}

// A regexMap is a set of regular-expression rules.
// As an optimization, it uses Aho-Corasick string matching find which regular
// expressions might match—instead of trying them all.
type regexMap struct {
	prefixList phraseList
	rules      map[string][]regexRule
}

func newRegexMap() *regexMap {
	return &regexMap{
		prefixList: newPhraseList(),
		rules:      make(map[string][]regexRule),
	}
}

func (rm *regexMap) findMatches(s string, tally map[rule]int) {
	tried := map[string]bool{}
	scanner := newPhraseScanner(rm.prefixList, func(prefix string) {
		if tried[prefix] {
			return
		}
		for _, r := range rm.rules[prefix] {
			if r.MatchString(s) {
				tally[r.rule] = 1
			}
		}
		tried[prefix] = true
	})

	for i := 0; i < len(s); i++ {
		scanner.scanByte(s[i])
	}

	// Now try the regexes that have no literal string prefix.
	for _, r := range rm.rules[""] {
		if r.MatchString(s) {
			tally[r.rule] = 1
		}
	}
}

// addRule adds a rule to the map.
func (rm *regexMap) addRule(r rule) {
	s := r.content

	re, err := regexp.Compile(s)
	if err != nil {
		log.Printf("Error parsing URL regular expression %s: %v", r, err)
		return
	}

	prefix, _ := re.LiteralPrefix()
	if prefix != "" {
		rm.prefixList.addPhrase(prefix)
	}
	rm.rules[prefix] = append(rm.rules[prefix], regexRule{r, re})
}

type URLMatcher struct {
	fragments    map[string]rule // a set of domain or domain+path URL fragments to test against
	regexes      *regexMap       // to match whole URL
	hostRegexes  *regexMap       // to match hostname only
	pathRegexes  *regexMap
	queryRegexes *regexMap
}

// finalize should be called after all rules have been added, but before
// using the URLMatcher.
func (m *URLMatcher) finalize() {
	m.regexes.prefixList.findFallbackNodes(0, nil)
	m.hostRegexes.prefixList.findFallbackNodes(0, nil)
	m.pathRegexes.prefixList.findFallbackNodes(0, nil)
	m.queryRegexes.prefixList.findFallbackNodes(0, nil)
}

func newURLMatcher() *URLMatcher {
	m := new(URLMatcher)
	m.fragments = make(map[string]rule)
	m.regexes = newRegexMap()
	m.hostRegexes = newRegexMap()
	m.pathRegexes = newRegexMap()
	m.queryRegexes = newRegexMap()
	return m
}

// AddRule adds a rule to the matcher (unless it's already there).
func (m *URLMatcher) AddRule(r rule) {
	switch r.t {
	case urlMatch:
		m.fragments[r.content] = r
	case urlRegex:
		m.regexes.addRule(r)
	case hostRegex:
		m.hostRegexes.addRule(r)
	case pathRegex:
		m.pathRegexes.addRule(r)
	case queryRegex:
		m.queryRegexes.addRule(r)
	}
}

// MatchingRules returns a list of the rules that u matches.
// For consistency with phrase matching, it is a map with rules for keys
// and with all values equal to 1.
func (m *URLMatcher) MatchingRules(u *url.URL) map[rule]int {
	result := make(map[rule]int)

	host := strings.ToLower(u.Host)
	path := strings.ToLower(u.Path)

	// strip off the port number, if present
	colon := strings.LastIndex(host, ":")
	if colon != -1 {
		host = host[:colon]
	}

	// Handle internationalized domain names.
	if strings.Contains(host, "xn--") {
		labels := strings.Split(host, ".")
		for i, puny := range labels {
			if !strings.HasPrefix(puny, "xn--") {
				continue
			}
			uni, err := punycode.DecodeString(puny[len("xn--"):])
			if err == nil {
				labels[i] = uni
			}
		}
		host = strings.ToLower(strings.Join(labels, "."))
	}

	m.regexes.findMatches(strings.ToLower(u.String()), result)
	m.hostRegexes.findMatches(strings.ToLower(u.Scheme) + "://" + host, result)
	m.pathRegexes.findMatches(path, result)
	m.queryRegexes.findMatches(strings.ToLower(u.RawQuery), result)

	// Test for matches of the host and of the domains it belongs to.
	s := host
	for {
		// Test for matches with the path.
		s2 := s + path
		for {
			if r, ok := m.fragments[s2]; ok {
				result[r] = 1
			}
			slash := strings.LastIndex(s2[:len(s2)-1], "/")
			if slash == -1 {
				break
			}
			s2 = s2[:slash+1]
		}

		if r, ok := m.fragments[s]; ok {
			result[r] = 1
		}
		dot := strings.Index(s, ".")
		if dot == -1 {
			break
		}
		s = s[dot+1:]
	}

	return result
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"honarnama/common"
	"net/http"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
)

func main() {
	// Static files
	fs := http.FileServer(http.Dir(common.ConfigString("STATIC_DIR")))
	http.Handle("/s/", http.StripPrefix("/s/", fs))
	http.Handle("/favicon.ico", fs)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		base := "base"
		name := r.URL.Path[1:]
		var data map[string]interface{}
		contentType := "text/html; charset=utf-8"
		if name == "" {
			name = "index"
		}
		if strings.HasPrefix(name, "email-") {
			err := r.ParseForm()
			if err != nil {
				logrus.WithField("name", name).WithError(err).Debug("Failed to ParseForm")
			} else {
				data = make(map[string]interface{})
				for k, v := range r.Form {
					data[k] = v[0]
				}
				logrus.WithField("name", name).WithField("data", data).Debug("Context for email template")
			}

			if strings.HasPrefix(name, "email-html/") {
				base = "email-base-html"
			}
			if strings.HasPrefix(name, "email-text/") {
				base = "email-base-text"
				contentType = "text/plain; charset=utf-8"
			}
			name = name[11:]
		}

		name = fmt.Sprintf("%s.tmpl", name)

		writeFunc, err := common.RenderTemplate(w, base, name, data)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(fmt.Sprintf("404 (%s)", err)))
		} else {
			w.Header().Set("Content-Type", contentType)
			writeFunc()
		}

		logrus.WithFields(logrus.Fields{
			logrus.ErrorKey: err,
			"method":        r.Method,
			"uri":           r.RequestURI,
			"referer":       r.Referer(),
			"user-agent":    r.UserAgent(),
		}).Infoln()
	})

	bind := fmt.Sprintf("%s:%s", os.Getenv("OPENSHIFT_GO_IP"), os.Getenv("OPENSHIFT_GO_PORT"))
	fmt.Printf("listening on %s...", bind)
	err := http.ListenAndServe(bind, nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Oops, bad import<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/remohammadi/delphi/common"
)

func main() {
	// Static files
	fs := http.FileServer(http.Dir(common.ConfigString("STATIC_DIR")))
	http.Handle("/s/", http.StripPrefix("/s/", fs))
	http.Handle("/favicon.ico", fs)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		base := "base"
		name := r.URL.Path[1:]
		var data map[string]interface{}
		contentType := "text/html; charset=utf-8"
		if name == "" {
			name = "index"
		}
		if strings.HasPrefix(name, "email-") {
			err := r.ParseForm()
			if err != nil {
				logrus.WithField("name", name).WithError(err).Debug("Failed to ParseForm")
			} else {
				data = make(map[string]interface{})
				for k, v := range r.Form {
					data[k] = v[0]
				}
				logrus.WithField("name", name).WithField("data", data).Debug("Context for email template")
			}

			if strings.HasPrefix(name, "email-html/") {
				base = "email-base-html"
			}
			if strings.HasPrefix(name, "email-text/") {
				base = "email-base-text"
				contentType = "text/plain; charset=utf-8"
			}
			name = name[11:]
		}

		name = fmt.Sprintf("%s.tmpl", name)

		writeFunc, err := common.RenderTemplate(w, base, name, data)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(fmt.Sprintf("404 (%s)", err)))
		} else {
			w.Header().Set("Content-Type", contentType)
			writeFunc()
		}

		logrus.WithFields(logrus.Fields{
			logrus.ErrorKey: err,
			"method":        r.Method,
			"uri":           r.RequestURI,
			"referer":       r.Referer(),
			"user-agent":    r.UserAgent(),
		}).Infoln()
	})

	bind := fmt.Sprintf("%s:%s", os.Getenv("OPENSHIFT_GO_IP"), os.Getenv("OPENSHIFT_GO_PORT"))
	fmt.Printf("listening on %s...", bind)
	err := http.ListenAndServe(bind, nil)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/tebeka/nrsc"
	"github.com/gorilla/mux"
	"html/template"
	"log"
	"net/http"
	"regexp"
)

var TMPL = regexp.MustCompile(".tmpl$")

var index = template.Must(nrsc.LoadTemplates(nil, "index.html.tmpl"))
var info = template.Must(nrsc.LoadTemplates(nil, "info.html.tmpl"))

type IndexState struct {
	Succeeded int
	Retryable int
	Failed    int
	Programs  []*Program
}

type InfoState struct {
	Program *Program
}

func handleIndex(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		if err := index.Execute(w, IndexState{77, 13, 12, dagr.AllPrograms()}); err != nil {
			http.NotFound(w, req)
		}
	}
}

func handleInfo(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		programName := vars["program"]
		program := dagr.FindProgram(programName)
		if program == nil {
			log.Println("no such program:", programName)
			http.NotFound(w, req)
		} else {
			if err := info.Execute(w, InfoState{program}); err != nil {
				http.NotFound(w, req)
			}
		}
	}
}

func handleExecution(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		programName := vars["program"]
		program := dagr.FindProgram(programName)
		if program == nil {
			log.Println("no such program:", programName)
			http.NotFound(w, req)
		} else {
			exec := NewExecution(program)
			exec.Execute()
			http.Redirect(w, req, "/", 302)
		}
	}
}

func Serve(httpAddr string, dagr Dagr) error {
	nrsc.Handle("/static/")
	nrsc.Mask(TMPL)

	r := mux.NewRouter()
	r.HandleFunc("/", handleIndex(dagr)).Methods("GET")
	r.HandleFunc("/program/{program}", handleInfo(dagr)).Methods("GET")
	r.HandleFunc("/program/{program}/execute", handleExecution(dagr)).Methods("POST")
	http.Handle("/", r)

	server := &http.Server{
		Addr: httpAddr,
	}

	log.Println("dagr listening on", httpAddr)

	return server.ListenAndServe()
}
<commit_msg>minor tidying<commit_after>package main

import (
	"bitbucket.org/tebeka/nrsc"
	"github.com/gorilla/mux"
	"html/template"
	"log"
	"net/http"
	"regexp"
)

var TMPL = regexp.MustCompile(".tmpl$")

var index = template.Must(nrsc.LoadTemplates(nil, "index.html.tmpl"))
var info = template.Must(nrsc.LoadTemplates(nil, "info.html.tmpl"))

type IndexState struct {
	Succeeded int
	Retryable int
	Failed    int
	Programs  []*Program
}

type InfoState struct {
	Program *Program
}

func handleIndex(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		if err := index.Execute(w, IndexState{77, 13, 12, dagr.AllPrograms()}); err != nil {
			http.NotFound(w, req)
		}
	}
}

func handleInfo(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		programName := vars["program"]
		program := dagr.FindProgram(programName)
		if program == nil {
			log.Println("no such program:", programName)
			http.NotFound(w, req)
		} else if err := info.Execute(w, InfoState{program}); err != nil {
			http.NotFound(w, req)
		}
	}
}

func handleExecution(dagr Dagr) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		programName := vars["program"]
		program := dagr.FindProgram(programName)
		if program == nil {
			log.Println("no such program:", programName)
			http.NotFound(w, req)
		} else {
			exec := NewExecution(program)
			exec.Execute()
			http.Redirect(w, req, "/", 302)
		}
	}
}

func Serve(httpAddr string, dagr Dagr) error {
	nrsc.Handle("/static/")
	nrsc.Mask(TMPL)

	r := mux.NewRouter()
	r.HandleFunc("/", handleIndex(dagr)).Methods("GET")
	r.HandleFunc("/program/{program}", handleInfo(dagr)).Methods("GET")
	r.HandleFunc("/program/{program}/execute", handleExecution(dagr)).Methods("POST")
	http.Handle("/", r)

	server := &http.Server{
		Addr: httpAddr,
	}

	log.Println("dagr listening on", httpAddr)

	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
)

// main starts an HTTP server listening on $PORT which dispatches to request handlers.
func main() {
	// wrap the poweredByHandler with logging middleware
	http.Handle("/", logRequestMiddleware(http.HandlerFunc(poweredByHandler)))
	port := os.Getenv("PORT")
	log.Printf("listening on %v...\n", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

// poweredByHandler writes "Powered by $POWERED_BY" to the response.
func poweredByHandler(w http.ResponseWriter, r *http.Request) {
	release := os.Getenv("DEIS_RELEASE")
	if release == "" {
		release = "<unknown>"
	}
	powered := os.Getenv("POWERED_BY")
	if powered == "" {
		powered = "Deis"
	}
	// print the string to the ResponseWriter
	hostname, _ := os.Hostname()
	fmt.Fprintf(w, "Powered by %v\nRelease %v on %v\n", powered, release, hostname)
}

// logRequestMiddleware writes out HTTP request information before passing to the next handler.
func logRequestMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		remote := r.RemoteAddr
		if forwardedFor := r.Header.Get("X-Forwarded-For"); forwardedFor != "" {
			remote = forwardedFor
		}
		log.Printf("%s %s %s", remote, r.Method, r.URL)
		// pass the request to the next handler
		next.ServeHTTP(w, r)
	})
}
<commit_msg>chore(web.go): DEIS_RELEASE -> WORKFLOW_RELEASE<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
)

// main starts an HTTP server listening on $PORT which dispatches to request handlers.
func main() {
	// wrap the poweredByHandler with logging middleware
	http.Handle("/", logRequestMiddleware(http.HandlerFunc(poweredByHandler)))
	port := os.Getenv("PORT")
	log.Printf("listening on %v...\n", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

// poweredByHandler writes "Powered by $POWERED_BY" to the response.
func poweredByHandler(w http.ResponseWriter, r *http.Request) {
	release := os.Getenv("WORKFLOW_RELEASE")
	if release == "" {
		release = "<unknown>"
	}
	powered := os.Getenv("POWERED_BY")
	if powered == "" {
		powered = "Deis"
	}
	// print the string to the ResponseWriter
	hostname, _ := os.Hostname()
	fmt.Fprintf(w, "Powered by %v\nRelease %v on %v\n", powered, release, hostname)
}

// logRequestMiddleware writes out HTTP request information before passing to the next handler.
func logRequestMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		remote := r.RemoteAddr
		if forwardedFor := r.Header.Get("X-Forwarded-For"); forwardedFor != "" {
			remote = forwardedFor
		}
		log.Printf("%s %s %s", remote, r.Method, r.URL)
		// pass the request to the next handler
		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/hoisie/mustache"
	"log"
	"net/http"
	"net/url"
	"strings"
)

func rss(w http.ResponseWriter, r *http.Request) {
	posts, err := RecentPosts(10)
	if err != nil {
		log.Println("OOPS ERROR", err.Error())
	} else {
		log.Println("OHAI", len(posts), "posts")
	}

	data := make(map[string]interface{})
	data["posts"] = posts
	data["title"] = "markpasc"

	baseurl, err := url.Parse("/")
	baseurl.Host = r.Host
	// TODO: somehow determine if we're on HTTPS or no?
	baseurl.Scheme = "http"
	baseurl.Fragment = ""
	data["baseurl"] = strings.TrimRight(baseurl.String(), "/")
	log.Println("Rendering RSS with baseurl of", data["baseurl"])

	xml := mustache.RenderFile("html/rss.xml", data)
	w.Header().Set("Content-Type", "application/rss+xml")
	w.Write([]byte(xml))
}

func index(w http.ResponseWriter, r *http.Request) {
	if len(r.URL.Path) > 1 {
		// Actually some other unhandled URL, so 404.
		http.NotFound(w, r)
		return
	}

	posts, err := RecentPosts(10)
	if err != nil {
		log.Println("OOPS ERROR", err.Error())
	} else {
		log.Println("OHAI", len(posts), "posts")
	}

	data := make(map[string]interface{})
	data["posts"] = posts
	data["title"] = "markpasc"
	html := mustache.RenderFileInLayout("html/index.html", "html/base.html", data)
	w.Write([]byte(html))
}

func permalink(w http.ResponseWriter, r *http.Request) {
	idstr := r.URL.Path[len("/post/"):]
	post, err := PostBySlug(idstr)
	if err != nil {
		http.Error(w, fmt.Sprintf("invalid post %s: %s", idstr, err.Error()), http.StatusBadRequest)
		return
	}

	if r.Method == "DELETE" {
		if !isAuthed(w, r) {
			return
		}

		post.MarkDeleted()

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNoContent)
		w.Write([]byte("{\"ok\":true}"))
		return
	}

	data := make(map[string]interface{})
	data["post"] = post
	data["title"] = "markpasc • a post"
	html := mustache.RenderFileInLayout("html/permalink.html", "html/base.html", data)
	w.Write([]byte(html))
}

func isAuthed(w http.ResponseWriter, r *http.Request) (authed bool) {
	authed = false
	authHeader := r.Header.Get("Authorization")
	if strings.HasPrefix(authHeader, "Basic ") {
		log.Println("Yay, client gave a Basic auth header")
		userpass, err := base64.StdEncoding.DecodeString(authHeader[6:])
		if err == nil {
			userpassParts := strings.SplitN(string(userpass), ":", 2)
			user, pass := userpassParts[0], userpassParts[1]
			if user == "markpasc" && pass == "password" {
				log.Println("Yay, client authorized!")
				authed = true
			} else {
				log.Println("Oops, client gave a bad username:password pair")
			}
		} else {
			log.Println("Oops, error decoding the client's Basic auth header:", err.Error())
		}
	}

	if !authed {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"cares\"")
		http.Error(w, "authorization required", http.StatusUnauthorized)
	}
	return
}

func post(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, "POST is required", http.StatusMethodNotAllowed)
		return
	}
	if !isAuthed(w, r) {
		return
	}

	post := NewPost()
	html := r.FormValue("html")
	if html == "" {
		http.Error(w, "html value is required", http.StatusBadRequest)
	}

	post.Html = html
	err := post.Save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	ret, err := json.Marshal(post)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(ret)
}

func static(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[1:]
	log.Println("Serving static file", path)
	http.ServeFile(w, r, path)
}
<commit_msg>Move utility isAuthed function to the top<commit_after>package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/hoisie/mustache"
	"log"
	"net/http"
	"net/url"
	"strings"
)

func isAuthed(w http.ResponseWriter, r *http.Request) (authed bool) {
	authed = false
	authHeader := r.Header.Get("Authorization")
	if strings.HasPrefix(authHeader, "Basic ") {
		log.Println("Yay, client gave a Basic auth header")
		userpass, err := base64.StdEncoding.DecodeString(authHeader[6:])
		if err == nil {
			userpassParts := strings.SplitN(string(userpass), ":", 2)
			user, pass := userpassParts[0], userpassParts[1]
			if user == "markpasc" && pass == "password" {
				log.Println("Yay, client authorized!")
				authed = true
			} else {
				log.Println("Oops, client gave a bad username:password pair")
			}
		} else {
			log.Println("Oops, error decoding the client's Basic auth header:", err.Error())
		}
	}

	if !authed {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"cares\"")
		http.Error(w, "authorization required", http.StatusUnauthorized)
	}
	return
}

func rss(w http.ResponseWriter, r *http.Request) {
	posts, err := RecentPosts(10)
	if err != nil {
		log.Println("OOPS ERROR", err.Error())
	} else {
		log.Println("OHAI", len(posts), "posts")
	}

	data := make(map[string]interface{})
	data["posts"] = posts
	data["title"] = "markpasc"

	baseurl, err := url.Parse("/")
	baseurl.Host = r.Host
	// TODO: somehow determine if we're on HTTPS or no?
	baseurl.Scheme = "http"
	baseurl.Fragment = ""
	data["baseurl"] = strings.TrimRight(baseurl.String(), "/")
	log.Println("Rendering RSS with baseurl of", data["baseurl"])

	xml := mustache.RenderFile("html/rss.xml", data)
	w.Header().Set("Content-Type", "application/rss+xml")
	w.Write([]byte(xml))
}

func index(w http.ResponseWriter, r *http.Request) {
	if len(r.URL.Path) > 1 {
		// Actually some other unhandled URL, so 404.
		http.NotFound(w, r)
		return
	}

	posts, err := RecentPosts(10)
	if err != nil {
		log.Println("OOPS ERROR", err.Error())
	} else {
		log.Println("OHAI", len(posts), "posts")
	}

	data := make(map[string]interface{})
	data["posts"] = posts
	data["title"] = "markpasc"
	html := mustache.RenderFileInLayout("html/index.html", "html/base.html", data)
	w.Write([]byte(html))
}

func permalink(w http.ResponseWriter, r *http.Request) {
	idstr := r.URL.Path[len("/post/"):]
	post, err := PostBySlug(idstr)
	if err != nil {
		http.Error(w, fmt.Sprintf("invalid post %s: %s", idstr, err.Error()), http.StatusBadRequest)
		return
	}

	if r.Method == "DELETE" {
		if !isAuthed(w, r) {
			return
		}

		post.MarkDeleted()

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNoContent)
		w.Write([]byte("{\"ok\":true}"))
		return
	}

	data := make(map[string]interface{})
	data["post"] = post
	data["title"] = "markpasc • a post"
	html := mustache.RenderFileInLayout("html/permalink.html", "html/base.html", data)
	w.Write([]byte(html))
}

func post(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, "POST is required", http.StatusMethodNotAllowed)
		return
	}
	if !isAuthed(w, r) {
		return
	}

	post := NewPost()
	html := r.FormValue("html")
	if html == "" {
		http.Error(w, "html value is required", http.StatusBadRequest)
	}

	post.Html = html
	err := post.Save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	ret, err := json.Marshal(post)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(ret)
}

func static(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[1:]
	log.Println("Serving static file", path)
	http.ServeFile(w, r, path)
}
<|endoftext|>


<commit_before>// Copyright 2012 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
/*
	Unmarshal an arbitrary XML document to a map or a JSON string.

	DocToMap() returns an intermediate result with the XML doc unmarshal'd to a map
	of type map[string]interface{}. It is analogous to unmarshal'ng a JSON string to
	a map using json.Unmarshal(). (This was the original purpose of this library.)

	DocToTree()/WriteTree() let you examine the parsed XML doc.

	XML values are all type 'string'. The optional argument 'recast' for DocToJson()
	and DocToMap() will convert element values to JSON data types - 'float64' and 'bool' -
	if possible.  This, however, should be done with caution as it will recast ALL numeric
	and boolean string values, even those that are meant to be of type 'string'.
 */
package x2j

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

type node struct {
	dup bool
	key string
	val string
	nodes []*node
}

// DocToJson - return an XML doc as a JSON string.
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToJson(doc string,recast ...bool) (string,error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	m,merr := DocToMap(doc,r)
	if m == nil || merr != nil {
		return "",merr
	}

	b, berr := json.Marshal(m)
	if berr != nil {
		return "",berr
	}

	return string(b),nil
}

// DocToJsonIndent - return an XML doc as a prettified JSON string.
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToJsonIndent(doc string,recast ...bool) (string,error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	m,merr := DocToMap(doc,r)
	if m == nil || merr != nil {
		return "",merr
	}

	b, berr := json.MarshalIndent(m,"","  ")
	if berr != nil {
		return "",berr
	}

	return string(b),nil
}

// DocToMap - convert an XML doc into a map[string]interface{}.
// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToMap(doc string,recast ...bool) (map[string]interface{},error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	n,err := DocToTree(doc)
	if err != nil {
		return nil,err
	}

	m := make(map[string]interface{})
	m[n.key] = treeToMap(n,r)

	return m,nil
}

// DocToTree - convert an XML doc into a tree of nodes.
func DocToTree(doc string) (*node, error) {
	reg,_ := regexp.Compile("[ \t]*<")
	doc = reg.ReplaceAllString(doc,"<")
	r := strings.NewReplacer("\n","","\r","","\t","")
	doc = r.Replace(doc)
	b := bytes.NewBufferString(doc)
	// p := xml.NewDecoder(b)     Go r1
	p := xml.NewDecoder(b)

	n, berr := xmlToTree("",nil,p)
	if berr != nil {
		return nil, berr
	}

	return n,nil
}

// WriteTree - convert a tree of nodes into a printable string.
//	'indent' is the starting indentation count for formating.
func WriteTree(indent int,n *node) string {
	var s string
	if n.val != "" {
		for i := 0 ; i < indent ; i++ {
			s += "  "
		}
		s += n.key+" : "+n.val+"\n"
	} else {
		for i := 0 ; i < indent ; i++ {
			s += "  "
		}
		s += n.key+" :"+"\n"
		for _,v := range n.nodes {
			s += WriteTree(indent+1,v)
		}
	}
	return s
}

// xmlToTree - load a 'clean' XML doc into a tree of *node.
func xmlToTree(skey string,a []xml.Attr,p *xml.Decoder) (*node, error) {
	n := new(node)
	n.nodes = make([]*node,0)

	if skey != "" {
		n.key = skey
		if len(a) > 0 {
			for _,v := range a {
				na := new(node)
				na.key = `-`+v.Name.Local
				na.val = v.Value
				n.nodes = append(n.nodes,na)
			}
		}
	}
	for {
		t,err := p.Token()
		if err != nil {
			return nil, err
		} else {
			switch t.(type) {
				case xml.StartElement:
					tt := t.(xml.StartElement)
					if n.key == "" {						// handle root
						n.key = tt.Name.Local
						if len(tt.Attr) > 0 {
							for _,v := range tt.Attr {
								na := new(node)
								na.key = `-`+v.Name.Local
								na.val = v.Value
								n.nodes = append(n.nodes,na)
							}
						}
					} else {
						nn, nnerr := xmlToTree(tt.Name.Local,tt.Attr,p)
						if nnerr != nil {
							return nil, nnerr
						}
						n.nodes = append(n.nodes,nn)
					}
				case xml.EndElement:
					// scan v.nodes for duplicate v.key values
					markDuplicateKeys(n)
					return n, nil
				case xml.CharData:
					tt := string(t.(xml.CharData))
					if len(n.nodes) > 0 {
						nn := new(node)
						nn.key = "#text"
						nn.val = tt
						n.nodes = append(n.nodes,nn)
					} else {
						n.val = tt
					}
				default:
					// noop
			}
		}
	}
	return nil, errors.New("EndElement not found for: "+n.key)
}

// markDuplicateKeys - set node.dup flag for loading map[string]interface{}.
func markDuplicateKeys(n *node) {
	l := len(n.nodes)
	for i := 0 ; i < l ; i++ {
		if n.nodes[i].dup {
			continue
		}
		for j := i+1 ; j < l ; j++ {
			if n.nodes[i].key == n.nodes[j].key {
				n.nodes[i].dup = true
				n.nodes[j].dup = true
			}
		}
	}
}

// treeToMap - convert a tree of nodes into a map[string]interface{}.
//	(Parses to map the same as json.Unmarshal().)
// Note: root is not instantiated; call with: "m[n.key] = treeToMap()".
func treeToMap(n *node,r bool) interface{} {
	if len(n.nodes) == 0 {
		return recast(n.val,r)
	}

	m := make(map[string]interface{},0)
	for _,v := range n.nodes {
		// just a value
		if !v.dup && len(v.nodes) == 0 {
			m[v.key] = recast(v.val,r)
			continue
		}

		// a list of values
		if v.dup {
			var a []interface{}
			if vv,ok := m[v.key]; ok {
				a = vv.([]interface{})
			} else {
				a = make([]interface{},0)
			}
			a = append(a,treeToMap(v,r))
			m[v.key] = interface{}(a)
			continue
		}

		// it's a unique key
		m[v.key] = treeToMap(v,r)
	}

	return interface{}(m)
}

// recast - try to cast string values to bool or float64
func recast(s string,r bool) interface{} {
	if r {
		// handle numeric strings ahead of boolean
		if f, err := strconv.ParseFloat(s,64); err == nil {
			return interface{}(f)
		}
		// ParseBool treats "1"==true & "0"==false
		if b, err := strconv.ParseBool(s); err == nil {
			return interface{}(b)
		}
	}
	return interface{}(s)
}

// WriteMap - dumps the map[string]interface{} for examination.
//	NOTE: with XML all element types are 'string'.
//	But code written as generic for use with maps[string]interface{} values from json.Unmarshal().
func WriteMap(indent int,m interface{}) string {
	var s string
	switch m.(type) {
		case nil:
			return "[nil] nil"
		case string:
			return "[string] "+m.(string)
		case float64:
			return "[float64] "+strconv.FormatFloat(m.(float64),'e',2,64)
		case bool:
			return "[bool] "+strconv.FormatBool(m.(bool))
		case []interface{}:
			s += "[[]interface{}]"
			for i,v := range m.([]interface{}) {
				s += "\n"
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += "item: "+strconv.FormatInt(int64(i),10)
				switch v.(type) {
					case string,float64,bool:
						s += "\n"
					default:
						// noop
				}
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += WriteMap(indent+1,v)
			}
		case map[string]interface{}:
			for k,v := range m.(map[string]interface{}) {
				s += "\n"
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += "[map[string]interface{}] "+k+" :"+WriteMap(indent+1,v)
		}
		default:
			// shouldn't ever be here ...
			s += fmt.Sprintf("unknown type for: %v",m)
	}
	return s
}
<commit_msg>v1.0.1.2<commit_after>// Copyright 2012 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
/*
	Unmarshal an arbitrary XML document to a map or a JSON string.

	DocToMap() returns an intermediate result with the XML doc unmarshal'd to a map
	of type map[string]interface{}. It is analogous to unmarshal'ng a JSON string to
	a map using json.Unmarshal(). (This was the original purpose of this library.)

	DocToTree()/WriteTree() let you examine the parsed XML doc.

	XML values are all type 'string'. The optional argument 'recast' for DocToJson()
	and DocToMap() will convert element values to JSON data types - 'float64' and 'bool' -
	if possible.  This, however, should be done with caution as it will recast ALL numeric
	and boolean string values, even those that are meant to be of type 'string'.
 */
package x2j

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

type node struct {
	dup bool
	key string
	val string
	nodes []*node
}

// DocToJson - return an XML doc as a JSON string.
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToJson(doc string,recast ...bool) (string,error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	m,merr := DocToMap(doc,r)
	if m == nil || merr != nil {
		return "",merr
	}

	b, berr := json.Marshal(m)
	if berr != nil {
		return "",berr
	}

	return string(b),nil
}

// DocToJsonIndent - return an XML doc as a prettified JSON string.
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToJsonIndent(doc string,recast ...bool) (string,error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	m,merr := DocToMap(doc,r)
	if m == nil || merr != nil {
		return "",merr
	}

	b, berr := json.MarshalIndent(m,"","  ")
	if berr != nil {
		return "",berr
	}

	return string(b),nil
}

// DocToMap - convert an XML doc into a map[string]interface{}.
// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
//	If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
func DocToMap(doc string,recast ...bool) (map[string]interface{},error) {
	var r bool
	if len(recast) == 1 {
		r = recast[0]
	}
	n,err := DocToTree(doc)
	if err != nil {
		return nil,err
	}

	m := make(map[string]interface{})
	m[n.key] = treeToMap(n,r)

	return m,nil
}

// DocToTree - convert an XML doc into a tree of nodes.
func DocToTree(doc string) (*node, error) {
	reg,_ := regexp.Compile("[ \t]*<")
	doc = reg.ReplaceAllString(doc,"<")
	r := strings.NewReplacer("\n","","\r","","\t","")
	doc = r.Replace(doc)
	b := bytes.NewBufferString(doc)
	// p := xml.NewDecoder(b)     Go r1
	p := xml.NewDecoder(b)

	n, berr := xmlToTree("",nil,p)
	if berr != nil {
		return nil, berr
	}

	return n,nil
}

// WriteTree - convert a tree of nodes into a printable string.
//	'indent' is the starting indentation count; typically: WriteTree(0,n).
func WriteTree(indent int,n *node) string {
	var s string
	if n.val != "" {
		for i := 0 ; i < indent ; i++ {
			s += "  "
		}
		s += n.key+" : "+n.val+"\n"
	} else {
		for i := 0 ; i < indent ; i++ {
			s += "  "
		}
		s += n.key+" :"+"\n"
		for _,v := range n.nodes {
			s += WriteTree(indent+1,v)
		}
	}
	return s
}

// xmlToTree - load a 'clean' XML doc into a tree of *node.
func xmlToTree(skey string,a []xml.Attr,p *xml.Decoder) (*node, error) {
	n := new(node)
	n.nodes = make([]*node,0)

	if skey != "" {
		n.key = skey
		if len(a) > 0 {
			for _,v := range a {
				na := new(node)
				na.key = `-`+v.Name.Local
				na.val = v.Value
				n.nodes = append(n.nodes,na)
			}
		}
	}
	for {
		t,err := p.Token()
		if err != nil {
			return nil, err
		} else {
			switch t.(type) {
				case xml.StartElement:
					tt := t.(xml.StartElement)
					// handle root
					if n.key == "" {
						n.key = tt.Name.Local
						if len(tt.Attr) > 0 {
							for _,v := range tt.Attr {
								na := new(node)
								na.key = `-`+v.Name.Local
								na.val = v.Value
								n.nodes = append(n.nodes,na)
							}
						}
					} else {
						nn, nnerr := xmlToTree(tt.Name.Local,tt.Attr,p)
						if nnerr != nil {
							return nil, nnerr
						}
						n.nodes = append(n.nodes,nn)
					}
				case xml.EndElement:
					// scan v.nodes for duplicate v.key values
					markDuplicateKeys(n)
					return n, nil
				case xml.CharData:
					tt := string(t.(xml.CharData))
					if len(n.nodes) > 0 {
						nn := new(node)
						nn.key = "#text"
						nn.val = tt
						n.nodes = append(n.nodes,nn)
					} else {
						n.val = tt
					}
				default:
					// noop
			}
		}
	}
	return nil, errors.New("EndElement not found for: "+n.key)
}

// markDuplicateKeys - set node.dup flag for loading map[string]interface{}.
func markDuplicateKeys(n *node) {
	l := len(n.nodes)
	for i := 0 ; i < l ; i++ {
		if n.nodes[i].dup {
			continue
		}
		for j := i+1 ; j < l ; j++ {
			if n.nodes[i].key == n.nodes[j].key {
				n.nodes[i].dup = true
				n.nodes[j].dup = true
			}
		}
	}
}

// treeToMap - convert a tree of nodes into a map[string]interface{}.
//	(Parses to map that is structurally the same as from json.Unmarshal().)
// Note: root is not instantiated; call with: "m[n.key] = treeToMap()".
func treeToMap(n *node,r bool) interface{} {
	if len(n.nodes) == 0 {
		return recast(n.val,r)
	}

	m := make(map[string]interface{},0)
	for _,v := range n.nodes {
		// just a value
		if !v.dup && len(v.nodes) == 0 {
			m[v.key] = recast(v.val,r)
			continue
		}

		// a list of values
		if v.dup {
			var a []interface{}
			if vv,ok := m[v.key]; ok {
				a = vv.([]interface{})
			} else {
				a = make([]interface{},0)
			}
			a = append(a,treeToMap(v,r))
			m[v.key] = interface{}(a)
			continue
		}

		// it's a unique key
		m[v.key] = treeToMap(v,r)
	}

	return interface{}(m)
}

// recast - try to cast string values to bool or float64
func recast(s string,r bool) interface{} {
	if r {
		// handle numeric strings ahead of boolean
		if f, err := strconv.ParseFloat(s,64); err == nil {
			return interface{}(f)
		}
		// ParseBool treats "1"==true & "0"==false
		if b, err := strconv.ParseBool(s); err == nil {
			return interface{}(b)
		}
	}
	return interface{}(s)
}

// WriteMap - dumps the map[string]interface{} for examination.
//	'indent' is initial indentation count; typically: WriteMap(0,m).
//	NOTE: with XML all element types are 'string'.
//	But code written as generic for use with maps[string]interface{} values from json.Unmarshal().
func WriteMap(indent int,m interface{}) string {
	var s string
	switch m.(type) {
		case nil:
			return "[nil] nil"
		case string:
			return "[string] "+m.(string)
		case float64:
			return "[float64] "+strconv.FormatFloat(m.(float64),'e',2,64)
		case bool:
			return "[bool] "+strconv.FormatBool(m.(bool))
		case []interface{}:
			s += "[[]interface{}]"
			for i,v := range m.([]interface{}) {
				s += "\n"
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += "item: "+strconv.FormatInt(int64(i),10)
				switch v.(type) {
					case string,float64,bool:
						s += "\n"
					default:
						// noop
				}
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += WriteMap(indent+1,v)
			}
		case map[string]interface{}:
			for k,v := range m.(map[string]interface{}) {
				s += "\n"
				for i := 0 ; i < indent ; i++ {
					s += "  "
				}
				s += "[map[string]interface{}] "+k+" :"+WriteMap(indent+1,v)
		}
		default:
			// shouldn't ever be here ...
			s += fmt.Sprintf("unknown type for: %v",m)
	}
	return s
}
<|endoftext|>