<commit_before>package surveys

import (
	"encoding/json"
	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"log"
	"net/http"
	"regexp"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() []LauncherSchema {
	schemaList := []LauncherSchema{
		LauncherSchemaFromFilename("0_star_wars.json"),
		LauncherSchemaFromFilename("1_0005.json"),
		LauncherSchemaFromFilename("1_0102.json"),
		LauncherSchemaFromFilename("1_0112.json"),
		LauncherSchemaFromFilename("1_0203.json"),
		LauncherSchemaFromFilename("1_0205.json"),
		LauncherSchemaFromFilename("1_0213.json"),
		LauncherSchemaFromFilename("1_0215.json"),
		LauncherSchemaFromFilename("2_0001.json"),
		LauncherSchemaFromFilename("census_communal.json"),
		LauncherSchemaFromFilename("census_household.json"),
		LauncherSchemaFromFilename("census_individual.json"),
		LauncherSchemaFromFilename("e_commerce.json"),
		LauncherSchemaFromFilename("mbs_0111.json"),
		LauncherSchemaFromFilename("mbs_0117.json"),
		LauncherSchemaFromFilename("mbs_0167.json"),
		LauncherSchemaFromFilename("mbs_0205.json"),
		LauncherSchemaFromFilename("mbs_0216.json"),
		LauncherSchemaFromFilename("mbs_0255.json"),
		LauncherSchemaFromFilename("mbs_0817.json"),
		LauncherSchemaFromFilename("mbs_0867.json"),
		LauncherSchemaFromFilename("multiple_answers.json"),
		LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
		LauncherSchemaFromFilename("test_checkbox.json"),
		LauncherSchemaFromFilename("test_conditional_dates.json"),
		LauncherSchemaFromFilename("test_conditional_routing.json"),
		LauncherSchemaFromFilename("test_confirmation_question.json"),
		LauncherSchemaFromFilename("test_currency.json"),
		LauncherSchemaFromFilename("test_date_range_period_validation.json"),
		LauncherSchemaFromFilename("test_dates.json"),
		LauncherSchemaFromFilename("test_default.json"),
		LauncherSchemaFromFilename("test_dependencies_calculation.json"),
		LauncherSchemaFromFilename("test_dependencies_max_value.json"),
		LauncherSchemaFromFilename("test_dependencies_min_value.json"),
		LauncherSchemaFromFilename("test_difference_in_years.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
		LauncherSchemaFromFilename("test_difference_in_years_range.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_dropdown_optional.json"),
		LauncherSchemaFromFilename("test_error_messages.json"),
		LauncherSchemaFromFilename("test_final_confirmation.json"),
		LauncherSchemaFromFilename("test_household_question.json"),
		LauncherSchemaFromFilename("test_interstitial_page.json"),
		LauncherSchemaFromFilename("test_introduction.json"),
		LauncherSchemaFromFilename("test_language.json"),
		LauncherSchemaFromFilename("test_language_cy.json"),
		LauncherSchemaFromFilename("test_markup.json"),
		LauncherSchemaFromFilename("test_metadata_routing.json"),
		LauncherSchemaFromFilename("test_multiple_piping.json"),
		LauncherSchemaFromFilename("test_navigation.json"),
		LauncherSchemaFromFilename("test_navigation_completeness.json"),
		LauncherSchemaFromFilename("test_navigation_confirmation.json"),
		LauncherSchemaFromFilename("test_navigation_routing.json"),
		LauncherSchemaFromFilename("test_numbers.json"),
		LauncherSchemaFromFilename("test_percentage.json"),
		LauncherSchemaFromFilename("test_question_guidance.json"),
		LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
		LauncherSchemaFromFilename("test_radio_mandatory.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
		LauncherSchemaFromFilename("test_relationship_household.json"),
		LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
		LauncherSchemaFromFilename("test_repeating_household.json"),
		LauncherSchemaFromFilename("test_routing_date_equals.json"),
		LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_date_less_than.json"),
		LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_group.json"),
		LauncherSchemaFromFilename("test_routing_number_equals.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
		LauncherSchemaFromFilename("test_single_date_period_validation.json"),
		LauncherSchemaFromFilename("test_skip_condition.json"),
		LauncherSchemaFromFilename("test_skip_condition_block.json"),
		LauncherSchemaFromFilename("test_skip_condition_group.json"),
		LauncherSchemaFromFilename("test_summary.json"),
		LauncherSchemaFromFilename("test_section_summary.json"),
		LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
		LauncherSchemaFromFilename("test_view_submitted_response.json"),
		LauncherSchemaFromFilename("test_textarea.json"),
		LauncherSchemaFromFilename("test_textfield.json"),
		LauncherSchemaFromFilename("test_timeout.json"),
		LauncherSchemaFromFilename("test_total_breakdown.json"),
		LauncherSchemaFromFilename("test_unit_patterns.json"),
	}

	return append(schemaList, getAvailableSchemasFromRegister()...)
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas() {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<commit_msg>add mbs 0123 and 0173<commit_after>package surveys

import (
	"encoding/json"
	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"log"
	"net/http"
	"regexp"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() []LauncherSchema {
	schemaList := []LauncherSchema{
		LauncherSchemaFromFilename("0_star_wars.json"),
		LauncherSchemaFromFilename("1_0005.json"),
		LauncherSchemaFromFilename("1_0102.json"),
		LauncherSchemaFromFilename("1_0112.json"),
		LauncherSchemaFromFilename("1_0203.json"),
		LauncherSchemaFromFilename("1_0205.json"),
		LauncherSchemaFromFilename("1_0213.json"),
		LauncherSchemaFromFilename("1_0215.json"),
		LauncherSchemaFromFilename("2_0001.json"),
		LauncherSchemaFromFilename("census_communal.json"),
		LauncherSchemaFromFilename("census_household.json"),
		LauncherSchemaFromFilename("census_individual.json"),
		LauncherSchemaFromFilename("e_commerce.json"),
		LauncherSchemaFromFilename("mbs_0111.json"),
		LauncherSchemaFromFilename("mbs_0117.json"),
		LauncherSchemaFromFilename("mbs_0123.json"),
		LauncherSchemaFromFilename("mbs_0167.json"),
		LauncherSchemaFromFilename("mbs_0173.json"),
		LauncherSchemaFromFilename("mbs_0205.json"),
		LauncherSchemaFromFilename("mbs_0216.json"),
		LauncherSchemaFromFilename("mbs_0255.json"),
		LauncherSchemaFromFilename("mbs_0817.json"),
		LauncherSchemaFromFilename("mbs_0867.json"),
		LauncherSchemaFromFilename("multiple_answers.json"),
		LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
		LauncherSchemaFromFilename("test_checkbox.json"),
		LauncherSchemaFromFilename("test_conditional_dates.json"),
		LauncherSchemaFromFilename("test_conditional_routing.json"),
		LauncherSchemaFromFilename("test_confirmation_question.json"),
		LauncherSchemaFromFilename("test_currency.json"),
		LauncherSchemaFromFilename("test_date_range_period_validation.json"),
		LauncherSchemaFromFilename("test_dates.json"),
		LauncherSchemaFromFilename("test_default.json"),
		LauncherSchemaFromFilename("test_dependencies_calculation.json"),
		LauncherSchemaFromFilename("test_dependencies_max_value.json"),
		LauncherSchemaFromFilename("test_dependencies_min_value.json"),
		LauncherSchemaFromFilename("test_difference_in_years.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
		LauncherSchemaFromFilename("test_difference_in_years_range.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_dropdown_optional.json"),
		LauncherSchemaFromFilename("test_error_messages.json"),
		LauncherSchemaFromFilename("test_final_confirmation.json"),
		LauncherSchemaFromFilename("test_household_question.json"),
		LauncherSchemaFromFilename("test_interstitial_page.json"),
		LauncherSchemaFromFilename("test_introduction.json"),
		LauncherSchemaFromFilename("test_language.json"),
		LauncherSchemaFromFilename("test_language_cy.json"),
		LauncherSchemaFromFilename("test_markup.json"),
		LauncherSchemaFromFilename("test_metadata_routing.json"),
		LauncherSchemaFromFilename("test_multiple_piping.json"),
		LauncherSchemaFromFilename("test_navigation.json"),
		LauncherSchemaFromFilename("test_navigation_completeness.json"),
		LauncherSchemaFromFilename("test_navigation_confirmation.json"),
		LauncherSchemaFromFilename("test_navigation_routing.json"),
		LauncherSchemaFromFilename("test_numbers.json"),
		LauncherSchemaFromFilename("test_percentage.json"),
		LauncherSchemaFromFilename("test_question_guidance.json"),
		LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
		LauncherSchemaFromFilename("test_radio_mandatory.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
		LauncherSchemaFromFilename("test_relationship_household.json"),
		LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
		LauncherSchemaFromFilename("test_repeating_household.json"),
		LauncherSchemaFromFilename("test_routing_date_equals.json"),
		LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_date_less_than.json"),
		LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_group.json"),
		LauncherSchemaFromFilename("test_routing_number_equals.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
		LauncherSchemaFromFilename("test_single_date_period_validation.json"),
		LauncherSchemaFromFilename("test_skip_condition.json"),
		LauncherSchemaFromFilename("test_skip_condition_block.json"),
		LauncherSchemaFromFilename("test_skip_condition_group.json"),
		LauncherSchemaFromFilename("test_summary.json"),
		LauncherSchemaFromFilename("test_section_summary.json"),
		LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
		LauncherSchemaFromFilename("test_view_submitted_response.json"),
		LauncherSchemaFromFilename("test_textarea.json"),
		LauncherSchemaFromFilename("test_textfield.json"),
		LauncherSchemaFromFilename("test_timeout.json"),
		LauncherSchemaFromFilename("test_total_breakdown.json"),
		LauncherSchemaFromFilename("test_unit_patterns.json"),
	}

	return append(schemaList, getAvailableSchemasFromRegister()...)
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas() {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<|endoftext|>


<commit_before>package router

import (
	"net/http"

	"github.com/ewhal/nyaa/service/captcha"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

var Router *mux.Router

func init() {
	// Static file handlers
	cssHandler := http.FileServer(http.Dir("./public/css/"))
	jsHandler := http.FileServer(http.Dir("./public/js/"))
	imgHandler := http.FileServer(http.Dir("./public/img/"))

	// Enable GZIP compression for all handlers except imgHandler and captcha
	gzipCSSHandler := handlers.CompressHandler(cssHandler)
	gzipJSHandler := handlers.CompressHandler(jsHandler)
	gzipHomeHandler := handlers.CompressHandler(http.HandlerFunc(HomeHandler))
	gzipSearchHandler := handlers.CompressHandler(http.HandlerFunc(SearchHandler))
	gzipAPIHandler := handlers.CompressHandler(http.HandlerFunc(ApiHandler))
	gzipAPIViewHandler := handlers.CompressHandler(http.HandlerFunc(ApiViewHandler))
	gzipAPIUploadHandler := handlers.CompressHandler(http.HandlerFunc(ApiUploadHandler))
	gzipAPIUpdateHandler := handlers.CompressHandler(http.HandlerFunc(ApiUpdateHandler))
	gzipFaqHandler := handlers.CompressHandler(http.HandlerFunc(FaqHandler))
	gzipRSSHandler := handlers.CompressHandler(http.HandlerFunc(RSSHandler))
	gzipViewHandler := handlers.CompressHandler(http.HandlerFunc(ViewHandler))
	gzipUploadHandler := handlers.CompressHandler(http.HandlerFunc(UploadHandler))
	gzipUserRegisterFormHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterFormHandler))
	gzipUserLoginFormHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginFormHandler))
	gzipUserVerifyEmailHandler := handlers.CompressHandler(http.HandlerFunc(UserVerifyEmailHandler))
	gzipUserRegisterPostHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterPostHandler))
	gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler))
	gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
	gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
	gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler))
	gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))

	gzipIndexModPanel := handlers.CompressHandler(http.HandlerFunc(IndexModPanel))
	gzipTorrentsListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentsListPanel))
	gzipUsersListPanel := handlers.CompressHandler(http.HandlerFunc(UsersListPanel))
	gzipCommentsListPanel := handlers.CompressHandler(http.HandlerFunc(CommentsListPanel))
	gzipTorrentEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentEditModPanel))
	gzipTorrentPostEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentPostEditModPanel))
	gzipCommentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(CommentDeleteModPanel))
	gzipTorrentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentDeleteModPanel))

	Router = mux.NewRouter()

	// Routes
	http.Handle("/css/", http.StripPrefix("/css/", gzipCSSHandler))
	http.Handle("/js/", http.StripPrefix("/js/", gzipJSHandler))
	http.Handle("/img/", http.StripPrefix("/img/", imgHandler))
	Router.Handle("/", gzipHomeHandler).Name("home")
	Router.Handle("/page/{page:[0-9]+}", gzipHomeHandler).Name("home_page")
	Router.Handle("/search", gzipSearchHandler).Name("search")
	Router.Handle("/search/{page}", gzipSearchHandler).Name("search_page")
	Router.Handle("/api", gzipAPIHandler).Methods("GET")
	Router.Handle("/api/{page:[0-9]*}", gzipAPIHandler).Methods("GET")
	Router.Handle("/api/view/{id}", gzipAPIViewHandler).Methods("GET")
	Router.Handle("/api/upload", gzipAPIUploadHandler).Methods("POST")
	Router.Handle("/api/update", gzipAPIUpdateHandler).Methods("PUT")
	Router.Handle("/faq", gzipFaqHandler).Name("faq")
	Router.Handle("/feed", gzipRSSHandler).Name("feed")
	Router.Handle("/view/{id}", gzipViewHandler).Methods("GET").Name("view_torrent")
	Router.HandleFunc("/view/{id}", PostCommentHandler).Methods("POST").Name("post_comment")
	Router.Handle("/upload", gzipUploadHandler).Name("upload")
	Router.Handle("/user/register", gzipUserRegisterFormHandler).Name("user_register").Methods("GET")
	Router.Handle("/user/login", gzipUserLoginFormHandler).Name("user_login").Methods("GET")
	Router.Handle("/verify/email/{token}", gzipUserVerifyEmailHandler).Name("user_verify").Methods("GET")
	Router.Handle("/user/register", gzipUserRegisterPostHandler).Name("user_register").Methods("POST")
	Router.Handle("/user/login", gzipUserLoginPostHandler).Name("user_login").Methods("POST")
	Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
	Router.Handle("/user/{id}/{username}", gzipUserProfileHandler).Name("user_profile").Methods("GET")
	Router.Handle("/user/{id}/{username}/follow", gzipUserFollowHandler).Name("user_follow").Methods("GET")
	Router.Handle("/user/{id}/{username}", gzipUserProfileFormHandler).Name("user_profile").Methods("POST")

	Router.Handle("/mod/", gzipIndexModPanel).Name("mod_index")
	Router.Handle("/mod/torrents", gzipTorrentsListPanel).Name("mod_tlist")
	Router.Handle("/mod/users", gzipUsersListPanel).Name("mod_ulist")
	Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_clist")
	Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_cedit")
	Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_cdelete")
	Router.Handle("/mod/torrent/", gzipTorrentEditModPanel).Name("mod_tedit")
	Router.Handle("/mod/torrent/", gzipTorrentPostEditModPanel).Name("mod_ptedit")
	Router.Handle("/mod/torrent/delete", gzipCommentDeleteModPanel).Name("mod_tdelete")
	Router.Handle("/mod/comment/delete", gzipTorrentDeleteModPanel).Name("mod_cdelete")

	Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)

	Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
}
<commit_msg>shenanigans<commit_after>package router

import (
	"net/http"

	"github.com/ewhal/nyaa/service/captcha"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

var Router *mux.Router

func init() {
	// Static file handlers
	cssHandler := http.FileServer(http.Dir("./public/css/"))
	jsHandler := http.FileServer(http.Dir("./public/js/"))
	imgHandler := http.FileServer(http.Dir("./public/img/"))

	// Enable GZIP compression for all handlers except imgHandler and captcha
	gzipCSSHandler := handlers.CompressHandler(cssHandler)
	gzipJSHandler := handlers.CompressHandler(jsHandler)
	gzipHomeHandler := handlers.CompressHandler(http.HandlerFunc(HomeHandler))
	gzipSearchHandler := handlers.CompressHandler(http.HandlerFunc(SearchHandler))
	gzipAPIHandler := handlers.CompressHandler(http.HandlerFunc(ApiHandler))
	gzipAPIViewHandler := handlers.CompressHandler(http.HandlerFunc(ApiViewHandler))
	gzipAPIUploadHandler := handlers.CompressHandler(http.HandlerFunc(ApiUploadHandler))
	gzipAPIUpdateHandler := handlers.CompressHandler(http.HandlerFunc(ApiUpdateHandler))
	gzipFaqHandler := handlers.CompressHandler(http.HandlerFunc(FaqHandler))
	gzipRSSHandler := handlers.CompressHandler(http.HandlerFunc(RSSHandler))
	gzipViewHandler := handlers.CompressHandler(http.HandlerFunc(ViewHandler))
	gzipUploadHandler := handlers.CompressHandler(http.HandlerFunc(UploadHandler))
	gzipUserRegisterFormHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterFormHandler))
	gzipUserLoginFormHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginFormHandler))
	gzipUserVerifyEmailHandler := handlers.CompressHandler(http.HandlerFunc(UserVerifyEmailHandler))
	gzipUserRegisterPostHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterPostHandler))
	gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler))
	gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
	gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
	gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler))
	gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))

	gzipIndexModPanel := handlers.CompressHandler(http.HandlerFunc(IndexModPanel))
	gzipTorrentsListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentsListPanel))
	gzipUsersListPanel := handlers.CompressHandler(http.HandlerFunc(UsersListPanel))
	gzipCommentsListPanel := handlers.CompressHandler(http.HandlerFunc(CommentsListPanel))
	gzipTorrentEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentEditModPanel))
	gzipTorrentPostEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentPostEditModPanel))
	gzipCommentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(CommentDeleteModPanel))
	gzipTorrentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentDeleteModPanel))

	Router = mux.NewRouter()

	// Routes
	http.Handle("/css/", http.StripPrefix("/css/", gzipCSSHandler))
	http.Handle("/js/", http.StripPrefix("/js/", gzipJSHandler))
	http.Handle("/img/", http.StripPrefix("/img/", imgHandler))
	Router.Handle("/", gzipHomeHandler).Name("home")
	Router.Handle("/page/{page:[0-9]+}", gzipHomeHandler).Name("home_page")
	Router.Handle("/search", gzipSearchHandler).Name("search")
	Router.Handle("/search/{page}", gzipSearchHandler).Name("search_page")
	Router.Handle("/api", gzipAPIHandler).Methods("GET")
	Router.Handle("/api/{page:[0-9]*}", gzipAPIHandler).Methods("GET")
	Router.Handle("/api/view/{id}", gzipAPIViewHandler).Methods("GET")
	Router.Handle("/api/upload", gzipAPIUploadHandler).Methods("POST")
	Router.Handle("/api/update", gzipAPIUpdateHandler).Methods("PUT")
	Router.Handle("/faq", gzipFaqHandler).Name("faq")
	Router.Handle("/feed", gzipRSSHandler).Name("feed")
	Router.Handle("/view/{id}", gzipViewHandler).Methods("GET").Name("view_torrent")
	Router.HandleFunc("/view/{id}", PostCommentHandler).Methods("POST").Name("post_comment")
	Router.Handle("/upload", gzipUploadHandler).Name("upload")
	Router.Handle("/user/register", gzipUserRegisterFormHandler).Name("user_register").Methods("GET")
	Router.Handle("/user/login", gzipUserLoginFormHandler).Name("user_login").Methods("GET")
	Router.Handle("/verify/email/{token}", gzipUserVerifyEmailHandler).Name("user_verify").Methods("GET")
	Router.Handle("/user/register", gzipUserRegisterPostHandler).Name("user_register").Methods("POST")
	Router.Handle("/user/login", gzipUserLoginPostHandler).Name("user_login").Methods("POST")
	Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
	Router.Handle("/user/{id}/{username}", gzipUserProfileHandler).Name("user_profile").Methods("GET")
	Router.Handle("/user/{id}/{username}/follow", gzipUserFollowHandler).Name("user_follow").Methods("GET")
	Router.Handle("/user/{id}/{username}", gzipUserProfileFormHandler).Name("user_profile").Methods("POST")

	Router.Handle("/mod/", gzipIndexModPanel).Name("mod_index")
	Router.Handle("/mod/torrents", gzipTorrentsListPanel).Name("mod_tlist")
	Router.Handle("/mod/users", gzipUsersListPanel).Name("mod_ulist")
	Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_clist")
	Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_cedit") // TODO
	Router.Handle("/mod/torrent/", gzipTorrentEditModPanel).Name("mod_tedit")
	Router.Handle("/mod/torrent/", gzipTorrentPostEditModPanel).Name("mod_ptedit")
	Router.Handle("/mod/torrent/delete", gzipTorrentDeleteModPanel).Name("mod_tdelete")
	Router.Handle("/mod/comment/delete", gzipCommentDeleteModPanel).Name("mod_cdelete")

	Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)

	Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
}
<|endoftext|>


<commit_before>package routes

import (
  . "seeme/models"
  . "seeme/helpers"

  "fmt"
  "net/http"
  "html/template"

  "encoding/json"
)

func HandlerAllUser(w http.ResponseWriter, r *http.Request) {
  js, err := json.Marshal(CreateDummyUsers())
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }

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

func HandlerRegisterUser(w http.ResponseWriter, r *http.Request) {
  templLogin := template.Must(template.ParseFiles("views/register.html"))
  var page Page

  if r.FormValue("register") != "" {
    if err := InsertNewUser(CreateUserFromRequest(r)); err != nil {
      page.Alert = err.Error()
      fmt.Println("Database Insert Failure: " + err.Error())
    } else {
      page.Alert = "User Registered"
    }
  }

  if err := templLogin.ExecuteTemplate(w, "register.html", page); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

func HandlerLoginUser(w http.ResponseWriter, r *http.Request) {
  templLogin := template.Must(template.ParseFiles("views/login.html"))
  var page Page

  db := GetDatabaseInstance()

  if r.FormValue("register") != "" {
    http.Redirect(w, r, "/register", http.StatusFound)
    return
  } else if r.FormValue("login") != "" {
    var username string
    var secret []byte

    rows, err := db.Query("select username, secret from users where username = ?", r.FormValue("username"))
    if err != nil {
      page.Alert = err.Error()
    }
    defer rows.Close()
    for rows.Next() {
      if err := rows.Scan(&username, &secret); err != nil {
        page.Alert = err.Error()
      }
    }
    fmt.Println(username, secret)
  }

  if err := templLogin.ExecuteTemplate(w, "login.html", page); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}<commit_msg>added user auth on login<commit_after>package routes

import (
  . "seeme/models"
  . "seeme/helpers"

  "fmt"
  "net/http"
  "html/template"

  "encoding/json"
  "golang.org/x/crypto/bcrypt"
)

func HandlerAllUser(w http.ResponseWriter, r *http.Request) {
  js, err := json.Marshal(CreateDummyUsers())
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }

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

func HandlerRegisterUser(w http.ResponseWriter, r *http.Request) {
  templLogin := template.Must(template.ParseFiles("views/register.html"))
  var page Page

  if r.FormValue("register") != "" {
    if err := InsertNewUser(CreateUserFromRequest(r)); err != nil {
      page.Alert = err.Error()
      fmt.Println("Database Insert Failure: " + err.Error())
    } else {
      page.Alert = "User Registered"
    }
  }

  if err := templLogin.ExecuteTemplate(w, "register.html", page); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

func HandlerLoginUser(w http.ResponseWriter, r *http.Request) {
  templLogin := template.Must(template.ParseFiles("views/login.html"))
  var page Page

  db := GetDatabaseInstance()

  if r.FormValue("register") != "" {
    http.Redirect(w, r, "/register", http.StatusFound)
    return
  } else if r.FormValue("login") != "" {
    var username string
    var secret []byte

    rows, err := db.Query("select username, secret from users where username = ?", r.FormValue("username"))
    if err != nil {
      page.Alert = err.Error()
    }
    defer rows.Close()
    for rows.Next() {
      if err := rows.Scan(&username, &secret); err != nil {
        page.Alert = err.Error()
      }
    }
    if username == "" {
        page.Alert = "User " + r.FormValue("username") + " not found"
      } else {
        if err = bcrypt.CompareHashAndPassword(secret, []byte(r.FormValue("password"))); err != nil {
          page.Alert = err.Error()
        } else {
          page.Alert = "User " + username + ": Authenticated"
        }
      }
  }

  if err := templLogin.ExecuteTemplate(w, "login.html", page); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"log"

	"github.com/spf13/cobra"

	"github.com/aymerick/kowa/builder"
	"github.com/aymerick/kowa/models"
	"github.com/aymerick/kowa/utils"
)

var addSiteCmd = &cobra.Command{
	Use:   "add_site [id] [user_id]",
	Short: "Add a new site",
	Long:  `Insert a new site in database.`,
	Run:   addSite,
}

func addSite(cmd *cobra.Command, args []string) {
	if len(args) < 2 {
		cmd.Usage()
		log.Fatalln("Missing arguments")
	}

	dbSession := models.NewDBSession()

	if site := dbSession.FindSite(args[0]); site != nil {
		log.Fatalln("There is already a site with that id: " + args[0])
	}

	site := &models.Site{
		Id:     args[0],
		UserId: args[1],
		Theme:  builder.DEFAULT_THEME,
	}

	if err := dbSession.CreateSite(site); err != nil {
		log.Fatalln(fmt.Sprintf("Failed to create site: %v", err))
	}

	utils.AppEnsureSiteUploadDir(site.Id)
}
<commit_msg>Adds the [site name] parameter to add_site command<commit_after>package commands

import (
	"fmt"
	"log"

	"github.com/spf13/cobra"

	"github.com/aymerick/kowa/builder"
	"github.com/aymerick/kowa/models"
	"github.com/aymerick/kowa/utils"
)

var addSiteCmd = &cobra.Command{
	Use:   "add_site [id] [site name] [user id]",
	Short: "Add a new site",
	Long:  `Insert a new site in database.`,
	Run:   addSite,
}

func addSite(cmd *cobra.Command, args []string) {
	if len(args) < 3 {
		cmd.Usage()
		log.Fatalln("Missing arguments")
	}

	dbSession := models.NewDBSession()

	if site := dbSession.FindSite(args[0]); site != nil {
		log.Fatalln("There is already a site with that id: " + args[0])
	}

	site := &models.Site{
		Id:     args[0],
		Name:   args[1],
		UserId: args[2],
		Theme:  builder.DEFAULT_THEME,
	}

	if err := dbSession.CreateSite(site); err != nil {
		log.Fatalln(fmt.Sprintf("Failed to create site: %v", err))
	}

	utils.AppEnsureSiteUploadDir(site.Id)
}
<|endoftext|>


<commit_before>package schema

type locationSchema struct {
	Lat  float64 `jsonschema:"lat"`
	Long float64 `jsonschema:"long"`
}

type scoreSchema struct {
	Type  string `jsonschema:"type;description:eg. GPA or PERCENTAGE - [Add score type]"`
	Value string `jsonschema:"value;description:eg. 3.4/4.0 - [Add obtained score/total score]"`
}

type coreSchema struct {
	Name              string         `jsonschema:"name;description:e.g. John Doe"`
	Title             string         `jsonschema:"title;description:e.g. Software Engineer"`
	Image             string         `jsonschema:"image;description:e.g. example.com/Abcxyz - [URL (as per RFC 3986) to a image in JPEG or PNG format]"`
	Email             string         `jsonschema:"email;description:e.g. lucas@example.com;format:email"`
	Phone             string         `jsonschema:"phone;description:e.g. 912-217-7923 - [Phone numbers are stored as strings so use any format you like]"`
	URL               string         `jsonschema:"url;description:e.g. http://www.example.com/my-slides/;format:uri"`
	Summary           string         `jsonschema:"summary;description:Write a short 2-3 sentence biography about yourself"`
	CurrentLocation   locationSchema `jsonschema:"currentLocation;description:Select the location where you currently live.;format:location"`
	PermanentLocation locationSchema `jsonschema:"permanentLocation;description:Select the location where you permanently live.;format:location"`
}

type workSchema struct {
	Name        string         `jsonschema:"name;description:e.g. XYZ Inc. - [Company name]"`
	Description string         `jsonschema:"description;description:e.g. A social media company - [Description of the companies primary activity]"`
	Position    string         `jsonschema:"position;description:e.g. Software Engineer - [Position at the company]"`
	Location    locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this activity];format:location"`
	URL         string         `jsonschema:"url;description:e.g. http://xyz.example.com - [Related link to the company website];format:uri"`
	StartDate   string         `jsonschema:"startDate;description:e.g. 2017-06-28 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string         `jsonschema:"endDate;description:e.g. 2018-12-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Summary     string         `jsonschema:"summary;description:Give an overview of your responsibilities at the company"`
	Highlights  []string       `jsonschema:"highlights;description:Specify multiple accomplishments;items_description:e.g. Worked with mobile team at Twitter to develop remote debugging tools for mobile browsers"`
}

type educationSchema struct {
	Institution string         `jsonschema:"institution;description:e.g. XYZ Institute of Technology - [Add institute name]"`
	Location    locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this institution];format:location"`
	Area        string         `jsonschema:"area;description:e.g. Engineering"`
	StudyType   string         `jsonschema:"studyType;description:e.g. Bachelor"`
	StartDate   string         `jsonschema:"startDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string         `jsonschema:"endDate;description:e.g. 2013-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Score       scoreSchema    `jsonschema:"score;additionalProperties"`
	Courses     []string       `jsonschema:"courses;description:List notable courses/subjects;items_description:e.g. CS302 - Introduction to Algorithms - [Add course name]"`
	Honors      []string       `jsonschema:"honors;description:List education honours;items_description:e.g. Magna Cum Laude"`
}

type volunteerSchema struct {
	Organization string         `jsonschema:"organization;description:e.g. Xyz "`
	Position     string         `jsonschema:"position;description:e.g. Open Source Contributor - [Contribution type]"`
	Location     locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this activity];format:location"`
	URL          string         `jsonschema:"url;description:e.g. http://xyz.example.com - [Related link to support volunteer experience];format:uri"`
	StartDate    string         `jsonschema:"startDate;description:e.g. 2014-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate      string         `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard] ;format:date"`
	Summary      string         `jsonschema:"summary;description:Give an overview of your responsibilities at the company"`
	Highlights   []string       `jsonschema:"highlights;description:Specify accomplishments and achievements;items_description:e.g Invited as a speaker in Xyzcon'17"`
}

type publicationSchema struct {
	Name        string `jsonschema:"name;description:e.g. Deep learning and Artificial Intelligence"`
	publisher   string `jsonschema:"publisher;description:e.g. XYZ, Computer Magazine"`
	ReleaseDate string `jsonschema:"releaseDate;description:e.g. 2015-08-01 - [resume.json uses the ISO 8601 date standard]"`
	Resources   []struct {
		URL   string `jsonschema:"url;description:e.g. http://www.example.com/my-example-slides/;format:uri"`
		Label string `jsonschema:"label;description:e.g Slides"`
	} `jsonschema:"resources;description:Specify multiple resources with label"`
	URL     string `jsonschema:"url;description:e.g. http://www.computer.org.example.com/csdl/mags/co/2015/10/rx069-abs.html;format:uri"`
	Summary string `jsonschema:"summary;description:e.g. Discussion of the advent of deep learning and artificial intelligence - [Short summary of publication]"`
}

type legalSchema struct {
	Name            string `jsonschema:"name;description:e.g. XYZ's patent on LZW compression, a fundamental part of the widely used GIF graphics format - [Add document name]"`
	LegalType       string `jsonschema:"legalType;description:e.g. Patent, Trademark, Copyright - [Give the type of this document]"`
	Description     string `jsonschema:"description;description:Give a brief description about this document"`
	ApplicationDate string `jsonschema:"applicationDate;description:e.g. 2015-08-01 - [resume.json uses the ISO 8601 date standard];format:date"`
	GrantDate       string `jsonschema:"grantDate;description:e.g. 2016-09-01 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate         string `jsonschema:"endDate;description:e.g. 2020-09-03 - [resume.json uses the ISO 8601 date standard];format:date"`
	Resources       []struct {
		URL   string `jsonschema:"url;description:e.g. http://www.example.com/my-patent-slides/;format:uri"`
		Label string `jsonschema:"label;description:e.g Slides"`
	} `jsonschema:"resources;description:Specify multiple resources with label"`
	IDNumber string `jsonschema:"idNumber;description:e.g. JP2004369746A - [Add the application number or Id Number]  "`
}

type skillSchema struct {
	Name    string `jsonschema:"name;description:e.g. Web Development"`
	Keyword []struct {
		Name  string      `jsonschema:"name;description:e.g. HTML - [Add the skill name]"`
		Score scoreSchema `jsonschema:"score;additionalProperties;description:Score for the skill name"`
	} `jsonschema:"keyword;description:List some keywords pertaining to the skill;items_additionalProperties"`
}

type awardSchema struct {
	Title   string `jsonschema:"title;description:e.g. Awarded Software Process Achievement Award "`
	Date    string `jsonschema:"date;description:e.g. 2016-06-12 - [resume.json uses the ISO 8601 date standard];format:date"`
	Awarder string `jsonschema:"awarder;description:e.g. IEEE"`
	Summary string `jsonschema:"summary;description:e.g. Received for my work in Deep learning and AI"`
}

type projectSchema struct {
	Name        string         `jsonschema:"name;description:e.g. File Transfer application - [Name of the project]"`
	Location    locationSchema `jsonschema:"location;description:e.g France - [Location for this activity];format:location"`
	Description string         `jsonschema:"description;description:e.g. Developed a client and server based application - [Short summary of project]"`
	Highlights  []string       `jsonschema:"highlights;description:Specify multiple features;items_description:e.g. used Java AWT and Swing for client side userinterface"`
	Keywords    []string       `jsonschema:"keywords;description:Specify special elements involved;items_description:e.g. Java"`
	StartDate   string         `jsonschema:"startDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string         `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard] ;format:date"`
	Resources   []struct {
		URL   string `jsonschema:"url;description:e.g. http://www.example.com/my-awesome-slides/"`
		Label string `jsonschema:"label;description:e.g Slides"`
	} `jsonschema:"resources;description:Specify multiple resources with label"`
	URL    string   `jsonschema:"url;description:e.g. http://www.example.org/csdl/mags/co/1996/10/rx069-abs.html;format:uri"`
	Roles  []string `jsonschema:"roles;description:Specify your role on this project or in company;items_description:e.g. Team Lead, Speaker, Writer"`
	Entity string   `jsonschema:"entity;description:e.g. 'greenpeace', 'corporationXYZ' - [Relevant company/entity affiliations]"`
	Type   string   `jsonschema:"type;description:e.g. 'volunteering', 'presentation', 'talk', 'application', 'conference'"`
}

type certificateSchema struct {
	Code          string      `jsonschema:"code;description:e.g. 1Z0-062"`
	Name          string      `jsonschema:"name;description:e.g. XYZ Certified Application Specialist (MCAS) - [Add the certificate name]"`
	Website       string      `jsonschema:"website;description:Link to issuing authority's description of the certificate;format:uri"`
	Verification  string      `jsonschema:"verification;description:External candidate verification URL;format:uri"`
	GrantDate     string      `jsonschema:"grantDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Score         scoreSchema `jsonschema:"score;additionalProperties;description:Exam result (PASS/FAIL, 100%, 100)"`
	EndDate       string      `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	DoesNotExpire bool        `jsonschema:"doesNotExpire;format:checkbox"`
}

type referenceSchema struct {
	Name      string `jsonschema:"name;description:e.g. Stephan Mark"`
	Company   string `jsonschema:"company;description:e.g. Xyz"`
	Position  string `jsonschema:"position;description:e.g. Senior Software Engineer"`
	Reference string `jsonschema:"reference;description:e.g. Joe blogs was a great employee, who turned up to work at least once a week. He exceeded my expectations when it came to doing nothing."`
}

type languageSchema struct {
	Language string      `jsonschema:"language;description:e.g. English - [Name of language]"`
	Score    scoreSchema `jsonschema:"score;additionalProperties;description:Score for the language"`
}

type interestSchema struct {
	Name     string   `jsonschema:"name;description:e.g. Machine Learning"`
	Keywords []string `jsonschema:"keywords;description:Specify keywords associated with the particular interest;items_description:e.g. Neural Networks, Convoluted Neural Networks"`
}

type metaSchema struct {
	Canonical    string `jsonschema:"canonical;description:URL (as per RFC 3986) to latest version of this document"`
	Version      string `jsonschema:"version;description:e.g. v1.0.0 - [A version field which follows semver]"`
	LastModified string `jsonschema:"lastModified;description:e.g. 2017-06-29T15:53:00 - [resume.json uses the ISO 8601 date standard];format:date"`
}

type Schema struct {
	Core         coreSchema          `jsonschema:"core;additionalProperties"`
	Work         []workSchema        `jsonschema:"work;items_additionalProperties"`
	Education    []educationSchema   `jsonschema:"education;items_additionalProperties"`
	Volunteer    []volunteerSchema   `jsonschema:"volunteer;items_additionalProperties"`
	Publications []publicationSchema `jsonschema:"publications;description:Specify your publications;items_additionalProperties"`
	Legal        []legalSchema       `jsonschema:"legal;description:Specify your labels;items_additionalProperties"`
	Skills       []skillSchema       `jsonschema:"skills;description:List out your professional skill-set;items_additionalProperties"`
	Awards       []awardSchema       `jsonschema:"awards;description:Specify any awards you have received throughout your professional career;items_additionalProperties"`
	Projects     []projectSchema     `jsonschema:"projects;description:Specify career projects;items_additionalProperties"`
	Certificates []certificateSchema `jsonschema:"certificate;items_additionalProperties"`
	References   []referenceSchema   `jsonschema:"references;description:List references you have received;items_additionalProperties"`
	Languages    []languageSchema    `jsonschema:"languages;description:List any other languages you speak;items_additionalProperties"`
	Interests    []interestSchema    `jsonschema:"interests;items_additionalProperties"`
	Meta         metaSchema          `jsonschema:"meta;description:The schema version and any other tooling configuration lives here;additionalProperties"`
}
<commit_msg>Move resourceSchema to its own struct<commit_after>package schema

type locationSchema struct {
	Lat  float64 `jsonschema:"lat"`
	Long float64 `jsonschema:"long"`
}

type scoreSchema struct {
	Type  string `jsonschema:"type;description:eg. GPA or PERCENTAGE - [Add score type]"`
	Value string `jsonschema:"value;description:eg. 3.4/4.0 - [Add obtained score/total score]"`
}

type resourceSchema struct {
	URL   string `jsonschema:"url;description:e.g. http://www.example.com/my-awesome-slides/"`
	Label string `jsonschema:"label;description:e.g Slides"`
}

type coreSchema struct {
	Name              string         `jsonschema:"name;description:e.g. John Doe"`
	Title             string         `jsonschema:"title;description:e.g. Software Engineer"`
	Image             string         `jsonschema:"image;description:e.g. example.com/Abcxyz - [URL (as per RFC 3986) to a image in JPEG or PNG format]"`
	Email             string         `jsonschema:"email;description:e.g. lucas@example.com;format:email"`
	Phone             string         `jsonschema:"phone;description:e.g. 912-217-7923 - [Phone numbers are stored as strings so use any format you like]"`
	URL               string         `jsonschema:"url;description:e.g. http://www.example.com/my-slides/;format:uri"`
	Summary           string         `jsonschema:"summary;description:Write a short 2-3 sentence biography about yourself"`
	CurrentLocation   locationSchema `jsonschema:"currentLocation;description:Select the location where you currently live.;format:location"`
	PermanentLocation locationSchema `jsonschema:"permanentLocation;description:Select the location where you permanently live.;format:location"`
}

type workSchema struct {
	Name        string         `jsonschema:"name;description:e.g. XYZ Inc. - [Company name]"`
	Description string         `jsonschema:"description;description:e.g. A social media company - [Description of the companies primary activity]"`
	Position    string         `jsonschema:"position;description:e.g. Software Engineer - [Position at the company]"`
	Location    locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this activity];format:location"`
	URL         string         `jsonschema:"url;description:e.g. http://xyz.example.com - [Related link to the company website];format:uri"`
	StartDate   string         `jsonschema:"startDate;description:e.g. 2017-06-28 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string         `jsonschema:"endDate;description:e.g. 2018-12-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Summary     string         `jsonschema:"summary;description:Give an overview of your responsibilities at the company"`
	Highlights  []string       `jsonschema:"highlights;description:Specify multiple accomplishments;items_description:e.g. Worked with mobile team at Twitter to develop remote debugging tools for mobile browsers"`
}

type educationSchema struct {
	Institution string         `jsonschema:"institution;description:e.g. XYZ Institute of Technology - [Add institute name]"`
	Location    locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this institution];format:location"`
	Area        string         `jsonschema:"area;description:e.g. Engineering"`
	StudyType   string         `jsonschema:"studyType;description:e.g. Bachelor"`
	StartDate   string         `jsonschema:"startDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string         `jsonschema:"endDate;description:e.g. 2013-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Score       scoreSchema    `jsonschema:"score;additionalProperties"`
	Courses     []string       `jsonschema:"courses;description:List notable courses/subjects;items_description:e.g. CS302 - Introduction to Algorithms - [Add course name]"`
	Honors      []string       `jsonschema:"honors;description:List education honours;items_description:e.g. Magna Cum Laude"`
}

type volunteerSchema struct {
	Organization string         `jsonschema:"organization;description:e.g. Xyz "`
	Position     string         `jsonschema:"position;description:e.g. Open Source Contributor - [Contribution type]"`
	Location     locationSchema `jsonschema:"location;description:e.g. Germany - [Location for this activity];format:location"`
	URL          string         `jsonschema:"url;description:e.g. http://xyz.example.com - [Related link to support volunteer experience];format:uri"`
	StartDate    string         `jsonschema:"startDate;description:e.g. 2014-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate      string         `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard] ;format:date"`
	Summary      string         `jsonschema:"summary;description:Give an overview of your responsibilities at the company"`
	Highlights   []string       `jsonschema:"highlights;description:Specify accomplishments and achievements;items_description:e.g Invited as a speaker in Xyzcon'17"`
}

type publicationSchema struct {
	Name        string           `jsonschema:"name;description:e.g. Deep learning and Artificial Intelligence"`
	Publisher   string           `jsonschema:"publisher;description:e.g. XYZ, Computer Magazine"`
	ReleaseDate string           `jsonschema:"releaseDate;description:e.g. 2015-08-01 - [resume.json uses the ISO 8601 date standard]"`
	Resources   []resourceSchema `jsonschema:"resources;description:Specify multiple resources with label"`
	URL         string           `jsonschema:"url;description:e.g. http://www.computer.org.example.com/csdl/mags/co/2015/10/rx069-abs.html;format:uri"`
	Summary     string           `jsonschema:"summary;description:e.g. Discussion of the advent of deep learning and artificial intelligence - [Short summary of publication]"`
}

type legalSchema struct {
	Name            string           `jsonschema:"name;description:e.g. XYZ's patent on LZW compression, a fundamental part of the widely used GIF graphics format - [Add document name]"`
	LegalType       string           `jsonschema:"legalType;description:e.g. Patent, Trademark, Copyright - [Give the type of this document]"`
	Description     string           `jsonschema:"description;description:Give a brief description about this document"`
	ApplicationDate string           `jsonschema:"applicationDate;description:e.g. 2015-08-01 - [resume.json uses the ISO 8601 date standard];format:date"`
	GrantDate       string           `jsonschema:"grantDate;description:e.g. 2016-09-01 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate         string           `jsonschema:"endDate;description:e.g. 2020-09-03 - [resume.json uses the ISO 8601 date standard];format:date"`
	Resources       []resourceSchema `jsonschema:"resources;description:Specify multiple resources with label"`
	IDNumber        string           `jsonschema:"idNumber;description:e.g. JP2004369746A - [Add the application number or Id Number]  "`
}

type skillSchema struct {
	Name    string `jsonschema:"name;description:e.g. Web Development"`
	Keyword []struct {
		Name  string      `jsonschema:"name;description:e.g. HTML - [Add the skill name]"`
		Score scoreSchema `jsonschema:"score;additionalProperties;description:Score for the skill name"`
	} `jsonschema:"keyword;description:List some keywords pertaining to the skill;items_additionalProperties"`
}

type awardSchema struct {
	Title   string `jsonschema:"title;description:e.g. Awarded Software Process Achievement Award "`
	Date    string `jsonschema:"date;description:e.g. 2016-06-12 - [resume.json uses the ISO 8601 date standard];format:date"`
	Awarder string `jsonschema:"awarder;description:e.g. IEEE"`
	Summary string `jsonschema:"summary;description:e.g. Received for my work in Deep learning and AI"`
}

type projectSchema struct {
	Name        string           `jsonschema:"name;description:e.g. File Transfer application - [Name of the project]"`
	Location    locationSchema   `jsonschema:"location;description:e.g France - [Location for this activity];format:location"`
	Description string           `jsonschema:"description;description:e.g. Developed a client and server based application - [Short summary of project]"`
	Highlights  []string         `jsonschema:"highlights;description:Specify multiple features;items_description:e.g. used Java AWT and Swing for client side userinterface"`
	Keywords    []string         `jsonschema:"keywords;description:Specify special elements involved;items_description:e.g. Java"`
	StartDate   string           `jsonschema:"startDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	EndDate     string           `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard] ;format:date"`
	Resources   []resourceSchema `jsonschema:"resources;description:Specify multiple resources with label"`
	URL         string           `jsonschema:"url;description:e.g. http://www.example.org/csdl/mags/co/1996/10/rx069-abs.html;format:uri"`
	Roles       []string         `jsonschema:"roles;description:Specify your role on this project or in company;items_description:e.g. Team Lead, Speaker, Writer"`
	Entity      string           `jsonschema:"entity;description:e.g. 'greenpeace', 'corporationXYZ' - [Relevant company/entity affiliations]"`
	Type        string           `jsonschema:"type;description:e.g. 'volunteering', 'presentation', 'talk', 'application', 'conference'"`
}

type certificateSchema struct {
	Code          string      `jsonschema:"code;description:e.g. 1Z0-062"`
	Name          string      `jsonschema:"name;description:e.g. XYZ Certified Application Specialist (MCAS) - [Add the certificate name]"`
	Website       string      `jsonschema:"website;description:Link to issuing authority's description of the certificate;format:uri"`
	Verification  string      `jsonschema:"verification;description:External candidate verification URL;format:uri"`
	GrantDate     string      `jsonschema:"grantDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	Score         scoreSchema `jsonschema:"score;additionalProperties;description:Exam result (PASS/FAIL, 100%, 100)"`
	EndDate       string      `jsonschema:"endDate;description:e.g. 2017-06-29 - [resume.json uses the ISO 8601 date standard];format:date"`
	DoesNotExpire bool        `jsonschema:"doesNotExpire;format:checkbox"`
}

type referenceSchema struct {
	Name      string `jsonschema:"name;description:e.g. Stephan Mark"`
	Company   string `jsonschema:"company;description:e.g. Xyz"`
	Position  string `jsonschema:"position;description:e.g. Senior Software Engineer"`
	Reference string `jsonschema:"reference;description:e.g. Joe blogs was a great employee, who turned up to work at least once a week. He exceeded my expectations when it came to doing nothing."`
}

type languageSchema struct {
	Language string      `jsonschema:"language;description:e.g. English - [Name of language]"`
	Score    scoreSchema `jsonschema:"score;additionalProperties;description:Score for the language"`
}

type interestSchema struct {
	Name     string   `jsonschema:"name;description:e.g. Machine Learning"`
	Keywords []string `jsonschema:"keywords;description:Specify keywords associated with the particular interest;items_description:e.g. Neural Networks, Convoluted Neural Networks"`
}

type metaSchema struct {
	Canonical    string `jsonschema:"canonical;description:URL (as per RFC 3986) to latest version of this document"`
	Version      string `jsonschema:"version;description:e.g. v1.0.0 - [A version field which follows semver]"`
	LastModified string `jsonschema:"lastModified;description:e.g. 2017-06-29T15:53:00 - [resume.json uses the ISO 8601 date standard];format:date"`
}

type Schema struct {
	Core         coreSchema          `jsonschema:"core;additionalProperties"`
	Work         []workSchema        `jsonschema:"work;items_additionalProperties"`
	Education    []educationSchema   `jsonschema:"education;items_additionalProperties"`
	Volunteer    []volunteerSchema   `jsonschema:"volunteer;items_additionalProperties"`
	Publications []publicationSchema `jsonschema:"publications;description:Specify your publications;items_additionalProperties"`
	Legal        []legalSchema       `jsonschema:"legal;description:Specify your labels;items_additionalProperties"`
	Skills       []skillSchema       `jsonschema:"skills;description:List out your professional skill-set;items_additionalProperties"`
	Awards       []awardSchema       `jsonschema:"awards;description:Specify any awards you have received throughout your professional career;items_additionalProperties"`
	Projects     []projectSchema     `jsonschema:"projects;description:Specify career projects;items_additionalProperties"`
	Certificates []certificateSchema `jsonschema:"certificate;items_additionalProperties"`
	References   []referenceSchema   `jsonschema:"references;description:List references you have received;items_additionalProperties"`
	Languages    []languageSchema    `jsonschema:"languages;description:List any other languages you speak;items_additionalProperties"`
	Interests    []interestSchema    `jsonschema:"interests;items_additionalProperties"`
	Meta         metaSchema          `jsonschema:"meta;description:The schema version and any other tooling configuration lives here;additionalProperties"`
}
<|endoftext|>


<commit_before>package sacloud

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

var (
	// SakuraCloudAPIRoot APIリクエスト送信先ルートURL(末尾にスラッシュを含まない)
	SakuraCloudAPIRoot = "https://secure.sakura.ad.jp/cloud/zone"
)

const (
	// LogLevelInfo INFOレベル
	LogLevelInfo = "INFO"
	// LogLevelWarn WARNレベル
	LogLevelWarn = "WARN"
	// LogLevelDebug DEBUGレベル
	LogLevelDebug = "DEBUG"
	// LogLevelTrace TRACEレベル
	LogLevelTrace = "TRACE"
)

// APICaller API呼び出し時に利用するトランスポートのインターフェース
type APICaller interface {
	Do(ctx context.Context, method, uri string, body interface{}) ([]byte, error)
}

// Client APIクライアント、APICallerインターフェースを実装する
type Client struct {
	// AccessToken アクセストークン
	AccessToken string `validate:"required"`
	// AccessTokenSecret アクセストークンシークレット
	AccessTokenSecret string `validate:"required"`
	// Zone 対象ゾーン
	Zone string // TODO ゾーンは各リソースに持たせた方がよくないか？
	// LogLevel ログレベル [TRACE / DEBUG / WARN / INFO(default)]
	LogLevel string
	// DefaultTimeoutDuration デフォルトタイムアウト間隔
	DefaultTimeoutDuration time.Duration
	// ユーザーエージェント
	UserAgent string
	// Accept-Language
	AcceptLanguage string
	// 503エラー時のリトライ回数
	RetryMax int
	// 503エラー時のリトライ待ち時間
	RetryInterval time.Duration
	// APIコール時に利用される*http.Client 未指定の場合http.DefaultClientが利用される
	HTTPClient *http.Client
}

// NewClient APIクライアント作成
func NewClient(token, tokenSecret, zone string) *Client {
	c := &Client{
		AccessToken:            token,
		AccessTokenSecret:      tokenSecret,
		Zone:                   zone,
		LogLevel:               LogLevelInfo,
		DefaultTimeoutDuration: 20 * time.Minute,
		UserAgent:              fmt.Sprintf("libsacloud/%s", Version),
		AcceptLanguage:         "",
		RetryMax:               0,
		RetryInterval:          5 * time.Second,
	}
	return c
}

// Clone APIクライアント クローン作成
func (c *Client) Clone() *Client {
	n := &Client{
		AccessToken:            c.AccessToken,
		AccessTokenSecret:      c.AccessTokenSecret,
		Zone:                   c.Zone,
		DefaultTimeoutDuration: c.DefaultTimeoutDuration,
		UserAgent:              c.UserAgent,
		AcceptLanguage:         c.AcceptLanguage,
		RetryMax:               c.RetryMax,
		RetryInterval:          c.RetryInterval,
		HTTPClient:             c.HTTPClient,
	}
	return n
}

func (c *Client) getEndpoint() string {
	return fmt.Sprintf("%s/%s", SakuraCloudAPIRoot, c.Zone)
}

func (c *Client) isOkStatus(code int) bool {
	codes := map[int]bool{
		200: true,
		201: true,
		202: true,
		204: true,
		305: false,
		400: false,
		401: false,
		403: false,
		404: false,
		405: false,
		406: false,
		408: false,
		409: false,
		411: false,
		413: false,
		415: false,
		423: false,
		500: false,
		503: false,
	}
	return codes[code]
}

// Do APIコール実施
func (c *Client) Do(ctx context.Context, method, uri string, body interface{}) ([]byte, error) {
	var (
		client = &retryableHTTPClient{
			Client:        c.HTTPClient,
			retryMax:      c.RetryMax,
			retryInterval: c.RetryInterval,
		}
		err     error
		req     *request
		strBody string
	)
	var url = uri
	if !strings.HasPrefix(url, "https://") {
		url = fmt.Sprintf("%s/%s", c.getEndpoint(), uri)
	}

	if body != nil {
		var bodyJSON []byte
		bodyJSON, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
		if method == "GET" {
			url = fmt.Sprintf("%s?%s", url, bytes.NewBuffer(bodyJSON))
			req, err = newRequest(ctx, method, url, nil)
		} else {
			req, err = newRequest(ctx, method, url, bytes.NewReader(bodyJSON))
		}
		b, _ := json.MarshalIndent(body, "", "\t")
		strBody = string(b)
		log.Printf("[TRACE] method : %#v , url : %s , \nbody : %s", method, url, strBody)
	} else {
		req, err = newRequest(ctx, method, url, nil)
		log.Printf("[TRACE] method : %#v , url : %s ", method, url)
	}

	if err != nil {
		return nil, fmt.Errorf("Error with request: %v - %q", url, err)
	}

	req.SetBasicAuth(c.AccessToken, c.AccessTokenSecret)
	req.Header.Add("X-Sakura-Bigint-As-Int", "1") //Use BigInt on resource ids.
	req.Header.Add("User-Agent", c.UserAgent)
	if c.AcceptLanguage != "" {
		req.Header.Add("Accept-Language", c.AcceptLanguage)
	}
	req.Method = method

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)

	v := &map[string]interface{}{}
	json.Unmarshal(data, v)
	b, _ := json.MarshalIndent(v, "", "\t")
	log.Printf("[TRACE] response: %s", b)
	if !c.isOkStatus(resp.StatusCode) {

		errResponse := &APIErrorResponse{}
		err := json.Unmarshal(data, errResponse)

		if err != nil {
			return nil, fmt.Errorf("Error in response: %s", string(data))
		}
		return nil, NewAPIError(req.Method, req.URL, strBody, resp.StatusCode, errResponse)

	}
	if err != nil {
		return nil, err
	}

	return data, nil
}

type lenReader interface {
	Len() int
}

type request struct {
	// body is a seekable reader over the request body payload. This is
	// used to rewind the request data in between retries.
	body io.ReadSeeker

	// Embed an HTTP request directly. This makes a *Request act exactly
	// like an *http.Request so that all meta methods are supported.
	*http.Request
}

func newRequest(ctx context.Context, method, url string, body io.ReadSeeker) (*request, error) {
	var rcBody io.ReadCloser
	if body != nil {
		rcBody = ioutil.NopCloser(body)
	}

	httpReq, err := http.NewRequest(method, url, rcBody)
	if err != nil {
		return nil, err
	}

	if lr, ok := body.(lenReader); ok {
		httpReq.ContentLength = int64(lr.Len())
	}

	return &request{body, httpReq.WithContext(ctx)}, nil
}

type retryableHTTPClient struct {
	*http.Client
	retryInterval time.Duration
	retryMax      int
}

func (c *retryableHTTPClient) Do(req *request) (*http.Response, error) {
	if c.Client == nil {
		c.Client = http.DefaultClient
	}
	for i := 0; ; i++ {

		if req.body != nil {
			if _, err := req.body.Seek(0, 0); err != nil {
				return nil, fmt.Errorf("failed to seek body: %v", err)
			}
		}

		res, err := c.Client.Do(req.Request)
		if res != nil && res.StatusCode != http.StatusServiceUnavailable && res.StatusCode != http.StatusLocked {
			return res, err
		}
		if res != nil && res.Body != nil {
			res.Body.Close()
		}

		if err != nil {
			return res, err
		}

		remain := c.retryMax - i
		if remain == 0 {
			break
		}
		time.Sleep(c.retryInterval)
	}

	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.retryMax+1)
}
<commit_msg>Filter trace logs<commit_after>package sacloud

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

var (
	// SakuraCloudAPIRoot APIリクエスト送信先ルートURL(末尾にスラッシュを含まない)
	SakuraCloudAPIRoot = "https://secure.sakura.ad.jp/cloud/zone"
)

const (
	// LogLevelInfo INFOレベル
	LogLevelInfo = "INFO"
	// LogLevelWarn WARNレベル
	LogLevelWarn = "WARN"
	// LogLevelDebug DEBUGレベル
	LogLevelDebug = "DEBUG"
	// LogLevelTrace TRACEレベル
	LogLevelTrace = "TRACE"
)

// APICaller API呼び出し時に利用するトランスポートのインターフェース
type APICaller interface {
	Do(ctx context.Context, method, uri string, body interface{}) ([]byte, error)
}

// Client APIクライアント、APICallerインターフェースを実装する
type Client struct {
	// AccessToken アクセストークン
	AccessToken string `validate:"required"`
	// AccessTokenSecret アクセストークンシークレット
	AccessTokenSecret string `validate:"required"`
	// Zone 対象ゾーン
	Zone string // TODO ゾーンは各リソースに持たせた方がよくないか？
	// LogLevel ログレベル [TRACE / DEBUG / WARN / INFO(default)]
	LogLevel string
	// DefaultTimeoutDuration デフォルトタイムアウト間隔
	DefaultTimeoutDuration time.Duration
	// ユーザーエージェント
	UserAgent string
	// Accept-Language
	AcceptLanguage string
	// 503エラー時のリトライ回数
	RetryMax int
	// 503エラー時のリトライ待ち時間
	RetryInterval time.Duration
	// APIコール時に利用される*http.Client 未指定の場合http.DefaultClientが利用される
	HTTPClient *http.Client
}

// NewClient APIクライアント作成
func NewClient(token, tokenSecret, zone string) *Client {
	c := &Client{
		AccessToken:            token,
		AccessTokenSecret:      tokenSecret,
		Zone:                   zone,
		LogLevel:               LogLevelInfo,
		DefaultTimeoutDuration: 20 * time.Minute,
		UserAgent:              fmt.Sprintf("libsacloud/%s", Version),
		AcceptLanguage:         "",
		RetryMax:               0,
		RetryInterval:          5 * time.Second,
	}
	return c
}

// Clone APIクライアント クローン作成
func (c *Client) Clone() *Client {
	n := &Client{
		AccessToken:            c.AccessToken,
		AccessTokenSecret:      c.AccessTokenSecret,
		Zone:                   c.Zone,
		DefaultTimeoutDuration: c.DefaultTimeoutDuration,
		UserAgent:              c.UserAgent,
		AcceptLanguage:         c.AcceptLanguage,
		RetryMax:               c.RetryMax,
		RetryInterval:          c.RetryInterval,
		HTTPClient:             c.HTTPClient,
	}
	return n
}

func (c *Client) getEndpoint() string {
	return fmt.Sprintf("%s/%s", SakuraCloudAPIRoot, c.Zone)
}

func (c *Client) isOkStatus(code int) bool {
	codes := map[int]bool{
		200: true,
		201: true,
		202: true,
		204: true,
		305: false,
		400: false,
		401: false,
		403: false,
		404: false,
		405: false,
		406: false,
		408: false,
		409: false,
		411: false,
		413: false,
		415: false,
		423: false,
		500: false,
		503: false,
	}
	return codes[code]
}

// Do APIコール実施
func (c *Client) Do(ctx context.Context, method, uri string, body interface{}) ([]byte, error) {
	var (
		client = &retryableHTTPClient{
			Client:        c.HTTPClient,
			retryMax:      c.RetryMax,
			retryInterval: c.RetryInterval,
		}
		err     error
		req     *request
		strBody string
	)
	var url = uri
	if !strings.HasPrefix(url, "https://") {
		url = fmt.Sprintf("%s/%s", c.getEndpoint(), uri)
	}

	if body != nil {
		var bodyJSON []byte
		bodyJSON, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
		if method == "GET" {
			url = fmt.Sprintf("%s?%s", url, bytes.NewBuffer(bodyJSON))
			req, err = newRequest(ctx, method, url, nil)
		} else {
			req, err = newRequest(ctx, method, url, bytes.NewReader(bodyJSON))
		}
		b, _ := json.MarshalIndent(body, "", "\t")
		strBody = string(b)
		if c.LogLevel == LogLevelTrace {
			log.Printf("[TRACE] method : %#v , url : %s , \nbody : %s", method, url, strBody)
		}
	} else {
		req, err = newRequest(ctx, method, url, nil)
		if c.LogLevel == LogLevelTrace {
			log.Printf("[TRACE] method : %#v , url : %s ", method, url)
		}
	}

	if err != nil {
		return nil, fmt.Errorf("Error with request: %v - %q", url, err)
	}

	req.SetBasicAuth(c.AccessToken, c.AccessTokenSecret)
	req.Header.Add("X-Sakura-Bigint-As-Int", "1") //Use BigInt on resource ids.
	req.Header.Add("User-Agent", c.UserAgent)
	if c.AcceptLanguage != "" {
		req.Header.Add("Accept-Language", c.AcceptLanguage)
	}
	req.Method = method

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)

	v := &map[string]interface{}{}
	json.Unmarshal(data, v)
	b, _ := json.MarshalIndent(v, "", "\t")
	if c.LogLevel == LogLevelTrace {
		log.Printf("[TRACE] response: %s", b)
	}
	if !c.isOkStatus(resp.StatusCode) {

		errResponse := &APIErrorResponse{}
		err := json.Unmarshal(data, errResponse)

		if err != nil {
			return nil, fmt.Errorf("Error in response: %s", string(data))
		}
		return nil, NewAPIError(req.Method, req.URL, strBody, resp.StatusCode, errResponse)

	}
	if err != nil {
		return nil, err
	}

	return data, nil
}

type lenReader interface {
	Len() int
}

type request struct {
	// body is a seekable reader over the request body payload. This is
	// used to rewind the request data in between retries.
	body io.ReadSeeker

	// Embed an HTTP request directly. This makes a *Request act exactly
	// like an *http.Request so that all meta methods are supported.
	*http.Request
}

func newRequest(ctx context.Context, method, url string, body io.ReadSeeker) (*request, error) {
	var rcBody io.ReadCloser
	if body != nil {
		rcBody = ioutil.NopCloser(body)
	}

	httpReq, err := http.NewRequest(method, url, rcBody)
	if err != nil {
		return nil, err
	}

	if lr, ok := body.(lenReader); ok {
		httpReq.ContentLength = int64(lr.Len())
	}

	return &request{body, httpReq.WithContext(ctx)}, nil
}

type retryableHTTPClient struct {
	*http.Client
	retryInterval time.Duration
	retryMax      int
}

func (c *retryableHTTPClient) Do(req *request) (*http.Response, error) {
	if c.Client == nil {
		c.Client = http.DefaultClient
	}
	for i := 0; ; i++ {

		if req.body != nil {
			if _, err := req.body.Seek(0, 0); err != nil {
				return nil, fmt.Errorf("failed to seek body: %v", err)
			}
		}

		res, err := c.Client.Do(req.Request)
		if res != nil && res.StatusCode != http.StatusServiceUnavailable && res.StatusCode != http.StatusLocked {
			return res, err
		}
		if res != nil && res.Body != nil {
			res.Body.Close()
		}

		if err != nil {
			return res, err
		}

		remain := c.retryMax - i
		if remain == 0 {
			break
		}
		time.Sleep(c.retryInterval)
	}

	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.retryMax+1)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"net/http"
	"time"
)

const (
	jwtAlg = "alg"
	jwtKid = "kid"
)

const (
	algNone = "none"
)

func responseToken(w http.ResponseWriter, tok *token) error {
	m := map[string]interface{}{
		formTokId:   tok.Id,
		formTokType: tokTypeBear,
	}
	if !tok.ExpiDate.IsZero() {
		m[formExpi] = int64(tok.ExpiDate.Sub(time.Now()).Seconds())
	}
	if tok.RefTok != "" {
		m[formRefTok] = tok.RefTok
	}
	if len(tok.Scops) > 0 {
		var buff string
		for scop := range tok.Scops {
			if len(buff) > 0 {
				buff += " "
			}
			buff += scop
		}
		m[formScop] = buff
	}
	buff, err := json.Marshal(m)
	if err != nil {
		return erro.Wrap(err)
	}

	w.Header().Add("Cache-Control", "no-store")
	w.Header().Add("Pragma", "no-cache")
	if _, err := w.Write(buff); err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
	}
	return nil
}

func tokenApi(sys *system, w http.ResponseWriter, r *http.Request) error {
	req := newTokenRequest(r)

	if grntType := req.grantType(); grntType == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formGrntType)
	} else if grntType == grntTypeCod {
		return responseError(w, http.StatusBadRequest, errUnsuppGrntType, grntType+" is not supported")
	}

	log.Debug("Grant type is " + grntTypeCod)

	codId := req.code()
	if codId == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formCod)
	}

	log.Debug("Code " + mosaic(codId) + " is declared")

	cod, err := sys.codCont.get(codId)
	if err != nil {
		return erro.Wrap(err)
	} else if cod == nil {
		return responseError(w, http.StatusBadRequest, errInvGrnt, "code "+codId+" is not exist")
	}

	log.Debug("Code " + mosaic(codId) + " is exist")

	taId := req.taId()
	if taId == "" {
		taId = cod.TaId
		log.Debug("TA ID is " + taId + " in code")
	} else if taId != cod.TaId {
		return responseError(w, http.StatusBadRequest, errInvTa, "you are not code holder")
	} else {
		log.Debug("TA ID " + taId + " is declared")
	}

	rediUri := req.redirectUri()
	if rediUri == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formRediUri)
	} else if rediUri != cod.RediUri {
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid "+formRediUri)
	}

	log.Debug(formRediUri + " matches that of code")

	if taAssType := req.taAssertionType(); taAssType == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+formTaAssType)
	} else if taAssType != taAssTypeJwt {
		return responseError(w, http.StatusBadRequest, errInvTa, taAssType+" is not supported")
	}

	log.Debug(formTaAssType + " is " + taAssTypeJwt)

	taAss := req.taAssertion()
	if taAss == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+formTaAss)
	}

	log.Debug(formTaAss + " is found")

	jws, err := util.ParseJws(taAss)
	if err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
		return responseError(w, http.StatusBadRequest, errInvTa, erro.Unwrap(err).Error())
	}

	if jws.Claim(clmIss) != taId {
		return responseError(w, http.StatusBadRequest, errInvTa, clmIss+" is not "+taId)
	} else if jws.Claim(clmSub) != taId {
		return responseError(w, http.StatusBadRequest, errInvTa, clmSub+" is not "+taId)
	} else if jti := jws.Claim(clmJti); jti == nil || jti == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmJti)
	}
	exp, _ := jws.Claim(clmExp).(float64)
	if exp == 0 {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmExp)
	} else if exp != float64(int64(exp)) {
		return responseError(w, http.StatusBadRequest, errInvTa, clmExp+" is not integer")
	}
	aud := jws.Claim(clmAud)
	if aud == nil {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmAud)
	}
	switch a := aud.(type) {
	case string:
		if a != sys.selfId+tokPath {
			return responseError(w, http.StatusBadRequest, errInvTa, clmAud+" is not "+sys.selfId+tokPath)
		}
	case []interface{}:
		ok := false
		for _, b := range a {
			c, _ := b.(string)
			if c == sys.selfId {
				ok = true
				break
			}
		}
		if !ok {
			return responseError(w, http.StatusBadRequest, errInvTa, clmAud+" does not have "+sys.selfId)
		}
	default:
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid"+clmAud)
	}
	if c := jws.Claim(clmCod); c != codId {
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid"+clmCod)
	}

	log.Debug("JWS claims are OK")

	ta, err := sys.taCont.get(taId)
	if err != nil {
		return erro.Wrap(err)
	}

	if alg := jws.Header(jwtAlg); alg == algNone {
		return responseError(w, http.StatusBadRequest, errInvTa, "JWS algorithm "+algNone+" is not allowed")
	} else if err := jws.Verify(ta.pubKeys); err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
		return responseError(w, http.StatusBadRequest, errInvTa, erro.Unwrap(err).Error())
	}

	log.Debug(taId + " is authenticated")

	tok, err := sys.tokCont.new(cod.AccId, 0)
	if err != nil {
		return erro.Wrap(err)
	}

	log.Debug("Token " + mosaic(tok.Id) + " is generated")

	return responseToken(w, tok)
}
<commit_msg>アクセストークンリクエストの aud 値が複数の値を含む場合の挙動を修正<commit_after>package main

import (
	"encoding/json"
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"net/http"
	"time"
)

const (
	jwtAlg = "alg"
	jwtKid = "kid"
)

const (
	algNone = "none"
)

func responseToken(w http.ResponseWriter, tok *token) error {
	m := map[string]interface{}{
		formTokId:   tok.Id,
		formTokType: tokTypeBear,
	}
	if !tok.ExpiDate.IsZero() {
		m[formExpi] = int64(tok.ExpiDate.Sub(time.Now()).Seconds())
	}
	if tok.RefTok != "" {
		m[formRefTok] = tok.RefTok
	}
	if len(tok.Scops) > 0 {
		var buff string
		for scop := range tok.Scops {
			if len(buff) > 0 {
				buff += " "
			}
			buff += scop
		}
		m[formScop] = buff
	}
	buff, err := json.Marshal(m)
	if err != nil {
		return erro.Wrap(err)
	}

	w.Header().Add("Cache-Control", "no-store")
	w.Header().Add("Pragma", "no-cache")
	if _, err := w.Write(buff); err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
	}
	return nil
}

func tokenApi(sys *system, w http.ResponseWriter, r *http.Request) error {
	req := newTokenRequest(r)

	if grntType := req.grantType(); grntType == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formGrntType)
	} else if grntType == grntTypeCod {
		return responseError(w, http.StatusBadRequest, errUnsuppGrntType, grntType+" is not supported")
	}

	log.Debug("Grant type is " + grntTypeCod)

	codId := req.code()
	if codId == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formCod)
	}

	log.Debug("Code " + mosaic(codId) + " is declared")

	cod, err := sys.codCont.get(codId)
	if err != nil {
		return erro.Wrap(err)
	} else if cod == nil {
		return responseError(w, http.StatusBadRequest, errInvGrnt, "code "+codId+" is not exist")
	}

	log.Debug("Code " + mosaic(codId) + " is exist")

	taId := req.taId()
	if taId == "" {
		taId = cod.TaId
		log.Debug("TA ID is " + taId + " in code")
	} else if taId != cod.TaId {
		return responseError(w, http.StatusBadRequest, errInvTa, "you are not code holder")
	} else {
		log.Debug("TA ID " + taId + " is declared")
	}

	rediUri := req.redirectUri()
	if rediUri == "" {
		return responseError(w, http.StatusBadRequest, errInvReq, "no "+formRediUri)
	} else if rediUri != cod.RediUri {
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid "+formRediUri)
	}

	log.Debug(formRediUri + " matches that of code")

	if taAssType := req.taAssertionType(); taAssType == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+formTaAssType)
	} else if taAssType != taAssTypeJwt {
		return responseError(w, http.StatusBadRequest, errInvTa, taAssType+" is not supported")
	}

	log.Debug(formTaAssType + " is " + taAssTypeJwt)

	taAss := req.taAssertion()
	if taAss == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+formTaAss)
	}

	log.Debug(formTaAss + " is found")

	jws, err := util.ParseJws(taAss)
	if err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
		return responseError(w, http.StatusBadRequest, errInvTa, erro.Unwrap(err).Error())
	}

	if jws.Claim(clmIss) != taId {
		return responseError(w, http.StatusBadRequest, errInvTa, clmIss+" is not "+taId)
	} else if jws.Claim(clmSub) != taId {
		return responseError(w, http.StatusBadRequest, errInvTa, clmSub+" is not "+taId)
	} else if jti := jws.Claim(clmJti); jti == nil || jti == "" {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmJti)
	}
	exp, _ := jws.Claim(clmExp).(float64)
	if exp == 0 {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmExp)
	} else if exp != float64(int64(exp)) {
		return responseError(w, http.StatusBadRequest, errInvTa, clmExp+" is not integer")
	}
	aud := jws.Claim(clmAud)
	if aud == nil {
		return responseError(w, http.StatusBadRequest, errInvTa, "no "+clmAud)
	}
	switch a := aud.(type) {
	case string:
		if a != sys.selfId+tokPath {
			return responseError(w, http.StatusBadRequest, errInvTa, clmAud+" is not "+sys.selfId+tokPath)
		}
	case []interface{}:
		ok := false
		for _, b := range a {
			c, _ := b.(string)
			if c == sys.selfId+tokPath {
				ok = true
				break
			}
		}
		if !ok {
			return responseError(w, http.StatusBadRequest, errInvTa, clmAud+" does not have "+sys.selfId+tokPath)
		}
	default:
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid"+clmAud)
	}
	if c := jws.Claim(clmCod); c != codId {
		return responseError(w, http.StatusBadRequest, errInvTa, "invalid"+clmCod)
	}

	log.Debug("JWS claims are OK")

	ta, err := sys.taCont.get(taId)
	if err != nil {
		return erro.Wrap(err)
	}

	if alg := jws.Header(jwtAlg); alg == algNone {
		return responseError(w, http.StatusBadRequest, errInvTa, "JWS algorithm "+algNone+" is not allowed")
	} else if err := jws.Verify(ta.pubKeys); err != nil {
		err = erro.Wrap(err)
		log.Err(erro.Unwrap(err))
		log.Debug(err)
		return responseError(w, http.StatusBadRequest, errInvTa, erro.Unwrap(err).Error())
	}

	log.Debug(taId + " is authenticated")

	tok, err := sys.tokCont.new(cod.AccId, 0)
	if err != nil {
		return erro.Wrap(err)
	}

	log.Debug("Token " + mosaic(tok.Id) + " is generated")

	return responseToken(w, tok)
}
<|endoftext|>


<commit_before>package main

// TweetTable html template
var TweetTable = `

{{.Handle}}'s Tweets

		Text

	{{range .List}}

		{{.Text}}

	{{end}}

`
<commit_msg>Updated table html template<commit_after>package main

// TweetTable html template
var TweetTable = `

{{.Handle}}'s Tweets

		Date
		Text
		Replies
		Upvotes
		Retweets

	{{range .Tweets}}

		{{.Date}}
		{{.Text}}
		{{.Replies}}
		{{.Upvotes}}
		{{.Retweets}}

	{{end}}

`
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"encoding/asn1"
	"errors"
	"io"
	"math/big"

	pb "github.com/libp2p/go-libp2p-core/crypto/pb"

	sha256 "github.com/minio/sha256-simd"
)

// ECDSAPrivateKey is an implementation of an ECDSA private key
type ECDSAPrivateKey struct {
	priv *ecdsa.PrivateKey
}

// ECDSAPublicKey is an implementation of an ECDSA public key
type ECDSAPublicKey struct {
	pub *ecdsa.PublicKey
}

// ECDSASig holds the r and s values of an ECDSA signature
type ECDSASig struct {
	R, S *big.Int
}

var (
	// ErrNotECDSAPubKey is returned when the public key passed is not an ecdsa public key
	ErrNotECDSAPubKey = errors.New("not an ecdsa public key")
	// ErrNilSig is returned when the signature is nil
	ErrNilSig = errors.New("sig is nil")
	// ErrNilPrivateKey is returned when a nil private key is provided
	ErrNilPrivateKey = errors.New("private key is nil")
	// ECDSACurve is the default ecdsa curve used
	ECDSACurve = elliptic.P256()
)

// GenerateECDSAKeyPair generates a new ecdsa private and public key
func GenerateECDSAKeyPair(src io.Reader) (PrivKey, PubKey, error) {
	return GenerateECDSAKeyPairWithCurve(ECDSACurve, src)
}

// GenerateECDSAKeyPairWithCurve generates a new ecdsa private and public key with a speicified curve
func GenerateECDSAKeyPairWithCurve(curve elliptic.Curve, src io.Reader) (PrivKey, PubKey, error) {
	priv, err := ecdsa.GenerateKey(curve, src)
	if err != nil {
		return nil, nil, err
	}

	return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// ECDSAKeyPairFromKey generates a new ecdsa private and public key from an input private key
func ECDSAKeyPairFromKey(priv *ecdsa.PrivateKey) (PrivKey, PubKey, error) {
	if priv == nil {
		return nil, nil, ErrNilPrivateKey
	}

	return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// MarshalECDSAPrivateKey returns x509 bytes from a private key
func MarshalECDSAPrivateKey(ePriv ECDSAPrivateKey) ([]byte, error) {
	return x509.MarshalECPrivateKey(ePriv.priv)
}

// MarshalECDSAPublicKey returns x509 bytes from a public key
func MarshalECDSAPublicKey(ePub ECDSAPublicKey) ([]byte, error) {
	return x509.MarshalPKIXPublicKey(ePub.pub)
}

// UnmarshalECDSAPrivateKey returns a private key from x509 bytes
func UnmarshalECDSAPrivateKey(data []byte) (PrivKey, error) {
	priv, err := x509.ParseECPrivateKey(data)
	if err != nil {
		return nil, err
	}

	return &ECDSAPrivateKey{priv}, nil
}

// UnmarshalECDSAPublicKey returns the public key from x509 bytes
func UnmarshalECDSAPublicKey(data []byte) (PubKey, error) {
	pubIfc, err := x509.ParsePKIXPublicKey(data)
	if err != nil {
		return nil, err
	}

	pub, ok := pubIfc.(*ecdsa.PublicKey)
	if !ok {
		return nil, ErrNotECDSAPubKey
	}

	return &ECDSAPublicKey{pub}, nil
}

// Bytes returns the private key as protobuf bytes
func (ePriv *ECDSAPrivateKey) Bytes() ([]byte, error) {
	return MarshalPrivateKey(ePriv)
}

// Type returns the key type
func (ePriv *ECDSAPrivateKey) Type() pb.KeyType {
	return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a private key
func (ePriv *ECDSAPrivateKey) Raw() ([]byte, error) {
	return x509.MarshalECPrivateKey(ePriv.priv)
}

// Equals compares two private keys
func (ePriv *ECDSAPrivateKey) Equals(o Key) bool {
	oPriv, ok := o.(*ECDSAPrivateKey)
	if !ok {
		return basicEquals(ePriv, o)
	}

	return ePriv.priv.D.Cmp(oPriv.priv.D) == 0
}

// Sign returns the signature of the input data
func (ePriv *ECDSAPrivateKey) Sign(data []byte) ([]byte, error) {
	hash := sha256.Sum256(data)
	r, s, err := ecdsa.Sign(rand.Reader, ePriv.priv, hash[:])
	if err != nil {
		return nil, err
	}

	return asn1.Marshal(ECDSASig{
		R: r,
		S: s,
	})
}

// GetPublic returns a public key
func (ePriv *ECDSAPrivateKey) GetPublic() PubKey {
	return &ECDSAPublicKey{&ePriv.priv.PublicKey}
}

// Bytes returns the public key as protobuf bytes
func (ePub *ECDSAPublicKey) Bytes() ([]byte, error) {
	return MarshalPublicKey(ePub)
}

// Type returns the key type
func (ePub *ECDSAPublicKey) Type() pb.KeyType {
	return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a public key
func (ePub *ECDSAPublicKey) Raw() ([]byte, error) {
	return x509.MarshalPKIXPublicKey(ePub.pub)
}

// Equals compares to public keys
func (ePub *ECDSAPublicKey) Equals(o Key) bool {
	oPub, ok := o.(*ECDSAPublicKey)
	if !ok {
		return basicEquals(ePub, o)
	}

	return ePub.pub.X != nil && ePub.pub.Y != nil && oPub.pub.X != nil && oPub.pub.Y != nil &&
		0 == ePub.pub.X.Cmp(oPub.pub.X) && 0 == ePub.pub.Y.Cmp(oPub.pub.Y)
}

// Verify compares data to a signature
func (ePub *ECDSAPublicKey) Verify(data, sigBytes []byte) (bool, error) {
	sig := new(ECDSASig)
	if _, err := asn1.Unmarshal(sigBytes, sig); err != nil {
		return false, err
	}
	if sig == nil {
		return false, ErrNilSig
	}

	hash := sha256.Sum256(data)

	return ecdsa.Verify(ePub.pub, hash[:], sig.R, sig.S), nil
}
<commit_msg>crypto: use basicEquals for ECDSA<commit_after>package crypto

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"encoding/asn1"
	"errors"
	"io"
	"math/big"

	pb "github.com/libp2p/go-libp2p-core/crypto/pb"

	sha256 "github.com/minio/sha256-simd"
)

// ECDSAPrivateKey is an implementation of an ECDSA private key
type ECDSAPrivateKey struct {
	priv *ecdsa.PrivateKey
}

// ECDSAPublicKey is an implementation of an ECDSA public key
type ECDSAPublicKey struct {
	pub *ecdsa.PublicKey
}

// ECDSASig holds the r and s values of an ECDSA signature
type ECDSASig struct {
	R, S *big.Int
}

var (
	// ErrNotECDSAPubKey is returned when the public key passed is not an ecdsa public key
	ErrNotECDSAPubKey = errors.New("not an ecdsa public key")
	// ErrNilSig is returned when the signature is nil
	ErrNilSig = errors.New("sig is nil")
	// ErrNilPrivateKey is returned when a nil private key is provided
	ErrNilPrivateKey = errors.New("private key is nil")
	// ECDSACurve is the default ecdsa curve used
	ECDSACurve = elliptic.P256()
)

// GenerateECDSAKeyPair generates a new ecdsa private and public key
func GenerateECDSAKeyPair(src io.Reader) (PrivKey, PubKey, error) {
	return GenerateECDSAKeyPairWithCurve(ECDSACurve, src)
}

// GenerateECDSAKeyPairWithCurve generates a new ecdsa private and public key with a speicified curve
func GenerateECDSAKeyPairWithCurve(curve elliptic.Curve, src io.Reader) (PrivKey, PubKey, error) {
	priv, err := ecdsa.GenerateKey(curve, src)
	if err != nil {
		return nil, nil, err
	}

	return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// ECDSAKeyPairFromKey generates a new ecdsa private and public key from an input private key
func ECDSAKeyPairFromKey(priv *ecdsa.PrivateKey) (PrivKey, PubKey, error) {
	if priv == nil {
		return nil, nil, ErrNilPrivateKey
	}

	return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// MarshalECDSAPrivateKey returns x509 bytes from a private key
func MarshalECDSAPrivateKey(ePriv ECDSAPrivateKey) ([]byte, error) {
	return x509.MarshalECPrivateKey(ePriv.priv)
}

// MarshalECDSAPublicKey returns x509 bytes from a public key
func MarshalECDSAPublicKey(ePub ECDSAPublicKey) ([]byte, error) {
	return x509.MarshalPKIXPublicKey(ePub.pub)
}

// UnmarshalECDSAPrivateKey returns a private key from x509 bytes
func UnmarshalECDSAPrivateKey(data []byte) (PrivKey, error) {
	priv, err := x509.ParseECPrivateKey(data)
	if err != nil {
		return nil, err
	}

	return &ECDSAPrivateKey{priv}, nil
}

// UnmarshalECDSAPublicKey returns the public key from x509 bytes
func UnmarshalECDSAPublicKey(data []byte) (PubKey, error) {
	pubIfc, err := x509.ParsePKIXPublicKey(data)
	if err != nil {
		return nil, err
	}

	pub, ok := pubIfc.(*ecdsa.PublicKey)
	if !ok {
		return nil, ErrNotECDSAPubKey
	}

	return &ECDSAPublicKey{pub}, nil
}

// Bytes returns the private key as protobuf bytes
func (ePriv *ECDSAPrivateKey) Bytes() ([]byte, error) {
	return MarshalPrivateKey(ePriv)
}

// Type returns the key type
func (ePriv *ECDSAPrivateKey) Type() pb.KeyType {
	return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a private key
func (ePriv *ECDSAPrivateKey) Raw() ([]byte, error) {
	return x509.MarshalECPrivateKey(ePriv.priv)
}

// Equals compares two private keys
func (ePriv *ECDSAPrivateKey) Equals(o Key) bool {
	return basicEquals(ePriv, o)
}

// Sign returns the signature of the input data
func (ePriv *ECDSAPrivateKey) Sign(data []byte) ([]byte, error) {
	hash := sha256.Sum256(data)
	r, s, err := ecdsa.Sign(rand.Reader, ePriv.priv, hash[:])
	if err != nil {
		return nil, err
	}

	return asn1.Marshal(ECDSASig{
		R: r,
		S: s,
	})
}

// GetPublic returns a public key
func (ePriv *ECDSAPrivateKey) GetPublic() PubKey {
	return &ECDSAPublicKey{&ePriv.priv.PublicKey}
}

// Bytes returns the public key as protobuf bytes
func (ePub *ECDSAPublicKey) Bytes() ([]byte, error) {
	return MarshalPublicKey(ePub)
}

// Type returns the key type
func (ePub *ECDSAPublicKey) Type() pb.KeyType {
	return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a public key
func (ePub *ECDSAPublicKey) Raw() ([]byte, error) {
	return x509.MarshalPKIXPublicKey(ePub.pub)
}

// Equals compares to public keys
func (ePub *ECDSAPublicKey) Equals(o Key) bool {
	return basicEquals(ePub, o)
}

// Verify compares data to a signature
func (ePub *ECDSAPublicKey) Verify(data, sigBytes []byte) (bool, error) {
	sig := new(ECDSASig)
	if _, err := asn1.Unmarshal(sigBytes, sig); err != nil {
		return false, err
	}
	if sig == nil {
		return false, ErrNilSig
	}

	hash := sha256.Sum256(data)

	return ecdsa.Verify(ePub.pub, hash[:], sig.R, sig.S), nil
}
<|endoftext|>


<commit_before>package runner

import (
	"log"
	"os/exec"

	"github.com/fallenhitokiri/leeroyci/database"
)

var RunQueue = make(chan int64, 100)

func Runner() {
	for {
		jobID := <-RunQueue

		job := database.GetJob(jobID)
		repository, err := database.GetRepositoryByID(string(job.RepositoryID))

		if err != nil {
			log.Println("Could not find repository for", job.Repository.URL)
			return
		}

		run(job, repository, database.CommandKindTest)
		run(job, repository, database.CommandKindBuild)
		job.TasksDone()

		run(job, repository, database.CommandKindDeploy)
		job.DeployDone()
	}
}

func run(job *database.Job, repository *database.Repository, kind string) {
	commands := repository.GetCommands(job.Branch, kind)

	for _, command := range commands {
		if command.Kind == kind {
			if (command.Branch != "" && command.Branch == job.Branch) || command.Branch == "" {
				repository := job.Repository.Name
				branch := job.Branch

				log.Println("Running", command.Name, "for", repository, branch)

				cmd := exec.Command(command.Execute, repository, branch)
				out, err := cmd.CombinedOutput()

				returnValue := ""

				if err != nil {
					returnValue = err.Error()
				}

				database.CreateCommandLog(&command, job, returnValue, string(out))
			}
		}
	}
}
<commit_msg>add comments<commit_after>// Package runner runs all tasks for all commands associated with a repository.
package runner

import (
	"log"
	"os/exec"

	"github.com/fallenhitokiri/leeroyci/database"
)

// RunQueue receives job IDs for which commands should run.
var RunQueue = make(chan int64, 100)

// Runner waits for jobs to be pushed on RunQueue and runs all commands. It also
// creates the command logs and sends the necessary notifications.
func Runner() {
	for {
		jobID := <-RunQueue

		job := database.GetJob(jobID)
		repository, err := database.GetRepositoryByID(string(job.RepositoryID))

		if err != nil {
			log.Println("Could not find repository for", job.Repository.URL)
			return
		}

		run(job, repository, database.CommandKindTest)
		run(job, repository, database.CommandKindBuild)
		job.TasksDone()

		run(job, repository, database.CommandKindDeploy)
		job.DeployDone()
	}
}

// run runs the command that is specified in Command.Execute and creates the
// command log with the results of the command.
func run(job *database.Job, repository *database.Repository, kind string) {
	commands := repository.GetCommands(job.Branch, kind)

	for _, command := range commands {
		if command.Kind == kind {
			if (command.Branch != "" && command.Branch == job.Branch) || command.Branch == "" {
				repository := job.Repository.Name
				branch := job.Branch

				log.Println("Running", command.Name, "for", repository, branch)

				cmd := exec.Command(command.Execute, repository, branch)
				out, err := cmd.CombinedOutput()

				returnValue := ""

				if err != nil {
					returnValue = err.Error()
				}

				database.CreateCommandLog(&command, job, returnValue, string(out))
			}
		}
	}
}
<|endoftext|>