<commit_before>// Copyright 2013 Matthew Baird
// 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.

/*
The API package provides the basic support for using HTTP to talk to the Mandrill and Mailchimp API's.
Each Struct contains a Key, Transport and endpoint property
*/
package gochimp

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"time"
)

const (
	mandrill_uri     = "mandrillapp.com"
	mandrill_version = "/api/1.0"
)

type MandrillAPI struct {
	Key       string
	Transport http.RoundTripper
	Timeout   time.Duration
	endpoint  string
}

type ChimpAPI struct {
	Key       string
	Transport http.RoundTripper
	Timeout   time.Duration
	endpoint  string
}

// see https://mandrillapp.com/api/docs/
// currently supporting json output formats
func NewMandrill(apiKey string) (*MandrillAPI, error) {
	u := url.URL{}
	u.Scheme = "https"
	u.Host = mandrill_uri
	u.Path = mandrill_version
	return &MandrillAPI{Key: apiKey, endpoint: u.String()}, nil
}

const mailchimp_uri string = "%s.api.mailchimp.com"
const mailchimp_version string = "/2.0"
const debug bool = false

var mailchimp_datacenter = regexp.MustCompile("[a-z]+[0-9]+$")

func NewChimp(apiKey string, https bool) *ChimpAPI {
	u := url.URL{}
	if https {
		u.Scheme = "https"
	} else {
		u.Scheme = "http"
	}
	u.Host = fmt.Sprintf("%s.api.mailchimp.com", mailchimp_datacenter.FindString(apiKey))
	u.Path = mailchimp_version
	return &ChimpAPI{Key: apiKey, endpoint: u.String()}
}

func runChimp(api *ChimpAPI, path string, parameters interface{}) ([]byte, error) {
	b, err := json.Marshal(parameters)
	if err != nil {
		return nil, err
	}
	requestUrl := fmt.Sprintf("%s%s", api.endpoint, path)
	if debug {
		log.Printf("Request URL:%s", requestUrl)
	}
	client := &http.Client{Transport: api.Transport}
	if api.Timeout > 0 {
		client.Timeout = api.Timeout
	}
	resp, err := client.Post(requestUrl, "application/json", bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Response Body:%s", string(body))
	}
	if err = chimpErrorCheck(body); err != nil {
		return nil, err
	}
	return body, nil
}

func runMandrill(api *MandrillAPI, path string, parameters map[string]interface{}) ([]byte, error) {
	if parameters == nil {
		parameters = make(map[string]interface{})
	}
	parameters["key"] = api.Key
	b, err := json.Marshal(parameters)
	if debug {
		log.Printf("Payload:%s", string(b))
	}
	if err != nil {
		return nil, err
	}
	requestUrl := fmt.Sprintf("%s%s", api.endpoint, path)
	if debug {
		log.Printf("Request URL:%s", requestUrl)
	}
	client := &http.Client{Transport: api.Transport}
	if api.Timeout > 0 {
		client.Timeout = api.Timeout
	}
	resp, err := client.Post(requestUrl, "application/json", bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Response Body:%s", string(body))
	}
	if err := mandrillErrorCheck(body); err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK && resp.Header.Get("Content-Type") != "application/json" {
		// don't bother trying to parse
		return nil, fmt.Errorf("fetch failure: HTTP %s", resp.Status)
	}
	return body, nil
}

func parseString(body []byte, err error) (string, error) {
	if err != nil {
		return "", err
	}
	return strconv.Unquote(string(body))
}

func parseMandrillJson(api *MandrillAPI, path string, parameters map[string]interface{}, retval interface{}) error {
	body, err := runMandrill(api, path, parameters)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(body, retval); err != nil {
		return err
	}

	return nil
}

func parseChimpJson(api *ChimpAPI, method string, parameters interface{}, retval interface{}) error {
	body, err := runChimp(api, method, parameters)

	if err != nil {
		return err
	}
	if retval != nil {
		return parseJson(body, retval)
	}
	return nil
}

type JsonAlterer interface {
	alterJson(b []byte) []byte
}

func parseJson(body []byte, retval interface{}) error {
	switch r := retval.(type) {
	case JsonAlterer:
		return json.Unmarshal(r.alterJson(body), retval)
	default:
		return json.Unmarshal(body, retval)
	}
}
<commit_msg>use mime.ParseMediaType<commit_after>// Copyright 2013 Matthew Baird
// 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.

/*
The API package provides the basic support for using HTTP to talk to the Mandrill and Mailchimp API's.
Each Struct contains a Key, Transport and endpoint property
*/
package gochimp

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"time"
)

const (
	mandrill_uri     = "mandrillapp.com"
	mandrill_version = "/api/1.0"
)

type MandrillAPI struct {
	Key       string
	Transport http.RoundTripper
	Timeout   time.Duration
	endpoint  string
}

type ChimpAPI struct {
	Key       string
	Transport http.RoundTripper
	Timeout   time.Duration
	endpoint  string
}

// see https://mandrillapp.com/api/docs/
// currently supporting json output formats
func NewMandrill(apiKey string) (*MandrillAPI, error) {
	u := url.URL{}
	u.Scheme = "https"
	u.Host = mandrill_uri
	u.Path = mandrill_version
	return &MandrillAPI{Key: apiKey, endpoint: u.String()}, nil
}

const mailchimp_uri string = "%s.api.mailchimp.com"
const mailchimp_version string = "/2.0"
const debug bool = false

var mailchimp_datacenter = regexp.MustCompile("[a-z]+[0-9]+$")

func NewChimp(apiKey string, https bool) *ChimpAPI {
	u := url.URL{}
	if https {
		u.Scheme = "https"
	} else {
		u.Scheme = "http"
	}
	u.Host = fmt.Sprintf("%s.api.mailchimp.com", mailchimp_datacenter.FindString(apiKey))
	u.Path = mailchimp_version
	return &ChimpAPI{Key: apiKey, endpoint: u.String()}
}

func runChimp(api *ChimpAPI, path string, parameters interface{}) ([]byte, error) {
	b, err := json.Marshal(parameters)
	if err != nil {
		return nil, err
	}
	requestUrl := fmt.Sprintf("%s%s", api.endpoint, path)
	if debug {
		log.Printf("Request URL:%s", requestUrl)
	}
	client := &http.Client{Transport: api.Transport}
	if api.Timeout > 0 {
		client.Timeout = api.Timeout
	}
	resp, err := client.Post(requestUrl, "application/json", bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Response Body:%s", string(body))
	}
	if err = chimpErrorCheck(body); err != nil {
		return nil, err
	}
	return body, nil
}

func runMandrill(api *MandrillAPI, path string, parameters map[string]interface{}) ([]byte, error) {
	if parameters == nil {
		parameters = make(map[string]interface{})
	}
	parameters["key"] = api.Key
	b, err := json.Marshal(parameters)
	if debug {
		log.Printf("Payload:%s", string(b))
	}
	if err != nil {
		return nil, err
	}
	requestUrl := fmt.Sprintf("%s%s", api.endpoint, path)
	if debug {
		log.Printf("Request URL:%s", requestUrl)
	}
	client := &http.Client{Transport: api.Transport}
	if api.Timeout > 0 {
		client.Timeout = api.Timeout
	}
	resp, err := client.Post(requestUrl, "application/json", bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if debug {
		log.Printf("Response Code:%d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Response Body:%s", string(body))
	}
	if err := mandrillErrorCheck(body); err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		if debug {
			log.Printf("Response Content-Type:%s", resp.Header.Get("Content-Type"))
		}
		typ, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		if err != nil || typ != "application/json" {
			// response doesn't look like JSON; don't bother trying to parse (so that we can return a more
			// user-friendly error)
			return nil, fmt.Errorf("request failure: HTTP %s", resp.Status)
		}
	}
	return body, nil
}

func parseString(body []byte, err error) (string, error) {
	if err != nil {
		return "", err
	}
	return strconv.Unquote(string(body))
}

func parseMandrillJson(api *MandrillAPI, path string, parameters map[string]interface{}, retval interface{}) error {
	body, err := runMandrill(api, path, parameters)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(body, retval); err != nil {
		return err
	}

	return nil
}

func parseChimpJson(api *ChimpAPI, method string, parameters interface{}, retval interface{}) error {
	body, err := runChimp(api, method, parameters)

	if err != nil {
		return err
	}
	if retval != nil {
		return parseJson(body, retval)
	}
	return nil
}

type JsonAlterer interface {
	alterJson(b []byte) []byte
}

func parseJson(body []byte, retval interface{}) error {
	switch r := retval.(type) {
	case JsonAlterer:
		return json.Unmarshal(r.alterJson(body), retval)
	default:
		return json.Unmarshal(body, retval)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"./helpers"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"strings"
)

type Space struct {
	SpaceID string
	Space   []Player
}

type Player struct {
	LocalIP  string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(so socketio.Socket, msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	localIP := ipNumbers[0]
	spaceID := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceID", spaceID)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceID))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			SpaceID: spaceID,
			Space: []Player{
				{
					LocalIP:  localIP,
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.LocalIP == localIP {
			known = true
			TRACE.Println("socket.io->Logon known LocalIP", element.LocalIP, "in Space", spaceID)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown LocalIP", localIP, "is added")

		player := Player{
			LocalIP:  localIP,
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("socket.io->Logon added", space)
	}

	so.Emit("updatechat", space)
}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<commit_msg>Logon returns now channel wit uuid<commit_after>package main

import (
	"./helpers"
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"strings"
)

type Space struct {
	Channel string
	SpaceID string
	Space   []Player
}

type Player struct {
	LocalIP  string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(so socketio.Socket, msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	localIP := ipNumbers[0]
	spaceID := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceID", spaceID)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceID))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceID: spaceID,
			Space: []Player{
				{
					LocalIP:  localIP,
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.LocalIP == localIP {
			known = true
			TRACE.Println("socket.io->Logon known LocalIP", element.LocalIP, "in Space", spaceID)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown LocalIP", localIP, "is added")

		player := Player{
			LocalIP:  localIP,
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("socket.io->Logon added", space)
	}

	so.Emit("updatechat", space)
}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/exercism/cli/configuration"
	"io/ioutil"
	"net/http"
)

const VERSION = "1.2.3"

var FetchEndpoints = map[string]string{
	"current":  "/api/v1/user/assignments/current",
	"next":     "/api/v1/user/assignments/next",
	"demo":     "/api/v1/assignments/demo",
	"exercise": "/api/v1/assignments",
}

type submitResponse struct {
	Id             string
	Status         string
	Language       string
	Exercise       string
	SubmissionPath string `json:"submission_path"`
	Error          string
}

type submitRequest struct {
	Key  string `json:"key"`
	Code string `json:"code"`
	Path string `json:"path"`
}

func FetchAssignments(config configuration.Config, path string) (as []Assignment, err error) {
	url := fmt.Sprintf("%s%s?key=%s", config.Hostname, path, config.ApiKey)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error fetching assignments: [%v]", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Error fetching assignments. HTTP Status Code: %d", resp.StatusCode)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	if err != nil {
		err = fmt.Errorf("Error fetching assignments: [%v]", err)
		return
	}

	var fr struct {
		Assignments []Assignment
	}

	err = json.Unmarshal(body, &fr)
	if err != nil {
		err = fmt.Errorf("Error parsing API response: [%v]", err)
		return
	}

	return fr.Assignments, err
}

func UnsubmitAssignment(config configuration.Config) (r string, err error) {
	path := "api/v1/user/assignments"

	url := fmt.Sprintf("%s/%s?key=%s", config.Hostname, path, config.ApiKey)

	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}

	req.Header.Set("User-Agent", fmt.Sprintf("github.com/kytrinyx/exercism CLI v%s", VERSION))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error destroying submission: [%v]", err)
		return
	}

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

	if resp.StatusCode != http.StatusNoContent {

		var ur struct {
			Error string
		}

		err = json.Unmarshal(body, &ur)
		if err != nil {
			return
		}

		err = fmt.Errorf("Status: %d, Error: %v", resp.StatusCode, ur.Error)
		return ur.Error, err
	}

	return
}

func SubmitAssignment(config configuration.Config, filePath string, code []byte) (r submitResponse, err error) {
	path := "api/v1/user/assignments"

	url := fmt.Sprintf("%s/%s", config.Hostname, path)

	submission := submitRequest{Key: config.ApiKey, Code: string(code), Path: filePath}
	submissionJson, err := json.Marshal(submission)
	if err != nil {
		return
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(submissionJson))
	if err != nil {
		return
	}

	req.Header.Set("User-Agent", fmt.Sprintf("github.com/kytrinyx/exercism CLI v%s", VERSION))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error posting assignment: [%v]", err)
		return
	}

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

	if resp.StatusCode != http.StatusCreated {
		err = json.Unmarshal(body, &r)
		if err != nil {
			return
		}
		err = fmt.Errorf("Status: %d, Error: %v", resp.StatusCode, r)
		return
	}

	err = json.Unmarshal(body, &r)
	if err != nil {
		err = fmt.Errorf("Error parsing API response: [%v]", err)
	}

	return
}
<commit_msg>Bump version<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/exercism/cli/configuration"
	"io/ioutil"
	"net/http"
)

const VERSION = "1.2.4"

var FetchEndpoints = map[string]string{
	"current":  "/api/v1/user/assignments/current",
	"next":     "/api/v1/user/assignments/next",
	"demo":     "/api/v1/assignments/demo",
	"exercise": "/api/v1/assignments",
}

type submitResponse struct {
	Id             string
	Status         string
	Language       string
	Exercise       string
	SubmissionPath string `json:"submission_path"`
	Error          string
}

type submitRequest struct {
	Key  string `json:"key"`
	Code string `json:"code"`
	Path string `json:"path"`
}

func FetchAssignments(config configuration.Config, path string) (as []Assignment, err error) {
	url := fmt.Sprintf("%s%s?key=%s", config.Hostname, path, config.ApiKey)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error fetching assignments: [%v]", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Error fetching assignments. HTTP Status Code: %d", resp.StatusCode)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	if err != nil {
		err = fmt.Errorf("Error fetching assignments: [%v]", err)
		return
	}

	var fr struct {
		Assignments []Assignment
	}

	err = json.Unmarshal(body, &fr)
	if err != nil {
		err = fmt.Errorf("Error parsing API response: [%v]", err)
		return
	}

	return fr.Assignments, err
}

func UnsubmitAssignment(config configuration.Config) (r string, err error) {
	path := "api/v1/user/assignments"

	url := fmt.Sprintf("%s/%s?key=%s", config.Hostname, path, config.ApiKey)

	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}

	req.Header.Set("User-Agent", fmt.Sprintf("github.com/kytrinyx/exercism CLI v%s", VERSION))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error destroying submission: [%v]", err)
		return
	}

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

	if resp.StatusCode != http.StatusNoContent {

		var ur struct {
			Error string
		}

		err = json.Unmarshal(body, &ur)
		if err != nil {
			return
		}

		err = fmt.Errorf("Status: %d, Error: %v", resp.StatusCode, ur.Error)
		return ur.Error, err
	}

	return
}

func SubmitAssignment(config configuration.Config, filePath string, code []byte) (r submitResponse, err error) {
	path := "api/v1/user/assignments"

	url := fmt.Sprintf("%s/%s", config.Hostname, path)

	submission := submitRequest{Key: config.ApiKey, Code: string(code), Path: filePath}
	submissionJson, err := json.Marshal(submission)
	if err != nil {
		return
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(submissionJson))
	if err != nil {
		return
	}

	req.Header.Set("User-Agent", fmt.Sprintf("github.com/kytrinyx/exercism CLI v%s", VERSION))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = fmt.Errorf("Error posting assignment: [%v]", err)
		return
	}

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

	if resp.StatusCode != http.StatusCreated {
		err = json.Unmarshal(body, &r)
		if err != nil {
			return
		}
		err = fmt.Errorf("Status: %d, Error: %v", resp.StatusCode, r)
		return
	}

	err = json.Unmarshal(body, &r)
	if err != nil {
		err = fmt.Errorf("Error parsing API response: [%v]", err)
	}

	return
}
<|endoftext|>


<commit_before>package mdqi

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"os/exec"

	"github.com/peterh/liner"
	"github.com/pkg/errors"
)

const (
	defaultMdqPath         = "mdq"
	defaultHistoryFilename = ".mdqi_history"
)

var Version string

var (
	ErrSlashCommandNotFound    = errors.New("unknown SlashCommand")
	ErrNotASlashCommand        = errors.New("there are no SlashCommand")
	ErrSlashCommandInvalidArgs = errors.New("invalid args")
	ErrUnknownPrinterName      = errors.New("unknown printer name")
)

type App struct {
	// Alive turns into false, mdqi will exit.
	Alive bool

	// mdqPath is path to mdq command.
	mdqPath string

	// mdqConfigPath is path to configuration file for mdq command.
	mdqConfigPath string

	// historyPath is path to command history file for liner.
	historyPath string

	// slashCommandDefinition holds SlashCommandDefinition.
	// app.slashCommandDefinition[category][name] = SlashCommandDefinition
	slashCommandDefinition map[string]map[string]SlashCommandDefinition

	// tag stores tag value for --tag option of mdq.
	tag string

	printer Printer
}

type Result struct {
	Database string
	Columns  []string
	Rows     []map[string]interface{}
}

func init() {
	defaultOutput = os.Stdout
}

func NewApp(conf Conf) (*App, error) {
	// validate mdq path
	mdqPath := defaultMdqPath
	if path := conf.Mdq.Bin; path != "" {
		if err := lookMdqPath(conf.Mdq.Bin); err != nil {
			return nil, errors.Wrapf(err, "mdq command not found at %s", path)
		}
		mdqPath = path
		debug.Println("conf.Mdq.Bin =", path)
	}

	// mdq config path
	if path := conf.Mdq.Config; path != "" {
		debug.Println("conf.Mdq.Config =", path)
	}

	// create history file
	historyPath := conf.Mdqi.History
	if path := conf.Mdqi.History; path != "" {
		var err error
		if err = createHistoryFile(path); err != nil {
			return nil, errors.Wrapf(err, "failed to create history file at %s", path)
		}
		historyPath = path
		debug.Println("conf.Mdqi.History =", historyPath)
	}

	app := &App{
		Alive: true,

		mdqPath:                mdqPath,
		mdqConfigPath:          conf.Mdq.Config,
		historyPath:            historyPath,
		slashCommandDefinition: map[string]map[string]SlashCommandDefinition{},
		printer:                HorizontalPrinter{},
	}

	// set default tag
	if tag := conf.Mdqi.DefaultTag; tag != "" {
		app.SetTag(tag)
		debug.Println("conf.Mdqi.DefaultTag =", tag)
	}

	// set default display
	if display := conf.Mdqi.DefaultDisplay; display != "" {
		if err := app.SetPrinterByName(display); err != nil {
			return nil, errors.Wrap(err, "failed to set default printer")
		}

		debug.Println("conf.Mdqi.DefaultDisplay =", display)
	}

	return app, nil
}

func createHistoryFile(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		if _, err := os.Create(path); err != nil {
			return errors.Wrap(err, "failed to create history file")
		}
	}

	return nil
}

func defaultHistoryPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return filepath.Join(usr.HomeDir, defaultHistoryFilename), err
}

func lookMdqPath(path string) error {
	_, err := exec.LookPath(path)

	return err
}

func (app *App) slashCommandCategories() []string {
	defs := app.slashCommandDefinition
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) slashCommandNames(category string) []string {
	defs := app.slashCommandDefinition[category]
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) Run() {
	app.runLiner()
}

func (app *App) runLiner() {
	line := liner.NewLiner()
	defer line.Close()

	line.SetCtrlCAborts(true)

	app.initHistory(line)

	rgxFinishLine := regexp.MustCompile(";$")
	lineFinished := true

	var l string
	var err error

LOOP:
	for {
		if !app.Alive {
			fmt.Println("bye")
			break LOOP
		}

		if lineFinished {
			l, err = line.Prompt("mdq> ")
		} else {
			var ll string
			ll, err = line.Prompt("   | ")
			l = strings.Join([]string{l, ll}, " ")
		}

		switch err {
		case nil:
			l = strings.Trim(l, " \n")

			if lineFinished = rgxFinishLine.MatchString(l); lineFinished {
			} else {
				// If line is not finished, read next line as continue.
				continue
			}

			if l == "" {
				continue
			}

			line.AppendHistory(l)

			scmd, _ := ParseSlashCommand(l)
			if scmd != nil {
				app.runSlashCommand(scmd)
				continue
			}

			results, err := app.RunCmd(l, app.buildCmdArgs()...)
			if err != nil {
				logger.Println(err.Error())
			}

			Print(app.printer, results)
		case liner.ErrPromptAborted:
			logger.Println("aborted")
			break LOOP
		case io.EOF:
			fmt.Println("bye")
			break LOOP
		default:
			logger.Println("error on reading line: ", err)
			break LOOP
		}

		app.saveHistory(line)
	}
}

func (app *App) initHistory(line *liner.State) {
	if f, err := os.Open(app.historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	} else {
		logger.Println("failed to read command history: ", err)
	}
}

func (app *App) saveHistory(line *liner.State) {
	if f, err := os.Create(app.historyPath); err == nil {
		if _, err := line.WriteHistory(f); err != nil {
			logger.Println("failed to write history: ", err)
		}

		f.Close()
	} else {
		logger.Println("failed to create history file: ", err)
	}
}

func (app *App) buildCmdArgs() []string {
	args := []string{}

	// config
	if path := app.mdqConfigPath; path != "" {
		args = append(args, "--config="+path)
	}

	// tag
	if tag := app.tag; tag != "" {
		args = append(args, "--tag="+tag)
	}

	return args
}

func (app *App) runSlashCommand(scmd *SlashCommand) {
	sdef, err := app.FindSlashCommandDefinition(scmd.Category, scmd.Name)

	switch err {
	case nil:
		if err := sdef.Handle(app, scmd); err != nil {
			logger.Println("failed to handle slash command:", err)
		}
	case ErrSlashCommandNotFound:
		logger.Println("unknown slash command")
	}

	return
}

func (app *App) SetPrinterByName(name string) error {

	switch name {
	case "horizontal":
		app.printer = HorizontalPrinter{}
	case "vertical":
		app.printer = VerticalPrinter{}
	default:
		return ErrUnknownPrinterName
	}

	return nil
}

func (app *App) GetTag() string {
	return app.tag
}

func (app *App) SetTag(tag string) {
	app.tag = tag
}

func (app *App) ClearTag() {
	app.tag = ""
}
<commit_msg>rename variable l -> input, ll -> l<commit_after>package mdqi

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"os/exec"

	"github.com/peterh/liner"
	"github.com/pkg/errors"
)

const (
	defaultMdqPath         = "mdq"
	defaultHistoryFilename = ".mdqi_history"
)

var Version string

var (
	ErrSlashCommandNotFound    = errors.New("unknown SlashCommand")
	ErrNotASlashCommand        = errors.New("there are no SlashCommand")
	ErrSlashCommandInvalidArgs = errors.New("invalid args")
	ErrUnknownPrinterName      = errors.New("unknown printer name")
)

type App struct {
	// Alive turns into false, mdqi will exit.
	Alive bool

	// mdqPath is path to mdq command.
	mdqPath string

	// mdqConfigPath is path to configuration file for mdq command.
	mdqConfigPath string

	// historyPath is path to command history file for liner.
	historyPath string

	// slashCommandDefinition holds SlashCommandDefinition.
	// app.slashCommandDefinition[category][name] = SlashCommandDefinition
	slashCommandDefinition map[string]map[string]SlashCommandDefinition

	// tag stores tag value for --tag option of mdq.
	tag string

	printer Printer
}

type Result struct {
	Database string
	Columns  []string
	Rows     []map[string]interface{}
}

func init() {
	defaultOutput = os.Stdout
}

func NewApp(conf Conf) (*App, error) {
	// validate mdq path
	mdqPath := defaultMdqPath
	if path := conf.Mdq.Bin; path != "" {
		if err := lookMdqPath(conf.Mdq.Bin); err != nil {
			return nil, errors.Wrapf(err, "mdq command not found at %s", path)
		}
		mdqPath = path
		debug.Println("conf.Mdq.Bin =", path)
	}

	// mdq config path
	if path := conf.Mdq.Config; path != "" {
		debug.Println("conf.Mdq.Config =", path)
	}

	// create history file
	historyPath := conf.Mdqi.History
	if path := conf.Mdqi.History; path != "" {
		var err error
		if err = createHistoryFile(path); err != nil {
			return nil, errors.Wrapf(err, "failed to create history file at %s", path)
		}
		historyPath = path
		debug.Println("conf.Mdqi.History =", historyPath)
	}

	app := &App{
		Alive: true,

		mdqPath:                mdqPath,
		mdqConfigPath:          conf.Mdq.Config,
		historyPath:            historyPath,
		slashCommandDefinition: map[string]map[string]SlashCommandDefinition{},
		printer:                HorizontalPrinter{},
	}

	// set default tag
	if tag := conf.Mdqi.DefaultTag; tag != "" {
		app.SetTag(tag)
		debug.Println("conf.Mdqi.DefaultTag =", tag)
	}

	// set default display
	if display := conf.Mdqi.DefaultDisplay; display != "" {
		if err := app.SetPrinterByName(display); err != nil {
			return nil, errors.Wrap(err, "failed to set default printer")
		}

		debug.Println("conf.Mdqi.DefaultDisplay =", display)
	}

	return app, nil
}

func createHistoryFile(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		if _, err := os.Create(path); err != nil {
			return errors.Wrap(err, "failed to create history file")
		}
	}

	return nil
}

func defaultHistoryPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return filepath.Join(usr.HomeDir, defaultHistoryFilename), err
}

func lookMdqPath(path string) error {
	_, err := exec.LookPath(path)

	return err
}

func (app *App) slashCommandCategories() []string {
	defs := app.slashCommandDefinition
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) slashCommandNames(category string) []string {
	defs := app.slashCommandDefinition[category]
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) Run() {
	app.runLiner()
}

func (app *App) runLiner() {
	line := liner.NewLiner()
	defer line.Close()

	line.SetCtrlCAborts(true)

	app.initHistory(line)

	rgxFinishLine := regexp.MustCompile(";$")
	lineFinished := true

	var input string
	var err error

LOOP:
	for {
		if !app.Alive {
			fmt.Println("bye")
			break LOOP
		}

		if lineFinished {
			input, err = line.Prompt("mdq> ")
		} else {
			var l string
			l, err = line.Prompt("   | ")
			input = strings.Join([]string{input, l}, " ")
		}

		switch err {
		case nil:
			input = strings.Trim(input, " \n")

			if lineFinished = rgxFinishLine.MatchString(input); lineFinished {
			} else {
				// If line is not finished, read next line as continue.
				continue
			}

			if input == "" {
				continue
			}

			line.AppendHistory(input)

			scmd, _ := ParseSlashCommand(input)
			if scmd != nil {
				app.runSlashCommand(scmd)
				continue
			}

			results, err := app.RunCmd(input, app.buildCmdArgs()...)
			if err != nil {
				logger.Println(err.Error())
			}

			Print(app.printer, results)
		case liner.ErrPromptAborted:
			logger.Println("aborted")
			break LOOP
		case io.EOF:
			fmt.Println("bye")
			break LOOP
		default:
			logger.Println("error on reading line: ", err)
			break LOOP
		}

		app.saveHistory(line)
	}
}

func (app *App) initHistory(line *liner.State) {
	if f, err := os.Open(app.historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	} else {
		logger.Println("failed to read command history: ", err)
	}
}

func (app *App) saveHistory(line *liner.State) {
	if f, err := os.Create(app.historyPath); err == nil {
		if _, err := line.WriteHistory(f); err != nil {
			logger.Println("failed to write history: ", err)
		}

		f.Close()
	} else {
		logger.Println("failed to create history file: ", err)
	}
}

func (app *App) buildCmdArgs() []string {
	args := []string{}

	// config
	if path := app.mdqConfigPath; path != "" {
		args = append(args, "--config="+path)
	}

	// tag
	if tag := app.tag; tag != "" {
		args = append(args, "--tag="+tag)
	}

	return args
}

func (app *App) runSlashCommand(scmd *SlashCommand) {
	sdef, err := app.FindSlashCommandDefinition(scmd.Category, scmd.Name)

	switch err {
	case nil:
		if err := sdef.Handle(app, scmd); err != nil {
			logger.Println("failed to handle slash command:", err)
		}
	case ErrSlashCommandNotFound:
		logger.Println("unknown slash command")
	}

	return
}

func (app *App) SetPrinterByName(name string) error {

	switch name {
	case "horizontal":
		app.printer = HorizontalPrinter{}
	case "vertical":
		app.printer = VerticalPrinter{}
	default:
		return ErrUnknownPrinterName
	}

	return nil
}

func (app *App) GetTag() string {
	return app.tag
}

func (app *App) SetTag(tag string) {
	app.tag = tag
}

func (app *App) ClearTag() {
	app.tag = ""
}
<|endoftext|>


<commit_before>package main

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

type apiHandler struct {}

func (s apiHandler) ServeHTTP(
  w http.ResponseWriter,
  r *http.Request) {

// TODO:
  // If authentication request, attempt authentication
  // Else
    // Reject non-authenticated requests
    // If user-preferences request, send user preferences
    // If user-preferences put, save user preferences
    // If file-hash request and if user-authorized, send file-hash
    // If file request and if user-authorized, send file
    // If file put and if user-authorized, save file

}

func main() {
  fs := http.FileServer(http.Dir("client"))
  http.Handle("/", fs)
  http.Handle("/api", apiHandler{})

  log.Println("Listening...")
  http.ListenAndServe(":3000", nil)
}
<commit_msg>mongoDB and basic authentication working.<commit_after>// TODO:
// If authentication request, attempt authentication
// Else
// Reject non-authenticated requests
// If user-preferences request, send user preferences
// If user-preferences put, save user preferences
// If file-hash request and if user-authorized, send file-hash
// If file request and if user-authorized, send file
// If file put and if user-authorized, save file

package main

import (
	"fmt"
	jwt "github.com/dgrijalva/jwt-go"
	bcrypt "golang.org/x/crypto/bcrypt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

type ApiHandler struct{}
type User struct {
	UserName string
	Hash     []byte
}

const (
	privKeyPath = "keys/app.rsa"     // openssl genrsa -out app.rsa keysize
	pubKeyPath  = "keys/app.rsa.pub" // openssl rsa -in app.rsa -pubout > app.rsa.pub
)

// keys are held in global variables
// i havn't seen a memory corruption/info leakage in go yet
// but maybe it's a better idea, just to store the public key in ram?
// and load the signKey on every signing request? depends on  your usage i guess
var (
	verifyKey, signKey []byte
	userDB, fileDB     *mgo.Collection
)

// read the key files before starting http handlers
func init() {
	var err error

	signKey, err = ioutil.ReadFile(privKeyPath)
	if err != nil {
		log.Fatal("Error reading private key")
		return
	}

	verifyKey, err = ioutil.ReadFile(pubKeyPath)
	if err != nil {
		log.Fatal("Error reading private key")
		return
	}
}

// just some html, to lazy for http.FileServer()
const (
	tokenName = "AccessToken"

	landingHtml = `
Welcome to the JWT Test

fun area

<form action="/authenticate" method="POST">
  <input type="text" name="user">
  <input type="password" name="pass">
  <input type="submit">
</form>`

	successHtml    = `<h2>Token Set - have fun!</h2><p>Go <a href="/">Back...</a></p>`
	restrictedHtml = `<h1>Welcome!!</h1><img src="https://httpcats.herokuapp.com/200" alt="" />`
)

// serves the form and restricted link
func landingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, landingHtml)
}

// reads the form values, checks them and creates the token
func authHandler(w http.ResponseWriter, r *http.Request) {
	// make sure its post
	if r.Method != "POST" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, "No POST", r.Method)
		return
	}

	username := r.FormValue("user")
	pass := r.FormValue("pass")

	log.Printf("Authenticate: user[%s] pass[%s]\n", username, pass)

	user := User{}
	err := userDB.Find(bson.M{"username": username}).One(&user)

	err = bcrypt.CompareHashAndPassword(user.Hash, []byte(pass))

	// check values
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintln(w, "Wrong info")
		return
	}

	// create a signer for rsa 256
	t := jwt.New(jwt.GetSigningMethod("RS256"))

	// set our claims
	t.Claims["AccessToken"] = "level1"
	t.Claims["CustomUserInfo"] = struct {
		Name string
		Kind string
	}{username, "human"}

	// set the expire time
	// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
	t.Claims["exp"] = time.Now().Add(time.Minute * 1).Unix()
	tokenString, err := t.SignedString(signKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Sorry, error while Signing Token!")
		log.Printf("Token Signing error: %v\n", err)
		return
	}

	// i know using cookies to store the token isn't really helpfull for cross domain api usage
	// but it's just an example and i did not want to involve javascript
	http.SetCookie(w, &http.Cookie{
		Name:       tokenName,
		Value:      tokenString,
		Path:       "/",
		RawExpires: "0",
	})

	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, successHtml)
}

// only accessible with a valid token
func restrictedHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		authHandler(w, r)
		return
	}
	// check if we have a cookie with out tokenName
	tokenCookie, err := r.Cookie(tokenName)
	switch {
	case err == http.ErrNoCookie:
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "No Token, no fun!")
		return
	case err != nil:
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Error while Parsing cookie!")
		log.Printf("Cookie parse error: %v\n", err)
		return
	}

	// just for the lulz, check if it is empty.. should fail on Parse anyway..
	if tokenCookie.Value == "" {
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "No Token, no fun!")
		return
	}

	// validate the token
	token, err := jwt.Parse(tokenCookie.Value, func(token *jwt.Token) (interface{}, error) {
		// since we only use the one private key to sign the tokens,
		// we also only use its public counter part to verify
		return verifyKey, nil
	})

	// branch out into the possible error from signing
	switch err.(type) {

	case nil: // no error

		if !token.Valid { // but may still be invalid
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprintln(w, "WHAT? Invalid Token? F*** off!")
			return
		}

		// see stdout and watch for the CustomUserInfo, nicely unmarshalled
		log.Printf("Someone accessed resricted area! Token:%+v\n", token)
		w.Header().Set("Content-Type", "text/html")
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, restrictedHtml)

	case *jwt.ValidationError: // something was wrong during the validation
		vErr := err.(*jwt.ValidationError)

		switch vErr.Errors {
		case jwt.ValidationErrorExpired:
			// w.WriteHeader(http.StatusUnauthorized)
			// fmt.Fprintln(w, "Token Expired, get a new one.")
			landingHandler(w, r)
			return

		default:
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, "Error while Parsing Token!")
			log.Printf("ValidationError error: %+v\n", vErr.Errors)
			return
		}

	default: // something else went wrong
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Error while Parsing Token!")
		log.Printf("Token parse error: %v\n", err)
		return
	}

}

func (s ApiHandler) ServeHTTP(
	w http.ResponseWriter,
	r *http.Request) {
}

func main() {
	// fs := http.FileServer(http.Dir("client"))
	// http.Handle("/", fs)
	// go http.ListenAndServeTLS(":8443", "certFile", "keyFile", &ApiHandler{})
	// log.Println("Listening...")
	// http.ListenAndServe(":3000", nil)

	// http.HandleFunc("/", landingHandler)
	// http.HandleFunc("/authenticate", authHandler)
	http.HandleFunc("/", restrictedHandler)
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()

	userDB = session.DB("Theseus").C("users")
	fileDB = session.DB("Theseus").C("files")

	// hash, _ := bcrypt.GenerateFromPassword([]byte("test"), 10)
	// testUser := User{"test", hash}
	// userDB.Insert(&testUser)

	log.Println("Listening...")
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package simplyput

// TODO: PropertyList can support nested objects with named properties like "A.B.C", support nested JSON objects.
// TODO: Add rudimentary single-property queries, pagination, sorting, etc.
// TODO: Add memcache
// TODO: Support ETags, If-Modified-Since, etc. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)
// TODO: PUT requests
// TODO: HEAD requests
// TODO: PATCH requests/semantics
// TODO: Batch requests (via multipart?)
// TODO: User POSTs a JSON schema, future requests are validated against that schema. Would anybody use that?

import (
	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	idKey        = "_id"
	createdKey   = "_created"
	updatedKey   = "_updated"
	defaultLimit = 10
)

func init() {
	http.HandleFunc("/datastore/v1dev/objects/", handle)
}

type userQuery struct {
	Limit, Offset int
	FilterKey, FilterType, FilterValue,
	StartCursor, EndCursor string
}

// getUserID gets the Google User ID for an access token.
func getUserID(accessToken string, client http.Client) (string, error) {
	resp, err := client.Get("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + accessToken)
	if err != nil {
		return "", err
	}
	var info struct {
		ID string
	}
	if err = json.NewDecoder(resp.Body).Decode(&info); err != nil {
		return "", err
	}
	resp.Body.Close()
	id := info.ID
	if id == "" {
		return "", errors.New("invalid auth")
	}
	return id, nil
}

// getKindAndID parses the kind and ID from a request path.
func getKindAndID(path string) (string, int64, error) {
	if match, err := regexp.MatchString("/datastore/v1dev/objects/[a-zA-Z]+/[0-9]+", path); err != nil {
		return "", int64(0), err
	} else if match {
		kind := path[len("/datastore/v1dev/objects/"):strings.LastIndex(path, "/")]
		idStr := path[strings.LastIndex(path, "/")+1:]
		id, err := strconv.ParseInt(idStr, 10, 64)
		if err != nil {
			return "", int64(0), err
		}
		return kind, id, nil
	}
	if match, err := regexp.MatchString("/datastore/v1dev/objects/[a-zA-Z]+", path); err != nil {
		return "", int64(0), err
	} else if match {
		kind := path[len("/datastore/v1dev/objects/"):]
		return kind, int64(0), nil
	}
	return "", int64(0), errors.New("invalid path")
}

// handle dispatches requests to the relevant API method and arranges certain common state
func handle(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	w.Header().Add("Access-Control-Allow-Origin", "*")

	r.ParseForm()
	client := urlfetch.Client(c)

	// Get the access_token from the request and turn it into a user ID with which we will namespace Kinds in the datastore.
	accessToken := r.Form.Get("access_token")
	if accessToken == "" {
		h := r.Header.Get("Authorization")
		if strings.HasPrefix(h, "Bearer ") {
			accessToken = h[len("Bearer "):]
		}
	}
	if accessToken == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	userID, err := getUserID(accessToken, *client)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	kind, id, err := getKindAndID(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	dsKind := fmt.Sprintf("%s--%s", userID, kind)

	resp := make(map[string]interface{}, 0)
	errCode := http.StatusOK
	if id == int64(0) {
		switch r.Method {
		case "POST":
			resp, errCode = insert(c, dsKind, r.Body)
			r.Body.Close()
		case "GET":
			resp, errCode = list(c, dsKind, newUserQuery(r))
		default:
			http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed)
			return
		}
	} else {
		switch r.Method {
		case "GET":
			resp, errCode = get(c, dsKind, id)
		case "DELETE":
			errCode = delete(c, dsKind, id)
		case "POST":
			// This is strictly "replace all properties/values", not "add new properties, update existing"
			resp, errCode = update(c, dsKind, id, r.Body)
			r.Body.Close()
		default:
			http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed)
			return
		}
	}
	if errCode != http.StatusOK {
		http.Error(w, "", errCode)
		return
	}
	if err := json.NewEncoder(w).Encode(&resp); err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}
	w.Header().Add("Content-Type", "application/json")
}

func newUserQuery(r *http.Request) userQuery {
	uq := userQuery{
		StartCursor: r.FormValue("start"),
		EndCursor:   r.FormValue("end"),
	}
	// TODO: MustParse for limit/offset, else panic
	uq.Limit, _ = strconv.Atoi(r.FormValue("limit"))
	uq.Offset, _ = strconv.Atoi(r.FormValue("offset"))

	// TODO: Support ?where=foo<bar queries (which may or may not be annoying to scope for users...)
	_ = r.FormValue("where")
	return uq
}

func delete(c appengine.Context, kind string, id int64) int {
	k := datastore.NewKey(c, kind, "", id, nil)
	if err := datastore.Delete(c, k); err != nil {
		if err == datastore.ErrNoSuchEntity {
			return http.StatusNotFound
		} else {
			c.Errorf("%v", err)
			return http.StatusInternalServerError
		}
	}
	return http.StatusOK
}

func get(c appengine.Context, kind string, id int64) (map[string]interface{}, int) {
	k := datastore.NewKey(c, kind, "", id, nil)
	var plist datastore.PropertyList
	if err := datastore.Get(c, k, &plist); err != nil {
		if err == datastore.ErrNoSuchEntity {
			return nil, http.StatusNotFound
		}
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m := plistToMap(plist, k)
	m[idKey] = k.IntID()
	return m, http.StatusOK
}

func insert(c appengine.Context, kind string, r io.Reader) (map[string]interface{}, int) {
	var m map[string]interface{}
	if err := json.NewDecoder(r).Decode(&m); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[createdKey] = time.Now().Unix()

	plist := mapToPlist(m)

	k := datastore.NewIncompleteKey(c, kind, nil)
	k, err := datastore.Put(c, k, &plist)
	if err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[idKey] = k.IntID()
	return m, http.StatusOK
}

// plistToMap transforms a PropertyList such as you would get from the datastore into a map[string]interface{} suitable for JSON-encoding.
func plistToMap(plist datastore.PropertyList, k *datastore.Key) map[string]interface{} {
	m := make(map[string]interface{})
	for _, p := range plist {
		if _, exists := m[p.Name]; exists {
			if _, isArr := m[p.Name].([]interface{}); isArr {
				m[p.Name] = append(m[p.Name].([]interface{}), p.Value)
			} else {
				m[p.Name] = []interface{}{m[p.Name], p.Value}
			}
		} else {
			m[p.Name] = p.Value
		}
	}
	m[idKey] = k.IntID()
	return m
}

// mapToPlist transforms a map[string]interface{} such as you would get from decoding JSON into a PropertyList to store in the datastore.
func mapToPlist(m map[string]interface{}) datastore.PropertyList {
	plist := make(datastore.PropertyList, 0, len(m))
	for k, v := range m {
		if _, mult := v.([]interface{}); mult {
			for _, mv := range v.([]interface{}) {
				plist = append(plist, datastore.Property{
					Name:     k,
					Value:    mv,
					Multiple: true,
				})
			}
		} else {
			plist = append(plist, datastore.Property{
				Name:  k,
				Value: v,
			})
		}
	}
	return plist
}

func list(c appengine.Context, kind string, uq userQuery) (map[string]interface{}, int) {
	q := datastore.NewQuery(kind)

	if uq.Limit != 0 {
		q = q.Limit(uq.Limit)
	}
	if c, err := datastore.DecodeCursor(uq.StartCursor); err == nil {
		q.Start(c)
	}
	if c, err := datastore.DecodeCursor(uq.EndCursor); err == nil {
		q.End(c)
	}

	items := make([]map[string]interface{}, 0)

	var crs datastore.Cursor
	for t := q.Run(c); ; {
		var plist datastore.PropertyList
		k, err := t.Next(&plist)
		if err == datastore.Done {
			break
		}
		if err != nil {
			c.Errorf("%v", err)
			return nil, http.StatusInternalServerError
		}
		m := plistToMap(plist, k)
		items = append(items, m)
		if crs, err = t.Cursor(); err != nil {
			c.Errorf("%v", err)
			return nil, http.StatusInternalServerError
		}
	}
	r := map[string]interface{}{
		"items":          items,
		"nextStartToken": crs.String(),
	}
	return r, http.StatusOK
}

func update(c appengine.Context, kind string, id int64, r io.Reader) (map[string]interface{}, int) {
	var m map[string]interface{}
	if err := json.NewDecoder(r).Decode(&m); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[updatedKey] = time.Now().Unix()

	plist := mapToPlist(m)

	k := datastore.NewKey(c, kind, "", id, nil)
	if _, err := datastore.Put(c, k, &plist); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[idKey] = id
	return m, http.StatusOK
}
<commit_msg>Factor out my own plist type so tests don't have to rely on datastore.PropertyList<commit_after>package simplyput

// TODO: PropertyList can support nested objects with named properties like "A.B.C", support nested JSON objects.
// TODO: Add rudimentary single-property queries, pagination, sorting, etc.
// TODO: Add memcache
// TODO: Support ETags, If-Modified-Since, etc. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)
// TODO: PUT requests
// TODO: HEAD requests
// TODO: PATCH requests/semantics
// TODO: Batch requests (via multipart?)
// TODO: User POSTs a JSON schema, future requests are validated against that schema. Would anybody use that?

import (
	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	idKey        = "_id"
	createdKey   = "_created"
	updatedKey   = "_updated"
	defaultLimit = 10
)

func init() {
	http.HandleFunc("/datastore/v1dev/objects/", handle)
}

type userQuery struct {
	Limit, Offset int
	FilterKey, FilterType, FilterValue,
	StartCursor, EndCursor string
}

// getUserID gets the Google User ID for an access token.
func getUserID(accessToken string, client http.Client) (string, error) {
	resp, err := client.Get("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + accessToken)
	if err != nil {
		return "", err
	}
	var info struct {
		ID string
	}
	if err = json.NewDecoder(resp.Body).Decode(&info); err != nil {
		return "", err
	}
	resp.Body.Close()
	id := info.ID
	if id == "" {
		return "", errors.New("invalid auth")
	}
	return id, nil
}

// getKindAndID parses the kind and ID from a request path.
func getKindAndID(path string) (string, int64, error) {
	if match, err := regexp.MatchString("/datastore/v1dev/objects/[a-zA-Z]+/[0-9]+", path); err != nil {
		return "", int64(0), err
	} else if match {
		kind := path[len("/datastore/v1dev/objects/"):strings.LastIndex(path, "/")]
		idStr := path[strings.LastIndex(path, "/")+1:]
		id, err := strconv.ParseInt(idStr, 10, 64)
		if err != nil {
			return "", int64(0), err
		}
		return kind, id, nil
	}
	if match, err := regexp.MatchString("/datastore/v1dev/objects/[a-zA-Z]+", path); err != nil {
		return "", int64(0), err
	} else if match {
		kind := path[len("/datastore/v1dev/objects/"):]
		return kind, int64(0), nil
	}
	return "", int64(0), errors.New("invalid path")
}

// handle dispatches requests to the relevant API method and arranges certain common state
func handle(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	w.Header().Add("Access-Control-Allow-Origin", "*")

	r.ParseForm()
	client := urlfetch.Client(c)

	// Get the access_token from the request and turn it into a user ID with which we will namespace Kinds in the datastore.
	accessToken := r.Form.Get("access_token")
	if accessToken == "" {
		h := r.Header.Get("Authorization")
		if strings.HasPrefix(h, "Bearer ") {
			accessToken = h[len("Bearer "):]
		}
	}
	if accessToken == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	userID, err := getUserID(accessToken, *client)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	kind, id, err := getKindAndID(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	dsKind := fmt.Sprintf("%s--%s", userID, kind)

	resp := make(map[string]interface{}, 0)
	errCode := http.StatusOK
	if id == int64(0) {
		switch r.Method {
		case "POST":
			resp, errCode = insert(c, dsKind, r.Body)
			r.Body.Close()
		case "GET":
			resp, errCode = list(c, dsKind, newUserQuery(r))
		default:
			http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed)
			return
		}
	} else {
		switch r.Method {
		case "GET":
			resp, errCode = get(c, dsKind, id)
		case "DELETE":
			errCode = delete(c, dsKind, id)
		case "POST":
			// This is strictly "replace all properties/values", not "add new properties, update existing"
			resp, errCode = update(c, dsKind, id, r.Body)
			r.Body.Close()
		default:
			http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed)
			return
		}
	}
	if errCode != http.StatusOK {
		http.Error(w, "", errCode)
		return
	}
	if err := json.NewEncoder(w).Encode(&resp); err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}
	w.Header().Add("Content-Type", "application/json")
}

func newUserQuery(r *http.Request) userQuery {
	uq := userQuery{
		StartCursor: r.FormValue("start"),
		EndCursor:   r.FormValue("end"),
	}
	// TODO: MustParse for limit/offset, else panic
	uq.Limit, _ = strconv.Atoi(r.FormValue("limit"))
	uq.Offset, _ = strconv.Atoi(r.FormValue("offset"))

	// TODO: Support ?where=foo<bar queries (which may or may not be annoying to scope for users...)
	_ = r.FormValue("where")
	return uq
}

func delete(c appengine.Context, kind string, id int64) int {
	k := datastore.NewKey(c, kind, "", id, nil)
	if err := datastore.Delete(c, k); err != nil {
		if err == datastore.ErrNoSuchEntity {
			return http.StatusNotFound
		} else {
			c.Errorf("%v", err)
			return http.StatusInternalServerError
		}
	}
	return http.StatusOK
}

func get(c appengine.Context, kind string, id int64) (map[string]interface{}, int) {
	k := datastore.NewKey(c, kind, "", id, nil)
	var pl plist
	if err := datastore.Get(c, k, &pl); err != nil {
		if err == datastore.ErrNoSuchEntity {
			return nil, http.StatusNotFound
		}
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m := plistToMap(pl, k)
	m[idKey] = k.IntID()
	return m, http.StatusOK
}

func insert(c appengine.Context, kind string, r io.Reader) (map[string]interface{}, int) {
	var m map[string]interface{}
	if err := json.NewDecoder(r).Decode(&m); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[createdKey] = time.Now().Unix()

	pl := mapToPlist(m)

	k := datastore.NewIncompleteKey(c, kind, nil)
	k, err := datastore.Put(c, k, &pl)
	if err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[idKey] = k.IntID()
	return m, http.StatusOK
}

type prop struct {
	Name string
	Value interface{}
	Multiple bool
	NoIndex bool
}
type plist []prop

// plistToMap transforms a plist such as you would get from the datastore into a map[string]interface{} suitable for JSON-encoding.
func plistToMap(pl plist, k *datastore.Key) map[string]interface{} {
	m := make(map[string]interface{})
	for _, p := range pl {
		if _, exists := m[p.Name]; exists {
			if _, isArr := m[p.Name].([]interface{}); isArr {
				m[p.Name] = append(m[p.Name].([]interface{}), p.Value)
			} else {
				m[p.Name] = []interface{}{m[p.Name], p.Value}
			}
		} else {
			m[p.Name] = p.Value
		}
	}
	m[idKey] = k.IntID()
	return m
}

// mapToPlist transforms a map[string]interface{} such as you would get from decoding JSON into a plist to store in the datastore.
func mapToPlist(m map[string]interface{}) plist {
	pl := make(plist, 0, len(m))
	for k, v := range m {
		if _, mult := v.([]interface{}); mult {
			for _, mv := range v.([]interface{}) {
				pl = append(pl, prop{
					Name:     k,
					Value:    mv,
					Multiple: true,
				})
			}
		} else {
			pl = append(pl, prop{
				Name:  k,
				Value: v,
			})
		}
	}
	return pl
}

func list(c appengine.Context, kind string, uq userQuery) (map[string]interface{}, int) {
	q := datastore.NewQuery(kind)

	if uq.Limit != 0 {
		q = q.Limit(uq.Limit)
	}
	if c, err := datastore.DecodeCursor(uq.StartCursor); err == nil {
		q.Start(c)
	}
	if c, err := datastore.DecodeCursor(uq.EndCursor); err == nil {
		q.End(c)
	}

	items := make([]map[string]interface{}, 0)

	var crs datastore.Cursor
	for t := q.Run(c); ; {
		var pl plist
		k, err := t.Next(&pl)
		if err == datastore.Done {
			break
		}
		if err != nil {
			c.Errorf("%v", err)
			return nil, http.StatusInternalServerError
		}
		m := plistToMap(pl, k)
		items = append(items, m)
		if crs, err = t.Cursor(); err != nil {
			c.Errorf("%v", err)
			return nil, http.StatusInternalServerError
		}
	}
	r := map[string]interface{}{
		"items":          items,
		"nextStartToken": crs.String(),
	}
	return r, http.StatusOK
}

func update(c appengine.Context, kind string, id int64, r io.Reader) (map[string]interface{}, int) {
	var m map[string]interface{}
	if err := json.NewDecoder(r).Decode(&m); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[updatedKey] = time.Now().Unix()

	pl := mapToPlist(m)

	k := datastore.NewKey(c, kind, "", id, nil)
	if _, err := datastore.Put(c, k, &pl); err != nil {
		c.Errorf("%v", err)
		return nil, http.StatusInternalServerError
	}
	m[idKey] = id
	return m, http.StatusOK
}
<|endoftext|>


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

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"
)

var tmpl = template.Must(template.New("").Parse(`
<html>
 <head>
  <meta name="go-import" content="{{.PkgPath}} git {{.Repo}}">
 </head>
</html>
`))

const (
	githubOrg       = "azul3d"
	repoAliasHost   = "azul3d.org"
	repoAliasScheme = "http"
	fileHost        = "azul3d.github.io"
)

var lastIdlePurge = time.Now()

// isTip is short-hand for:
//  return version == "v0" || version == "dev"
func isTip(version string) bool {
	return version == "v0" || version == "dev"
}

// Pulls version tag from the URL. It would be at the last part of the URL
// like so:
//  foobar.org/something/something/maybe/here.v0
//  foobar.org/something/something/maybe/here.dev
//  foobar.org/something/something/maybe/here.v1.2
//
// NOT like:
//  foobar.org/something/something/maybe/here.v1.2/info/refs
//
// Always returns "dev" for any .dev or .v0 string.
func versionFromEnd(p string) string {
	if strings.HasSuffix(p, "dev") || strings.HasSuffix(p, "v0") {
		return "dev"
	}
	// he.re.v1.2
	split := strings.Split(path.Base(p), ".v")
	if len(split) > 1 {
		return "v" + split[len(split)-1]
	}
	return ""
}

// Takes a string like:
//  cmd/foo/bar.dev
//  cmd/foo/bar.v1
// and returns:
//  cmd-foo-bar
func gitRepoName(path string) string {
	// Change cmd/foo to cmd-foo
	path = strings.Replace(path, "/", "-", -1)
	// Strip version from end.
	return strings.Split(path, ".")[0]
}

func handleGoTool(w http.ResponseWriter, r *http.Request) bool {
	// Clean the URL.
	u := path.Clean(r.URL.Path)

	// Parse the query.
	query, _ := url.ParseQuery(r.URL.RawQuery)

	// If the client is the 'go get' tool, then we serve them a small page that
	// just contains the go-import meta tag -- that's all.
	if r.Method == "GET" && len(query.Get("go-get")) > 0 {
		repo := *r.URL
		repo.Host = repoAliasHost
		repo.Scheme = repoAliasScheme
		repo.Path = path.Join(u, "repo")
		repo.RawQuery = ""
		tmpl.Execute(w, map[string]interface{}{
			"Repo":    repo.String(),
			"PkgPath": path.Join(repoAliasHost, u),
		})
		return true
	}

	// If the client asks for /info/refs then we fetch them from the git repo
	// and serve them to the client.
	if r.Method == "GET" && strings.HasSuffix(u, "/info/refs") && query.Get("service") == "git-upload-pack" {
		// Strip /repo/info/refs from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-3]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		if len(version) == 0 {
			// The path doesn't have a version in it, we can't serve this
			// request.
			log.Printf("Request without version in URL.\n")
			w.WriteHeader(http.StatusNotFound)
			return true
		}

		// Create URL to target repo's /info/refs
		target := &url.URL{
			Scheme:   "http",
			Host:     "github.com",
			Path:     path.Join(githubOrg, repoName+".git", "/info/refs"),
			RawQuery: "service=git-upload-pack",
		}

		// Fetch info/refs from target repository.
		//log.Printf("fetchRefs from %s\n", target.String())
		refs, err := fetchRefs(target.String())
		if err != nil {
			log.Printf("Failed to fetch remote refs: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return true
		}

		if !isTip(version) {
			//log.Printf("\n\nHack git refs:\n\n%s\n", string(refs.data))
			// Hack the git refs to the given version.
			err = refs.hack(version)
			if err != nil {
				log.Printf("%v\n", err)
				w.WriteHeader(http.StatusNotFound)
				return true
			}
			//log.Printf("\n\nAFTER HACK:\n\n%s\n", string(refs.data))
		}

		w.Header().Set("Content-Type", "application/x-git-upload-pack-advertisement")
		w.Write(refs.data)
		return true
	}

	// If the client wants to POST to /git-upload-pack we redirect their
	// request to the actual git repo.
	if r.Method == "POST" && strings.HasSuffix(u, "/git-upload-pack") {
		// Strip /repo/git-upload-pack from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-2]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		// Create URL to target repo's /git-upload-pack
		target := &url.URL{
			Scheme: "http",
			Host:   "github.com",
			Path:   path.Join(githubOrg, repoName+".git", "/git-upload-pack"),
		}

		w.Header().Set("Location", target.String())
		w.WriteHeader(http.StatusMovedPermanently)
		return true
	}

	// /info/refs for service=git-receive-pack is just forwarded to the repo
	// directly. This occurs when pushing changes via git.
	if r.Method == "GET" && strings.HasSuffix(u, "/info/refs") && query.Get("service") == "git-receive-pack" {
		// Strip /repo/info/refs from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-3]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		// Create URL to target repo's /info/refs
		target := &url.URL{
			Scheme:   "http",
			Host:     "github.com",
			Path:     path.Join(githubOrg, repoName+".git", "/info/refs"),
			RawQuery: "service=git-receive-pack",
		}

		http.Redirect(w, r, target.String(), http.StatusSeeOther)
		return true
	}

	return false
}

func handler(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v %v\n", r.Method, r.URL)

	// Purge idle connections.
	if time.Since(lastIdlePurge) > 2*time.Hour {
		lastIdlePurge = time.Now()
		http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	}

	// If it's the Go tool (or git HTTP, etc) then we let that function handle
	// it.
	if handleGoTool(w, r) {
		return
	}

	// Just proxy the request to the file host then (it's an actual user -- not
	// the Go tool).
	if r.URL.Scheme == "" {
		r.URL.Scheme = "http"
	}
	r.RequestURI = ""
	delete(r.Header, "Content-Length")

	// Change Host in URL so that the request goes to the file host.
	r.URL.Host = fileHost

	// Force the Host header to be the file host (github.io uses this header).
	r.Host = fileHost

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		log.Printf("GET error: %v\n", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer resp.Body.Close()

	// Copy headers over.
	hdr := w.Header()
	for k, v := range resp.Header {
		hdr[k] = v
	}

	// Peek to detect the content type.
	br := bufio.NewReaderSize(resp.Body, 1024)
	if r.Method != "HEAD" && len(resp.Header.Get("If-Modified-Since")) != 0 {
		ident, err := br.Peek(512)
		if err != nil {
			log.Printf("Proxy peek error: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		hdr["Content-Type"] = []string{http.DetectContentType(ident)}
	} else if len(versionFromEnd(r.URL.Path)) > 0 {
		// .dev and versioned files (.v0, .v1.2 etc) are always HTML files.
		hdr["Content-Type"] = []string{
			"text/html",
			"charset=utf-8",
		}
	}

	// Write the header / status code.
	w.WriteHeader(resp.StatusCode)

	// Copy the response to the user.
	_, err = io.Copy(w, br)
	if err != nil {
		log.Printf("Proxy copy error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	return
}

var (
	addr = flag.String("http", ":80", "HTTP address to serve on")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", handler)
	log.Println("Serving on", *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}

type gitRefs struct {
	data []byte
}

var (
	ErrRepoNotFound    = errors.New("git repository not found")
	ErrVersionNotFound = errors.New("failed to find version in git refs")
)

func (r *gitRefs) hack(version string) error {
	var mrefi, mrefj, vrefi, vrefj int

	vhead := "refs/heads/" + version
	vtag := "refs/tags/" + version

	data := r.data
	sdata := string(r.data)
	for i, j := 0, 0; i < len(data); i = j {
		size, err := strconv.ParseInt(sdata[i:i+4], 16, 32)
		if err != nil {
			return fmt.Errorf("cannot parse refs line size: %s", string(data[i:i+4]))
		}
		if size == 0 {
			size = 4
		}
		j = i + int(size)
		if j > len(sdata) {
			return fmt.Errorf("incomplete refs data received from repo")
		}
		if sdata[0] == '#' {
			continue
		}

		hashi := i + 4
		hashj := strings.IndexByte(sdata[hashi:j], ' ')
		if hashj < 0 || hashj != 40 {
			continue
		}
		hashj += hashi

		namei := hashj + 1
		namej := strings.IndexAny(sdata[namei:j], "\n\x00")
		if namej < 0 {
			namej = j
		} else {
			namej += namei
		}

		name := sdata[namei:namej]

		if name == "refs/heads/master" {
			mrefi = hashi
			mrefj = hashj
		}

		if strings.HasPrefix(name, "refs/heads/v") || strings.HasPrefix(name, "refs/tags/v") {
			// Annotated tag is peeled off and overrides the same version just parsed.
			name = strings.TrimSuffix(name, "^{}")
			if name == vtag || name == vhead {
				vrefi = hashi
				vrefj = hashj
			}
		}

		//if mrefi > 0 && vrefi > 0 {
		//	break
		//}
	}

	if mrefi == 0 || vrefi == 0 {
		return ErrVersionNotFound
	}

	copy(data[mrefi:mrefj], data[vrefi:vrefj])
	return nil
}

func fetchRefs(refsURL string) (*gitRefs, error) {
	resp, err := http.Get(refsURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusNotFound {
			return nil, ErrRepoNotFound
		} else {
			return nil, fmt.Errorf("error from repo: %v", resp.Status)
		}
	}

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

	return &gitRefs{
		data: data,
	}, nil
}
<commit_msg>Support HTTPS (fixes azul3d/issues#24)<commit_after>// Copyright 2014 The Azul3D Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"
)

var tmpl = template.Must(template.New("").Parse(`
<html>
 <head>
  <meta name="go-import" content="{{.PkgPath}} git {{.Repo}}">
 </head>
</html>
`))

const (
	githubOrg     = "azul3d"
	repoAliasHost = "azul3d.org"
	fileHost      = "azul3d.github.io"
	certFile      = "azul3d.org.pem"
	keyFile       = "azul3d.org.key"
)

var lastIdlePurge = time.Now()

// isTip is short-hand for:
//  return version == "v0" || version == "dev"
func isTip(version string) bool {
	return version == "v0" || version == "dev"
}

// Pulls version tag from the URL. It would be at the last part of the URL
// like so:
//  foobar.org/something/something/maybe/here.v0
//  foobar.org/something/something/maybe/here.dev
//  foobar.org/something/something/maybe/here.v1.2
//
// NOT like:
//  foobar.org/something/something/maybe/here.v1.2/info/refs
//
// Always returns "dev" for any .dev or .v0 string.
func versionFromEnd(p string) string {
	if strings.HasSuffix(p, "dev") || strings.HasSuffix(p, "v0") {
		return "dev"
	}
	// he.re.v1.2
	split := strings.Split(path.Base(p), ".v")
	if len(split) > 1 {
		return "v" + split[len(split)-1]
	}
	return ""
}

// Takes a string like:
//  cmd/foo/bar.dev
//  cmd/foo/bar.v1
// and returns:
//  cmd-foo-bar
func gitRepoName(path string) string {
	// Change cmd/foo to cmd-foo
	path = strings.Replace(path, "/", "-", -1)
	// Strip version from end.
	return strings.Split(path, ".")[0]
}

func handleGoTool(w http.ResponseWriter, r *http.Request) bool {
	// Clean the URL.
	u := path.Clean(r.URL.Path)

	// Parse the query.
	query, _ := url.ParseQuery(r.URL.RawQuery)

	// If the client is the 'go get' tool, then we serve them a small page that
	// just contains the go-import meta tag -- that's all.
	if r.Method == "GET" && len(query.Get("go-get")) > 0 {
		repo := *r.URL
		repo.Host = repoAliasHost
		repo.Path = path.Join(u, "repo")
		repo.RawQuery = ""
		tmpl.Execute(w, map[string]interface{}{
			"Repo":    repo.String(),
			"PkgPath": path.Join(repoAliasHost, u),
		})
		return true
	}

	// If the client asks for /info/refs then we fetch them from the git repo
	// and serve them to the client.
	if r.Method == "GET" && strings.HasSuffix(u, "/info/refs") && query.Get("service") == "git-upload-pack" {
		// Strip /repo/info/refs from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-3]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		if len(version) == 0 {
			// The path doesn't have a version in it, we can't serve this
			// request.
			log.Printf("Request without version in URL.\n")
			w.WriteHeader(http.StatusNotFound)
			return true
		}

		// Create URL to target repo's /info/refs
		target := &url.URL{
			Scheme:   r.URL.Scheme,
			Host:     "github.com",
			Path:     path.Join(githubOrg, repoName+".git", "/info/refs"),
			RawQuery: "service=git-upload-pack",
		}

		// Fetch info/refs from target repository.
		//log.Printf("fetchRefs from %s\n", target.String())
		refs, err := fetchRefs(target.String())
		if err != nil {
			log.Printf("Failed to fetch remote refs: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return true
		}

		if !isTip(version) {
			//log.Printf("\n\nHack git refs:\n\n%s\n", string(refs.data))
			// Hack the git refs to the given version.
			err = refs.hack(version)
			if err != nil {
				log.Printf("%v\n", err)
				w.WriteHeader(http.StatusNotFound)
				return true
			}
			//log.Printf("\n\nAFTER HACK:\n\n%s\n", string(refs.data))
		}

		w.Header().Set("Content-Type", "application/x-git-upload-pack-advertisement")
		w.Write(refs.data)
		return true
	}

	// If the client wants to POST to /git-upload-pack we redirect their
	// request to the actual git repo.
	if r.Method == "POST" && strings.HasSuffix(u, "/git-upload-pack") {
		// Strip /repo/git-upload-pack from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-2]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		// Create URL to target repo's /git-upload-pack
		target := &url.URL{
			Scheme: r.URL.Scheme,
			Host:   "github.com",
			Path:   path.Join(githubOrg, repoName+".git", "/git-upload-pack"),
		}

		w.Header().Set("Location", target.String())
		w.WriteHeader(http.StatusMovedPermanently)
		return true
	}

	// /info/refs for service=git-receive-pack is just forwarded to the repo
	// directly. This occurs when pushing changes via git.
	if r.Method == "GET" && strings.HasSuffix(u, "/info/refs") && query.Get("service") == "git-receive-pack" {
		// Strip /repo/info/refs from URL.
		fp := strings.Split(u, "/")
		fp = fp[1 : len(fp)-3]
		fps := path.Join(fp...)
		version := versionFromEnd(fps)
		repoName := gitRepoName(strings.TrimSuffix(fps, version))
		//log.Printf("u=%q version=%q repoName=%q\n", u, version, repoName)

		// Create URL to target repo's /info/refs
		target := &url.URL{
			Scheme:   r.URL.Scheme,
			Host:     "github.com",
			Path:     path.Join(githubOrg, repoName+".git", "/info/refs"),
			RawQuery: "service=git-receive-pack",
		}

		http.Redirect(w, r, target.String(), http.StatusSeeOther)
		return true
	}

	return false
}

func handler(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v %v\n", r.Method, r.URL)

	// Purge idle connections.
	if time.Since(lastIdlePurge) > 2*time.Hour {
		lastIdlePurge = time.Now()
		http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	}

	if r.URL.Scheme == "" {
		r.URL.Scheme = "https"
	}

	// If it's the Go tool (or git HTTP, etc) then we let that function handle
	// it.
	if handleGoTool(w, r) {
		return
	}

	// Just proxy the request to the file host then (it's an actual user -- not
	// the Go tool).
	r.RequestURI = ""
	delete(r.Header, "Content-Length")

	// Change Host in URL so that the request goes to the file host.
	r.URL.Host = fileHost

	// Force the Host header to be the file host (github.io uses this header).
	r.Host = fileHost

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		log.Printf("GET error: %v\n", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer resp.Body.Close()

	// Copy headers over.
	hdr := w.Header()
	for k, v := range resp.Header {
		hdr[k] = v
	}

	// Peek to detect the content type.
	br := bufio.NewReaderSize(resp.Body, 1024)
	if r.Method != "HEAD" && len(resp.Header.Get("If-Modified-Since")) != 0 {
		ident, err := br.Peek(512)
		if err != nil {
			log.Printf("Proxy peek error: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		hdr["Content-Type"] = []string{http.DetectContentType(ident)}
	} else if len(versionFromEnd(r.URL.Path)) > 0 {
		// .dev and versioned files (.v0, .v1.2 etc) are always HTML files.
		hdr["Content-Type"] = []string{
			"text/html",
			"charset=utf-8",
		}
	}

	// Write the header / status code.
	w.WriteHeader(resp.StatusCode)

	// Copy the response to the user.
	_, err = io.Copy(w, br)
	if err != nil {
		log.Printf("Proxy copy error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	return
}

var (
	addr    = flag.String("http", ":80", "HTTP address to serve on")
	tlsaddr = flag.String("https", ":443", "HTTPS address to serve on")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", handler)

	// Start HTTPS server:
	go func() {
		log.Println("Serving on", *tlsaddr)
		err := http.ListenAndServeTLS(*tlsaddr, certFile, keyFile, nil)
		if err != nil {
			log.Fatal(err)
		}
	}()

	// Start HTTP server:
	log.Println("Serving on", *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}

type gitRefs struct {
	data []byte
}

var (
	ErrRepoNotFound    = errors.New("git repository not found")
	ErrVersionNotFound = errors.New("failed to find version in git refs")
)

func (r *gitRefs) hack(version string) error {
	var mrefi, mrefj, vrefi, vrefj int

	vhead := "refs/heads/" + version
	vtag := "refs/tags/" + version

	data := r.data
	sdata := string(r.data)
	for i, j := 0, 0; i < len(data); i = j {
		size, err := strconv.ParseInt(sdata[i:i+4], 16, 32)
		if err != nil {
			return fmt.Errorf("cannot parse refs line size: %s", string(data[i:i+4]))
		}
		if size == 0 {
			size = 4
		}
		j = i + int(size)
		if j > len(sdata) {
			return fmt.Errorf("incomplete refs data received from repo")
		}
		if sdata[0] == '#' {
			continue
		}

		hashi := i + 4
		hashj := strings.IndexByte(sdata[hashi:j], ' ')
		if hashj < 0 || hashj != 40 {
			continue
		}
		hashj += hashi

		namei := hashj + 1
		namej := strings.IndexAny(sdata[namei:j], "\n\x00")
		if namej < 0 {
			namej = j
		} else {
			namej += namei
		}

		name := sdata[namei:namej]

		if name == "refs/heads/master" {
			mrefi = hashi
			mrefj = hashj
		}

		if strings.HasPrefix(name, "refs/heads/v") || strings.HasPrefix(name, "refs/tags/v") {
			// Annotated tag is peeled off and overrides the same version just parsed.
			name = strings.TrimSuffix(name, "^{}")
			if name == vtag || name == vhead {
				vrefi = hashi
				vrefj = hashj
			}
		}

		//if mrefi > 0 && vrefi > 0 {
		//	break
		//}
	}

	if mrefi == 0 || vrefi == 0 {
		return ErrVersionNotFound
	}

	copy(data[mrefi:mrefj], data[vrefi:vrefj])
	return nil
}

func fetchRefs(refsURL string) (*gitRefs, error) {
	resp, err := http.Get(refsURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusNotFound {
			return nil, ErrRepoNotFound
		} else {
			return nil, fmt.Errorf("error from repo: %v", resp.Status)
		}
	}

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

	return &gitRefs{
		data: data,
	}, nil
}
<|endoftext|>


<commit_before>package unpuzzled

import (
	"fmt"
	"html/template"
	"os"

	"reflect"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	log "github.com/sirupsen/logrus"
)

type App struct {
	Name            string
	Usage           string
	LongDescription string
	Copyright       string
	ParsingOrder    []ParsingType
	Command         *Command
	Authors         []Author
	HelpCommands    map[string]bool
	Action          func()
	ConfigFlag      string
	RemoveColor     bool
	args            []string
	activeCommands  []*Command
}

type ParsingType int

const (
	EnvironmentVariables ParsingType = iota
	JsonConfig
	TomlConfig
	CliFlags
)

var ParingTypeStringMap = map[ParsingType]string{
	EnvironmentVariables: "Environment",
	JsonConfig:           "JSON Config",
	TomlConfig:           "Toml Config",
	CliFlags:             "CLI Flag",
}

// Create a new application with default values set.
func NewApp() *App {
	return &App{
		Name:    "cli",
		Authors: make([]Author, 0),
		HelpCommands: map[string]bool{
			"--help": true,
			"-h":     true,
			"help":   true,
		},
		ParsingOrder: []ParsingType{
			EnvironmentVariables,
			JsonConfig,
			TomlConfig,
			CliFlags,
		},
	}
}

// Run the app. Should be called with:
// app := cli.NewApp()
// app.Run(os.Args)
func (a *App) Run(args []string) {
	if len(args) < 1 {
		log.Fatal("Arguments must be at least 1, please run with app.Run(os.Args).")
	}
	a.args = args[1:]
	a.parseCommands()

	finalCommand := a.activeCommands[len(a.activeCommands)-1]
	if finalCommand.Action != nil {
		finalCommand.Action()
	}
}

func (a *App) parseCommands() {
	if a.Command == nil {
		log.Fatal("No command attached to the app!")
	}
	a.Command.buildTree(nil)
	a.Command.assignArguments(a.args)

	if helpCommand, isHelp := a.Command.isHelpCommand(a.HelpCommands); isHelp {
		fmt.Println("help.", helpCommand.Name)
		return
	}

	if err := a.Command.parseFlags(); err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("error parsing flags.")
		return
	}

	a.activeCommands = a.Command.GetActiveCommands()
	a.Command.findConfigVars()
	a.Command.parseConfigVars()
	settingsMap := a.parseByOrder()
	a.applySettingsMap(settingsMap)
	settingsMap.checkDuplicatePointers()
	settingsMap.PrintDuplicates(a.activeCommands)
	settingsMap.PrintDuplicatesStdout(a.RemoveColor)
}

func (a *App) parseByOrder() *mappedSettings {
	settingsMap := newMappedSettings()
	if a.ParsingOrder == nil {
		log.Fatal("No parsing order! Use unpuzzled.NewApp when creating an application.")
	}

	for _, order := range a.ParsingOrder {
		switch order {
		case EnvironmentVariables:
			settingsMap.addParsedArray(a.Command.parseEnvVars())

		case JsonConfig:
			vars := a.Command.getConfigVarsByType(JsonConfig)
			if len(vars) == 0 {
				continue
			}
			setValues := a.Command.parseConfigValues(vars)
			settingsMap.addParsedArray(setValues)

		case TomlConfig:
			vars := a.Command.getConfigVarsByType(TomlConfig)
			if len(vars) == 0 {
				continue
			}
			setValues := a.Command.parseConfigValues(vars)
			settingsMap.addParsedArray(setValues)

		case CliFlags:
			settingsMap.addParsedArray(a.Command.getSetFlags())
		}
	}
	return settingsMap
}

func (a *App) applySettingsMap(settingsMap *mappedSettings) {
	commandMap := a.Command.GetExpandedActiveCommmands()
	// loop through commands, ensure that the order of settings are constantly applied,
	// instead of looping through MainMap, which is not a consistent order.
	for _, command := range a.activeCommands {
		path := command.GetExpandedName()
		variableSettingsMap := settingsMap.MainMap[path]
		currCommand := commandMap[path]
		variableMap := currCommand.GetVariableMap()

		for _, setting := range variableSettingsMap {
			activeSetting := setting[len(setting)-1]
			currVariable := variableMap[activeSetting.VariableName]
			if _, ok := currVariable.(*ConfigVariable); ok {
				continue
			}
			currVariable.apply(activeSetting.Value)
		}
	}
}

type mappedSettings struct {
	MainMap map[string]map[string][]*activeSetting `json:"main_map"`
}

func newMappedSettings() *mappedSettings {
	return &mappedSettings{
		MainMap: make(map[string]map[string][]*activeSetting),
	}
}

func (m *mappedSettings) addParsedArray(settings []*activeSetting) {
	for _, setting := range settings {
		if m.MainMap[setting.CommandPath] == nil {
			m.MainMap[setting.CommandPath] = make(map[string][]*activeSetting)
		}
		if m.MainMap[setting.CommandPath][setting.VariableName] == nil {
			m.MainMap[setting.CommandPath][setting.VariableName] = make([]*activeSetting, 0)
		}
		m.MainMap[setting.CommandPath][setting.VariableName] = append(m.MainMap[setting.CommandPath][setting.VariableName], setting)
	}
}

func (m *mappedSettings) checkDuplicatePointers() {
	pointerMap := make(map[interface{}][]*activeSetting)
	for _, commandName := range m.MainMap {
		for _, settings := range commandName {
			for _, setting := range settings {
				if pointerMap[setting.Destination] == nil {
					pointerMap[setting.Destination] = make([]*activeSetting, 0)
				}
				pointerMap[setting.Destination] = append(pointerMap[setting.Destination], setting)
			}
		}
	}
	for _, settings := range pointerMap {
		settingsLen := len(settings)
		if settingsLen < 2 {
			continue
		}
		for i, setting := range settings {
			if i != settingsLen-1 {
				setting.DuplicateDestination = true
			}

		}
	}

}

// Helper to print duplciates in table format to Stdout.
func (m *mappedSettings) PrintDuplicates(commands []*Command) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Command", "Variable", "Source", "Value", "Type", "Status"})
	for _, command := range commands {
		expandedName := command.GetExpandedName()
		if m.MainMap[expandedName] == nil {
			continue
		}
		for _, settings := range m.MainMap[expandedName] {
			length := len(settings)
			for i, setting := range settings {
				var status string
				if i == length-1 {
					status = "✔ Used"
				} else {
					status = "x Ignored"
				}
				row := []string{
					expandedName,
					setting.VariableName,
					ParingTypeStringMap[setting.Source],
					fmt.Sprintf("%s", setting.Value),
					reflect.TypeOf(setting.Value).String(),
					status,
				}
				if setting.Source == EnvironmentVariables {
					row[1] += " (" + convertNameToOS(setting.VariableName) + ")"
				}
				table.Append(row)
			}
		}
	}
	table.Render()
}

// Use a custom formatted string to print duplicates on Stdout.
func (m *mappedSettings) PrintDuplicatesStdout(noColor bool) {
	t := template.New("duplicates")
	funcMap := template.FuncMap{
		"blue":  color.BlueString,
		"red":   color.RedString,
		"green": color.GreenString,
		"bold":  color.New(color.Bold).Sprint,
		"sourceString": func(setting activeSetting) string {
			if setting.Source == EnvironmentVariables {
				return fmt.Sprintf("%s (%s)", ParingTypeStringMap[setting.Source], convertNameToOS(setting.VariableName))
			} else if setting.Source == TomlConfig || setting.Source == JsonConfig {
				return fmt.Sprintf("%s (%s)", ParingTypeStringMap[setting.Source], setting.SettingName)
			} else {
				return ParingTypeStringMap[setting.Source]
			}
		},
		"plus1": func(x int) int {
			return x + 1
		},
	}
	if noColor {
		funcMap["blue"] = identityString
		funcMap["red"] = identityString
		funcMap["green"] = identityString
		funcMap["bold"] = identityString
	}
	t.Funcs(funcMap)
	t.Parse(`{{ range $command, $variables := . -}}
-------------------------------------
{{ blue "Configuration:"}} {{ bold $command }}
{{ range $key, $vars := $variables -}}
-------------
{{ range $k, $var := $vars }}{{ $length := len $vars -}}
	{{ if eq $length (plus1 $k) -}}
		{{ green $key }} = {{ green $var.Value }}
	{{ green "set from" }} {{ sourceString $var -}}
	{{ else -}}
		{{ red $key }} = {{ red $var.Value }}
	{{ red "ignored from" }} {{ sourceString $var -}}
	{{ end }}
{{ end -}}
{{ end }}
{{ end }}`)
	t.Execute(os.Stdout, m.MainMap)
}

func identityString(s string) string {
	return s
}
<commit_msg>update print statements to show when a pointer has been overwritten<commit_after>package unpuzzled

import (
	"fmt"
	"html/template"
	"os"

	"reflect"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	log "github.com/sirupsen/logrus"
)

type App struct {
	Name            string
	Usage           string
	LongDescription string
	Copyright       string
	ParsingOrder    []ParsingType
	Command         *Command
	Authors         []Author
	HelpCommands    map[string]bool
	Action          func()
	ConfigFlag      string
	RemoveColor     bool
	args            []string
	activeCommands  []*Command
}

type ParsingType int

const (
	EnvironmentVariables ParsingType = iota
	JsonConfig
	TomlConfig
	CliFlags
)

var ParingTypeStringMap = map[ParsingType]string{
	EnvironmentVariables: "Environment",
	JsonConfig:           "JSON Config",
	TomlConfig:           "Toml Config",
	CliFlags:             "CLI Flag",
}

// Create a new application with default values set.
func NewApp() *App {
	return &App{
		Name:    "cli",
		Authors: make([]Author, 0),
		HelpCommands: map[string]bool{
			"--help": true,
			"-h":     true,
			"help":   true,
		},
		ParsingOrder: []ParsingType{
			EnvironmentVariables,
			JsonConfig,
			TomlConfig,
			CliFlags,
		},
	}
}

// Run the app. Should be called with:
// app := cli.NewApp()
// app.Run(os.Args)
func (a *App) Run(args []string) {
	if len(args) < 1 {
		log.Fatal("Arguments must be at least 1, please run with app.Run(os.Args).")
	}
	a.args = args[1:]
	a.parseCommands()

	finalCommand := a.activeCommands[len(a.activeCommands)-1]
	if finalCommand.Action != nil {
		finalCommand.Action()
	}
}

func (a *App) parseCommands() {
	if a.Command == nil {
		log.Fatal("No command attached to the app!")
	}
	a.Command.buildTree(nil)
	a.Command.assignArguments(a.args)

	if helpCommand, isHelp := a.Command.isHelpCommand(a.HelpCommands); isHelp {
		fmt.Println("help.", helpCommand.Name)
		return
	}

	if err := a.Command.parseFlags(); err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("error parsing flags.")
		return
	}

	a.activeCommands = a.Command.GetActiveCommands()
	a.Command.findConfigVars()
	a.Command.parseConfigVars()
	settingsMap := a.parseByOrder()
	a.applySettingsMap(settingsMap)
	settingsMap.checkDuplicatePointers()
	settingsMap.PrintDuplicates(a.activeCommands)
	settingsMap.PrintDuplicatesStdout(a.RemoveColor)
}

func (a *App) parseByOrder() *mappedSettings {
	settingsMap := newMappedSettings()
	if a.ParsingOrder == nil {
		log.Fatal("No parsing order! Use unpuzzled.NewApp when creating an application.")
	}

	for _, order := range a.ParsingOrder {
		switch order {
		case EnvironmentVariables:
			settingsMap.addParsedArray(a.Command.parseEnvVars())

		case JsonConfig:
			vars := a.Command.getConfigVarsByType(JsonConfig)
			if len(vars) == 0 {
				continue
			}
			setValues := a.Command.parseConfigValues(vars)
			settingsMap.addParsedArray(setValues)

		case TomlConfig:
			vars := a.Command.getConfigVarsByType(TomlConfig)
			if len(vars) == 0 {
				continue
			}
			setValues := a.Command.parseConfigValues(vars)
			settingsMap.addParsedArray(setValues)

		case CliFlags:
			settingsMap.addParsedArray(a.Command.getSetFlags())
		}
	}
	return settingsMap
}

func (a *App) applySettingsMap(settingsMap *mappedSettings) {
	commandMap := a.Command.GetExpandedActiveCommmands()
	// loop through commands, ensure that the order of settings are constantly applied,
	// instead of looping through MainMap, which is not a consistent order.
	for _, command := range a.activeCommands {
		path := command.GetExpandedName()
		variableSettingsMap := settingsMap.MainMap[path]
		currCommand := commandMap[path]
		variableMap := currCommand.GetVariableMap()

		for _, setting := range variableSettingsMap {
			activeSetting := setting[len(setting)-1]
			currVariable := variableMap[activeSetting.VariableName]
			if _, ok := currVariable.(*ConfigVariable); ok {
				continue
			}
			currVariable.apply(activeSetting.Value)
		}
	}
}

type mappedSettings struct {
	MainMap map[string]map[string][]*activeSetting `json:"main_map"`
}

func newMappedSettings() *mappedSettings {
	return &mappedSettings{
		MainMap: make(map[string]map[string][]*activeSetting),
	}
}

func (m *mappedSettings) addParsedArray(settings []*activeSetting) {
	for _, setting := range settings {
		if m.MainMap[setting.CommandPath] == nil {
			m.MainMap[setting.CommandPath] = make(map[string][]*activeSetting)
		}
		if m.MainMap[setting.CommandPath][setting.VariableName] == nil {
			m.MainMap[setting.CommandPath][setting.VariableName] = make([]*activeSetting, 0)
		}
		m.MainMap[setting.CommandPath][setting.VariableName] = append(m.MainMap[setting.CommandPath][setting.VariableName], setting)
	}
}

func (m *mappedSettings) checkDuplicatePointers() {
	pointerMap := make(map[interface{}][]*activeSetting)
	for _, commandName := range m.MainMap {
		for _, settings := range commandName {
			for _, setting := range settings {
				if pointerMap[setting.Destination] == nil {
					pointerMap[setting.Destination] = make([]*activeSetting, 0)
				}
				pointerMap[setting.Destination] = append(pointerMap[setting.Destination], setting)
			}
		}
	}
	for _, settings := range pointerMap {
		settingsLen := len(settings)
		if settingsLen < 2 {
			continue
		}
		for i, setting := range settings {
			if i != settingsLen-1 {
				setting.DuplicateDestination = true
			}

		}
	}

}

// Helper to print duplciates in table format to Stdout.
func (m *mappedSettings) PrintDuplicates(commands []*Command) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Command", "Variable", "Source", "Value", "Type", "Status"})
	for _, command := range commands {
		expandedName := command.GetExpandedName()
		if m.MainMap[expandedName] == nil {
			continue
		}
		for _, settings := range m.MainMap[expandedName] {
			length := len(settings)
			for i, setting := range settings {
				var status string
				if setting.DuplicateDestination {
					status = "x Overwritten Destination"
				} else if i != length-1 {
					status = "x Ignored"
				} else {
					status = "✔ Used"
				}
				row := []string{
					expandedName,
					setting.VariableName,
					ParingTypeStringMap[setting.Source],
					fmt.Sprintf("%v", setting.Value),
					reflect.TypeOf(setting.Value).String(),
					status,
				}
				if setting.Source == EnvironmentVariables {
					row[1] += " (" + convertNameToOS(setting.VariableName) + ")"
				}
				table.Append(row)
			}
		}
	}
	table.Render()
}

// Use a custom formatted string to print duplicates on Stdout.
func (m *mappedSettings) PrintDuplicatesStdout(noColor bool) {
	t := template.New("duplicates")
	funcMap := template.FuncMap{
		"blue":  color.BlueString,
		"red":   color.RedString,
		"green": color.GreenString,
		"bold":  color.New(color.Bold).Sprint,
		"sourceString": func(setting *activeSetting) string {
			if setting.Source == EnvironmentVariables {
				return fmt.Sprintf("%s (%s)", ParingTypeStringMap[setting.Source], convertNameToOS(setting.VariableName))
			} else if setting.Source == TomlConfig || setting.Source == JsonConfig {
				return fmt.Sprintf("%s (%s)", ParingTypeStringMap[setting.Source], setting.SettingName)
			} else {
				return ParingTypeStringMap[setting.Source]
			}
		},
		"plus1": func(x int) int {
			return x + 1
		},
		"stringify": func(x interface{}) string {
			return fmt.Sprintf("%v", x)
		},
	}
	if noColor {
		funcMap["blue"] = identityString
		funcMap["red"] = identityString
		funcMap["green"] = identityString
		funcMap["bold"] = identityString
	}
	t.Funcs(funcMap)
	t.Parse(`{{ range $command, $variables := . -}}
-------------------------------------
{{ blue "Configuration:"}} {{ bold $command }}
{{ range $key, $vars := $variables -}}
-------------
{{ range $k, $var := $vars }}{{ $length := len $vars -}}
    {{ if $var.DuplicateDestination -}}
		{{ red $key }} = {{ red (stringify $var.Value) }}
	{{ red "ignored" }} {{ sourceString $var -}} {{ red " from overwritten pointer." }}
	{{ else if eq $length (plus1 $k) -}}
		{{ green $key }} = {{ green (stringify $var.Value) }}
	{{ green "set from" }} {{ sourceString $var -}}
	{{ else -}}
		{{ red $key }} = {{ red (stringify $var.Value) }}
	{{ red "ignored from" }} {{ sourceString $var -}}
	{{ end }}
{{ end -}}
{{ end }}
{{ end }}`)
	t.Execute(os.Stdout, m.MainMap)
}

func identityString(s string) string {
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"net/http/fcgi"
	"runtime"
)

var local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

type Controller struct{}

func (this Controller) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers.Add("Content-Type", "text/html")
	io.WriteString(w, "
Hello world from Go!
")
}

func main() {
	controller := Controller{}

	flag.Parse()
	var err error

	if *local != "" { // Run as a local web server
		err = http.ListenAndServe(*local, controller)
	} else { // Run as FCGI via standard I/O
		err = fcgi.Serve(nil, controller)
	}
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Cleanup app.go.<commit_after>package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"net/http/fcgi"
	"runtime"
)

var local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers.Add("Content-Type", "text/html")
	io.WriteString(w, "
Hello world from Go!
")
}

func main() {
	http.HandleFunc("/", ServeHTTP)

	flag.Parse()
	var err error

	if *local != "" { // Run as a local web server
		err = http.ListenAndServe(*local, nil)
	} else { // Run as FCGI via standard I/O
		err = fcgi.Serve(nil, nil)
	}
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>