<commit_before>package router

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/ewhal/nyaa/model"
	"github.com/ewhal/nyaa/service/captcha"
	"github.com/ewhal/nyaa/service/user"
	"github.com/ewhal/nyaa/service/user/form"
	"github.com/ewhal/nyaa/service/user/permission"
	"github.com/ewhal/nyaa/util/languages"
	"github.com/ewhal/nyaa/util/modelHelper"
	"github.com/gorilla/mux"
)

// Getting View User Registration
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
	_, errorUser := userService.CurrentUser(r)
	if errorUser != nil {
		b := form.RegistrationForm{}
		modelHelper.BindValueForm(&b, r)
		b.CaptchaID = captcha.GetID()
		languages.SetTranslationFromRequest(viewRegisterTemplate, r, "en-us")
		htv := UserRegisterTemplateVariables{b, form.NewErrors(), NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
		err := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	} else {
		HomeHandler(w, r)
	}
}

// Getting View User Login
func UserLoginFormHandler(w http.ResponseWriter, r *http.Request) {
	b := form.LoginForm{}
	modelHelper.BindValueForm(&b, r)

	languages.SetTranslationFromRequest(viewLoginTemplate, r, "en-us")
	htv := UserLoginFormVariables{b, form.NewErrors(), NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}

	err := viewLoginTemplate.ExecuteTemplate(w, "index.html", htv)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Getting User Profile
func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		currentUser := GetUser(r)
		view := r.URL.Query()["edit"]
		follow := r.URL.Query()["followed"]
		unfollow := r.URL.Query()["unfollowed"]
		infosForm := form.NewInfos()
		deleteVar := r.URL.Query()["delete"]

		if (view != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
			b := form.UserForm{}
			modelHelper.BindValueForm(&b, r)
			languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
			htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}

			err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		} else if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
			err := form.NewErrors()
			_, errUser := userService.DeleteUser(w, currentUser, id)
			if errUser != nil {
				err["errors"] = append(err["errors"], errUser.Error())
			}
			languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us")
			htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			T := languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us")
			if follow != nil {
				infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_followed_msg"), userProfile.Username))
			}
			if unfollow != nil {
				infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
			}
			htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}

			err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	} else {
		searchForm := NewSearchForm()
		searchForm.HideAdvancedSearch = true

		languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
		err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

// Getting View User Profile Update
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	currentUser := GetUser(r)
	userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) {
			b := form.UserForm{}
			err := form.NewErrors()
			infos := form.NewInfos()
			T := languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
			if len(r.PostFormValue("email")) > 0 {
				_, err = form.EmailValidation(r.PostFormValue("email"), err)
			}
			if len(r.PostFormValue("username")) > 0 {
				_, err = form.ValidateUsername(r.PostFormValue("username"), err)
			}
			if len(err) == 0 {
				modelHelper.BindValueForm(&b, r)
				err = modelHelper.ValidateForm(&b, err)
				if len(err) == 0 {
					userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id)
					if errorUser != nil {
						err["errors"] = append(err["errors"], errorUser.Error())
					}
					if len(err) == 0 {
						infos["infos"] = append(infos["infos"], T("profile_updated"))
					}
				}
			}
			htv := UserProfileEditVariables{&userProfile, b, err, infos, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			searchForm := NewSearchForm()
			searchForm.HideAdvancedSearch = true

			languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
			err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	} else {
		searchForm := NewSearchForm()
		searchForm.HideAdvancedSearch = true

		languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
		err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

// Post Registration controller, we do some check on the form here, the rest on user service
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
	// Check same Password
	b := form.RegistrationForm{}
	err := form.NewErrors()
	if !captcha.Authenticate(captcha.Extract(r)) {
		err["errors"] = append(err["errors"], "Wrong captcha!")
	}
	if len(err) == 0 {
		if len(r.PostFormValue("email")) > 0 {
			_, err = form.EmailValidation(r.PostFormValue("email"), err)
		}
		_, err = form.ValidateUsername(r.PostFormValue("username"), err)
		if len(err) == 0 {
			modelHelper.BindValueForm(&b, r)
			err = modelHelper.ValidateForm(&b, err)
			if len(err) == 0 {
				_, errorUser := userService.CreateUser(w, r)
				if errorUser != nil {
					err["errors"] = append(err["errors"], errorUser.Error())
				}
				if len(err) == 0 {
					languages.SetTranslationFromRequest(viewRegisterSuccessTemplate, r, "en-us")
					u := model.User{
						Email: r.PostFormValue("email"), // indicate whether user had email set
					}
					htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, &u, r.URL, mux.CurrentRoute(r)}
					errorTmpl := viewRegisterSuccessTemplate.ExecuteTemplate(w, "index.html", htv)
					if errorTmpl != nil {
						http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
					}
				}
			}
		}
	}
	if len(err) > 0 {
		b.CaptchaID = captcha.GetID()
		languages.SetTranslationFromRequest(viewRegisterTemplate, r, "en-us")
		htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
		errorTmpl := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
		if errorTmpl != nil {
			http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
		}
	}
}

func UserVerifyEmailHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	token := vars["token"]
	err := form.NewErrors()
	_, errEmail := userService.EmailVerification(token, w)
	if errEmail != nil {
		err["errors"] = append(err["errors"], errEmail.Error())
	}
	languages.SetTranslationFromRequest(viewVerifySuccessTemplate, r, "en-us")
	htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
	errorTmpl := viewVerifySuccessTemplate.ExecuteTemplate(w, "index.html", htv)
	if errorTmpl != nil {
		http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
	}
}

// Post Login controller
func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) {
	b := form.LoginForm{}
	modelHelper.BindValueForm(&b, r)
	err := form.NewErrors()
	err = modelHelper.ValidateForm(&b, err)
	if len(err) == 0 {
		_, errorUser := userService.CreateUserAuthentication(w, r)
		if errorUser != nil {
			err["errors"] = append(err["errors"], errorUser.Error())
			languages.SetTranslationFromRequest(viewLoginTemplate, r, "en-us")
			htv := UserLoginFormVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewLoginTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			url, _ := Router.Get("home").URL()
			http.Redirect(w, r, url.String(), http.StatusSeeOther)
		}

	}

}

// Logout
func UserLogoutHandler(w http.ResponseWriter, r *http.Request) {
	_, _ = userService.ClearCookie(w)
	url, _ := Router.Get("home").URL()
	http.Redirect(w, r, url.String(), http.StatusSeeOther)
}

func UserFollowHandler(w http.ResponseWriter, r *http.Request) {
	var followAction string
	vars := mux.Vars(r)
	id := vars["id"]
	currentUser := GetUser(r)
	user, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		if !userPermission.IsFollower(&user, currentUser) {
			followAction = "followed"
			userService.SetFollow(&user, currentUser)
		} else {
			followAction = "unfollowed"
			userService.RemoveFollow(&user, currentUser)
		}
	}
	url, _ := Router.Get("user_profile").URL("id", strconv.Itoa(int(user.ID)), "username", user.Username)
	http.Redirect(w, r, url.String()+"?"+followAction, http.StatusSeeOther)
}
<commit_msg>Fix the advanced search field showing up on user profile pages. Fixes #230.<commit_after>package router

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/ewhal/nyaa/model"
	"github.com/ewhal/nyaa/service/captcha"
	"github.com/ewhal/nyaa/service/user"
	"github.com/ewhal/nyaa/service/user/form"
	"github.com/ewhal/nyaa/service/user/permission"
	"github.com/ewhal/nyaa/util/languages"
	"github.com/ewhal/nyaa/util/modelHelper"
	"github.com/gorilla/mux"
)

// Getting View User Registration
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
	_, errorUser := userService.CurrentUser(r)
	if errorUser != nil {
		b := form.RegistrationForm{}
		modelHelper.BindValueForm(&b, r)
		b.CaptchaID = captcha.GetID()
		languages.SetTranslationFromRequest(viewRegisterTemplate, r, "en-us")
		htv := UserRegisterTemplateVariables{b, form.NewErrors(), NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
		err := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	} else {
		HomeHandler(w, r)
	}
}

// Getting View User Login
func UserLoginFormHandler(w http.ResponseWriter, r *http.Request) {
	b := form.LoginForm{}
	modelHelper.BindValueForm(&b, r)

	languages.SetTranslationFromRequest(viewLoginTemplate, r, "en-us")
	htv := UserLoginFormVariables{b, form.NewErrors(), NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}

	err := viewLoginTemplate.ExecuteTemplate(w, "index.html", htv)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Getting User Profile
func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		currentUser := GetUser(r)
		view := r.URL.Query()["edit"]
		follow := r.URL.Query()["followed"]
		unfollow := r.URL.Query()["unfollowed"]
		infosForm := form.NewInfos()
		deleteVar := r.URL.Query()["delete"]

		if (view != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
			b := form.UserForm{}
			modelHelper.BindValueForm(&b, r)
			languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
			searchForm := NewSearchForm()
			searchForm.HideAdvancedSearch = true
			htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}

			err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		} else if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
			err := form.NewErrors()
			_, errUser := userService.DeleteUser(w, currentUser, id)
			if errUser != nil {
				err["errors"] = append(err["errors"], errUser.Error())
			}
			languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us")
			searchForm := NewSearchForm()
			searchForm.HideAdvancedSearch = true
			htv := UserVerifyTemplateVariables{err, searchForm, Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			T := languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us")
			if follow != nil {
				infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_followed_msg"), userProfile.Username))
			}
			if unfollow != nil {
				infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
			}
			searchForm := NewSearchForm()
			searchForm.HideAdvancedSearch = true
			htv := UserProfileVariables{&userProfile, infosForm, searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}

			err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	} else {
		searchForm := NewSearchForm()
		searchForm.HideAdvancedSearch = true

		languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
		err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

// Getting View User Profile Update
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	currentUser := GetUser(r)
	userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) {
			b := form.UserForm{}
			err := form.NewErrors()
			infos := form.NewInfos()
			T := languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
			if len(r.PostFormValue("email")) > 0 {
				_, err = form.EmailValidation(r.PostFormValue("email"), err)
			}
			if len(r.PostFormValue("username")) > 0 {
				_, err = form.ValidateUsername(r.PostFormValue("username"), err)
			}
			if len(err) == 0 {
				modelHelper.BindValueForm(&b, r)
				err = modelHelper.ValidateForm(&b, err)
				if len(err) == 0 {
					userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id)
					if errorUser != nil {
						err["errors"] = append(err["errors"], errorUser.Error())
					}
					if len(err) == 0 {
						infos["infos"] = append(infos["infos"], T("profile_updated"))
					}
				}
			}
			htv := UserProfileEditVariables{&userProfile, b, err, infos, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			searchForm := NewSearchForm()
			searchForm.HideAdvancedSearch = true

			languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
			err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	} else {
		searchForm := NewSearchForm()
		searchForm.HideAdvancedSearch = true

		languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
		err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

// Post Registration controller, we do some check on the form here, the rest on user service
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
	// Check same Password
	b := form.RegistrationForm{}
	err := form.NewErrors()
	if !captcha.Authenticate(captcha.Extract(r)) {
		err["errors"] = append(err["errors"], "Wrong captcha!")
	}
	if len(err) == 0 {
		if len(r.PostFormValue("email")) > 0 {
			_, err = form.EmailValidation(r.PostFormValue("email"), err)
		}
		_, err = form.ValidateUsername(r.PostFormValue("username"), err)
		if len(err) == 0 {
			modelHelper.BindValueForm(&b, r)
			err = modelHelper.ValidateForm(&b, err)
			if len(err) == 0 {
				_, errorUser := userService.CreateUser(w, r)
				if errorUser != nil {
					err["errors"] = append(err["errors"], errorUser.Error())
				}
				if len(err) == 0 {
					languages.SetTranslationFromRequest(viewRegisterSuccessTemplate, r, "en-us")
					u := model.User{
						Email: r.PostFormValue("email"), // indicate whether user had email set
					}
					htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, &u, r.URL, mux.CurrentRoute(r)}
					errorTmpl := viewRegisterSuccessTemplate.ExecuteTemplate(w, "index.html", htv)
					if errorTmpl != nil {
						http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
					}
				}
			}
		}
	}
	if len(err) > 0 {
		b.CaptchaID = captcha.GetID()
		languages.SetTranslationFromRequest(viewRegisterTemplate, r, "en-us")
		htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
		errorTmpl := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
		if errorTmpl != nil {
			http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
		}
	}
}

func UserVerifyEmailHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	token := vars["token"]
	err := form.NewErrors()
	_, errEmail := userService.EmailVerification(token, w)
	if errEmail != nil {
		err["errors"] = append(err["errors"], errEmail.Error())
	}
	languages.SetTranslationFromRequest(viewVerifySuccessTemplate, r, "en-us")
	htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
	errorTmpl := viewVerifySuccessTemplate.ExecuteTemplate(w, "index.html", htv)
	if errorTmpl != nil {
		http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
	}
}

// Post Login controller
func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) {
	b := form.LoginForm{}
	modelHelper.BindValueForm(&b, r)
	err := form.NewErrors()
	err = modelHelper.ValidateForm(&b, err)
	if len(err) == 0 {
		_, errorUser := userService.CreateUserAuthentication(w, r)
		if errorUser != nil {
			err["errors"] = append(err["errors"], errorUser.Error())
			languages.SetTranslationFromRequest(viewLoginTemplate, r, "en-us")
			htv := UserLoginFormVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
			errorTmpl := viewLoginTemplate.ExecuteTemplate(w, "index.html", htv)
			if errorTmpl != nil {
				http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
			}
		} else {
			url, _ := Router.Get("home").URL()
			http.Redirect(w, r, url.String(), http.StatusSeeOther)
		}

	}

}

// Logout
func UserLogoutHandler(w http.ResponseWriter, r *http.Request) {
	_, _ = userService.ClearCookie(w)
	url, _ := Router.Get("home").URL()
	http.Redirect(w, r, url.String(), http.StatusSeeOther)
}

func UserFollowHandler(w http.ResponseWriter, r *http.Request) {
	var followAction string
	vars := mux.Vars(r)
	id := vars["id"]
	currentUser := GetUser(r)
	user, _, errorUser := userService.RetrieveUserForAdmin(id)
	if errorUser == nil {
		if !userPermission.IsFollower(&user, currentUser) {
			followAction = "followed"
			userService.SetFollow(&user, currentUser)
		} else {
			followAction = "unfollowed"
			userService.RemoveFollow(&user, currentUser)
		}
	}
	url, _ := Router.Get("user_profile").URL("id", strconv.Itoa(int(user.ID)), "username", user.Username)
	http.Redirect(w, r, url.String()+"?"+followAction, http.StatusSeeOther)
}
<|endoftext|>


<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/mock/mock_test.go                                    |
|                                                          |
| LastModified: Feb 21, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package mock

import (
	"testing"
	"time"

	"github.com/hprose/hprose-golang/v3/rpc/core"
	"github.com/hprose/hprose-golang/v3/rpc/plugins/log"
	"github.com/hprose/hprose-golang/v3/rpc/plugins/timeout"
	"github.com/stretchr/testify/assert"
)

func TestHelloWorld(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(name string) string {
		return "hello " + name
	}, "hello")
	server := Server{"testHelloWorld"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testHelloWorld")
	client.Use(log.IOHandler)
	var proxy struct {
		Hello func(name string) string
	}
	client.UseService(&proxy)
	result := proxy.Hello("world")
	assert.Equal(t, "hello world", result)
	server.Close()
}

func TestClientTimeout(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(d time.Duration) {
		time.Sleep(d)
	}, "wait")
	server := Server{"testClientTimeout"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testClientTimeout")
	client.Use(log.IOHandler)
	client.Timeout = time.Millisecond
	var proxy struct {
		Wait func(d time.Duration) error
	}
	client.UseService(&proxy)
	err = proxy.Wait(time.Second * 30)
	assert.True(t, core.IsTimeoutError(err))
	server.Close()
}

func TestServiceTimeout(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(d time.Duration) {
		time.Sleep(d)
	}, "wait")
	service.Use(timeout.GetHandler(time.Millisecond), log.IOHandler)
	server := Server{"testServiceTimeout"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testServiceTimeout")
	var proxy struct {
		Wait func(d time.Duration) error
	}
	client.UseService(&proxy)
	err = proxy.Wait(time.Second * 30)
	assert.True(t, core.IsTimeoutError(err))
	server.Close()
}
<commit_msg>update mock test<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/mock/mock_test.go                                    |
|                                                          |
| LastModified: Feb 21, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package mock

import (
	"context"
	"encoding/json"
	"testing"
	"time"

	"github.com/hprose/hprose-golang/v3/rpc/core"
	"github.com/hprose/hprose-golang/v3/rpc/plugins/log"
	"github.com/hprose/hprose-golang/v3/rpc/plugins/timeout"
	"github.com/stretchr/testify/assert"
)

func TestHelloWorld(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(name string) string {
		return "hello " + name
	}, "hello")
	server := Server{"testHelloWorld"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testHelloWorld")
	client.Use(log.IOHandler)
	var proxy struct {
		Hello func(name string) string
	}
	client.UseService(&proxy)
	result := proxy.Hello("world")
	assert.Equal(t, "hello world", result)
	server.Close()
}

func TestClientTimeout(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(d time.Duration) {
		time.Sleep(d)
	}, "wait")
	server := Server{"testClientTimeout"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testClientTimeout")
	client.Use(log.IOHandler)
	client.Timeout = time.Millisecond
	var proxy struct {
		Wait func(d time.Duration) error
	}
	client.UseService(&proxy)
	err = proxy.Wait(time.Second * 30)
	assert.True(t, core.IsTimeoutError(err))
	server.Close()
}

func TestServiceTimeout(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(d time.Duration) {
		time.Sleep(d)
	}, "wait")
	service.Use(timeout.GetHandler(time.Millisecond), log.IOHandler)
	server := Server{"testServiceTimeout"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testServiceTimeout")
	var proxy struct {
		Wait func(d time.Duration) error
	}
	client.UseService(&proxy)
	err = proxy.Wait(time.Second * 30)
	assert.True(t, core.IsTimeoutError(err))
	server.Close()
}

func TestMissingMethod(t *testing.T) {
	service := core.NewService()
	service.AddMissingMethod(func(name string, args []interface{}) (result []interface{}, err error) {
		data, err := json.Marshal(args)
		if err != nil {
			return nil, err
		}
		return []interface{}{name + string(data)}, nil
	})
	server := Server{"testMissingMethod"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testMissingMethod")
	client.Use(log.IOHandler)
	var proxy struct {
		Hello func(name string) string
	}
	client.UseService(&proxy)
	result := proxy.Hello("world")
	assert.Equal(t, `Hello["world"]`, result)
	server.Close()
}

func TestMissingMethod2(t *testing.T) {
	service := core.NewService()
	service.AddMissingMethod(func(ctx context.Context, name string, args []interface{}) (result []interface{}, err error) {
		serviceContext := core.GetServiceContext(ctx)
		data, err := json.Marshal(args)
		if err != nil {
			return nil, err
		}
		return []interface{}{name + string(data) + serviceContext.RemoteAddr.String()}, nil
	})
	server := Server{"testMissingMethod2"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testMissingMethod2")
	client.Use(log.IOHandler)
	var proxy struct {
		Hello func(name string) string
	}
	client.UseService(&proxy)
	result := proxy.Hello("world")
	assert.Equal(t, `Hello["world"]testMissingMethod2`, result)
	server.Close()
}

func TestHeaders(t *testing.T) {
	service := core.NewService()
	service.AddFunction(func(name string) string {
		return "hello " + name
	}, "hello")
	service.Use(func(ctx context.Context, name string, args []interface{}, next core.NextInvokeHandler) (result []interface{}, err error) {
		serviceContext := core.GetServiceContext(ctx)
		ping := serviceContext.RequestHeaders().GetBool("ping")
		assert.True(t, ping)
		serviceContext.ResponseHeaders().Set("pong", true)
		return next(ctx, name, args)
	})
	server := Server{"testHeaders"}
	err := service.Bind(server)
	assert.NoError(t, err)
	client := core.NewClient("mock://testHeaders")
	client.Use(log.IOHandler)
	var proxy struct {
		Hello func(ctx context.Context, name string) string `header:"ping"`
	}
	client.UseService(&proxy)
	clientContext := core.NewClientContext()
	ctx := core.WithContext(context.Background(), clientContext)
	result := proxy.Hello(ctx, "world")
	assert.Equal(t, `hello world`, result)
	assert.True(t, clientContext.ResponseHeaders().GetBool("pong"))
	server.Close()
}
<|endoftext|>


<commit_before>package rproxy

import (
	"testing"

	"github.com/stvp/assert"
)

func TestProxy(t *testing.T) {
	srv := StartFakeRedisServer()
	assert.Equal(t, srv.ReqCnt(), 0)

	srv.Addr()
	proxy, err := NewProxy(&ConstConfig{
		conf: &ProxyConfig{
			UplinkAddr: srv.Addr().String(),
			ListenOn:   "127.0.0.1:0",
			AdminOn:    "127.0.0.1:0",
		},
	})
	assert.Nil(t, err)
	assert.False(t, proxy.Alive())

	go proxy.Run()
	waitUntil(t, func() bool { return proxy.Alive() })

	proxy.controller.Stop()
	waitUntil(t, func() bool { return !proxy.Alive() })
}
<commit_msg>Remove leftover dead code.<commit_after>package rproxy

import (
	"testing"

	"github.com/stvp/assert"
)

func TestProxy(t *testing.T) {
	srv := StartFakeRedisServer()
	assert.Equal(t, srv.ReqCnt(), 0)

	proxy, err := NewProxy(&ConstConfig{
		conf: &ProxyConfig{
			UplinkAddr: srv.Addr().String(),
			ListenOn:   "127.0.0.1:0",
			AdminOn:    "127.0.0.1:0",
		},
	})
	assert.Nil(t, err)
	assert.False(t, proxy.Alive())

	go proxy.Run()
	waitUntil(t, func() bool { return proxy.Alive() })

	proxy.controller.Stop()
	waitUntil(t, func() bool { return !proxy.Alive() })
}
<|endoftext|>


<commit_before>package java

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/ezbuy/tgen/global"
	"github.com/ezbuy/tgen/langs"
	"github.com/ezbuy/tgen/tmpl"
	"github.com/samuel/go-thrift/parser"
)

const (
	JavaTypeshort  = "short"
	JavaTypeint    = "int"
	JavaTypelong   = "long"
	JavaTypebool   = "boolean"
	JavaTypebyte   = "byte"
	JavaTypedouble = "double"

	JavaTypeString = "String"

	JavaTypeShort  = "Short"
	JavaTypeInt    = "Integer"
	JavaTypeLong   = "Long"
	JavaTypeBool   = "Boolean"
	JavaTypeByte   = "Byte"
	JavaTypeDouble = "Double"

	// other types (such as array, map, etc.) are implemented in the method 'Typecast'
)

const (
	TPL_STRUCT  = "tgen/java/struct"
	TPL_SERVICE = "tgen/java/service"
)

var plaintypemapping = map[string]string{
	langs.ThriftTypeI16:    JavaTypeshort,
	langs.ThriftTypeI32:    JavaTypeint,
	langs.ThriftTypeI64:    JavaTypelong,
	langs.ThriftTypeString: JavaTypeString,
	langs.ThriftTypeByte:   JavaTypebyte,
	langs.ThriftTypeBool:   JavaTypebool,
	langs.ThriftTypeDouble: JavaTypedouble,
}

var objecttypemapping = map[string]string{
	langs.ThriftTypeI16:    JavaTypeShort,
	langs.ThriftTypeI32:    JavaTypeInt,
	langs.ThriftTypeI64:    JavaTypeLong,
	langs.ThriftTypeString: JavaTypeString,
	langs.ThriftTypeByte:   JavaTypeByte,
	langs.ThriftTypeBool:   JavaTypeBool,
	langs.ThriftTypeDouble: JavaTypeDouble,
}

type JavaGen struct {
	langs.BaseGen
}

type BaseJava struct {
	Namespace string
	t         *parser.Thrift
	ts        *map[string]*parser.Thrift
}

func (this *BaseJava) FilterVariableName(n string) string {
	if this.IsKeyword(n) {
		return fmt.Sprintf("t%s%s", strings.ToUpper(n[:1]), n[1:])
	}
	return n
}

func (this *BaseJava) IsKeyword(n string) bool {
	switch n {
	case "package", "int", "short", "long", "byte", "boolean", "case", "switch", "if ", "for", "else",
		"goto", "Integer", "Short", "Long", "Byte", "Boolean", "class", "break", "try", "catch",
		"double", "Double", "do", "while", "final", "finally", "continue", "interface", "private",
		"public", "protected", "return", "this", "throw", "static", "super", "throws",
		"true", "false", "float", "volatile", "synchronized", "abstract", "default", "extends",
		"native", "new":
		return true
	}
	return false
}

func (this *BaseJava) PlainTypecast(t *parser.Type) string {
	return this.typecast(t, true)
}

func (this *BaseJava) ObjectTypecast(t *parser.Type) string {
	return this.typecast(t, false)
}

func (this *BaseJava) typecast(t *parser.Type, isplain bool) string {
	if t == nil {
		if isplain {
			return "void"
		} else {
			return "Void"
		}
	}

	var typemapping map[string]string

	if isplain {
		typemapping = plaintypemapping
	} else {
		typemapping = objecttypemapping
	}

	if t, ok := typemapping[t.Name]; ok {
		return t
	}

	switch t.Name {
	case langs.ThriftTypeList, langs.ThriftTypeSet:
		return fmt.Sprintf("ArrayList<%s>", this.ObjectTypecast(t.ValueType))
	case langs.ThriftTypeMap:
		return fmt.Sprintf("Map<%s, %s>", this.ObjectTypecast(t.KeyType), this.ObjectTypecast(t.ValueType))
	default:
		s := strings.Split(t.Name, ".")
		if len(s) == 1 {
			return s[0]
		} else if len(s) == 2 {
			pkg := ""
			for k, v := range this.t.Includes {
				if k == s[0] {

					for p, t := range *this.ts {
						if v == p {
							pkg = t.Namespaces["java"]
							break
						}
					}

					break
				}
			}

			if pkg == "" {
				return s[1]
			}
			return fmt.Sprintf("%s.%s", pkg, s[1])
		} else {
			return t.Name
		}
	}
}

func (this *BaseJava) AssembleParams(method *parser.Method) string {
	var buf bytes.Buffer

	for i, arg := range method.Arguments {
		if i != 0 {
			buf.WriteString(", ")
		}

		buf.WriteString(fmt.Sprintf("final %s %s", this.PlainTypecast(arg.Type), this.FilterVariableName(arg.Name)))
	}

	if len(method.Arguments) == 0 {
		buf.WriteString("")
	} else {
		buf.WriteString(", ")
	}

	buf.WriteString(fmt.Sprintf("final Listener<%s> listener", this.ObjectTypecast(method.ReturnType)))

	return buf.String()
}

func (this *BaseJava) GetInnerType(t *parser.Type) string {
	if t == nil {
		return "Void"
	}

	// map is ignored
	if t.Name == langs.ThriftTypeList || t.Name == langs.ThriftTypeSet {
		return this.GetInnerType(t.ValueType)
	}

	return this.ObjectTypecast(t)
}

type javaStruct struct {
	*BaseJava
	*parser.Struct
}

func (this *javaStruct) HasKeyword() bool {
	for _, f := range this.Struct.Fields {
		if this.BaseJava.IsKeyword(f.Name) {
			return true
		}
	}
	return false
}

type javaService struct {
	*BaseJava
	*parser.Service
}

func generateAll(gen *JavaGen, output string, parsedThrift map[string]*parser.Thrift) {
	generateWithModel(gen, global.MODE_REST, filepath.Join(output, global.MODE_REST), parsedThrift)
	generateWithModel(gen, global.MODE_JSONRPC, filepath.Join(output, global.MODE_JSONRPC), parsedThrift)
}

func (g *JavaGen) Generate(output string, parsedThrift map[string]*parser.Thrift) {
	if global.Mode != "" {
		generateWithModel(g, global.Mode, output, parsedThrift)
	} else {
		generateAll(g, output, parsedThrift)
	}

	// generatejsonrpc(filepath.Join(output, "jsonrpc"), parsedThrift)
	// genraterest(filepath.Join(output, "rest"), parsedThrift)
}

func generateWithModel(gen *JavaGen, m string, output string, parsedThrift map[string]*parser.Thrift) {
	if m != global.MODE_REST && m != global.MODE_JSONRPC {
		log.Fatalf("mode '%s' is invalid", m)
	}

	gen.BaseGen.Init("java", parsedThrift)

	if err := os.MkdirAll(output, 0755); err != nil {
		panic(fmt.Errorf("failed to create output directory %s", output))
	}

	// init templates
	var structpl *template.Template
	var servicetpl *template.Template
	if m == global.MODE_REST {
		structpl = initemplate(TPL_STRUCT, "tmpl/java/rest_struct.gojava")
		servicetpl = initemplate(TPL_SERVICE, "tmpl/java/rest_service.gojava")
	} else if m == global.MODE_JSONRPC {
		structpl = initemplate(TPL_STRUCT, "tmpl/java/jsonrpc_struct.gojava")
		servicetpl = initemplate(TPL_SERVICE, "tmpl/java/jsonrpc_service.gojava")
	}

	// key is the absoule path of thrift file
	for tf, t := range parsedThrift {
		// due to java's features,
		// we generate the struct and service in seperate template file

		ns, ok := t.Namespaces["java"]
		if !ok {
			fmt.Fprintf(os.Stderr, "error: namespace not found in file[%s] of language[java]\n", tf)
			return
		}

		log.Printf("## structs")

		for _, s := range t.Structs {
			// filename is the struct name
			name := s.Name + ".java"

			// fix java file path
			p := filepath.Join(output, strings.Replace(ns, ".", "/", -1))
			if err := os.MkdirAll(p, 0755); err != nil {
				panic(fmt.Errorf("failed to create output directory %s", p))
			}

			path := filepath.Join(p, name)

			base := BaseJava{Namespace: ns, t: t, ts: &parsedThrift}
			data := &javaStruct{BaseJava: &base, Struct: s}

			if err := outputfile(path, structpl, TPL_STRUCT, data); err != nil {
				panic(fmt.Errorf("failed to write file %s. error: %v\n", path, err))
			}

			log.Printf("%s", path)
		}

		log.Printf("## services")

		for _, s := range t.Services {
			// filename is the service name plus 'Service'
			name := s.Name + "Service.java"

			// fix java file path
			p := filepath.Join(output, strings.Replace(ns, ".", "/", -1))
			if err := os.MkdirAll(p, 0755); err != nil {
				panic(fmt.Errorf("failed to create output directory %s", p))
			}

			path := filepath.Join(p, name)

			base := BaseJava{Namespace: ns, t: t, ts: &parsedThrift}
			data := &javaService{BaseJava: &base, Service: s}

			if err := outputfile(path, servicetpl, TPL_SERVICE, data); err != nil {
				panic(fmt.Errorf("failed to write file %s. error: %v\n", path, err))
			}

			log.Printf("%s", path)
		}
	}
}

func initemplate(n string, path string) *template.Template {
	data, err := tmpl.Asset(path)
	if err != nil {
		panic(err)
	}

	tpl, err := template.New(n).Parse(string(data))
	if err != nil {
		panic(err)
	}

	return tpl
}

func outputfile(fp string, t *template.Template, tplname string, data interface{}) error {
	file, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}

	defer file.Close()

	return t.ExecuteTemplate(file, tplname, data)
}

func init() {
	langs.Langs["java"] = &JavaGen{}
}
<commit_msg>remove unnecessary check<commit_after>package java

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/ezbuy/tgen/global"
	"github.com/ezbuy/tgen/langs"
	"github.com/ezbuy/tgen/tmpl"
	"github.com/samuel/go-thrift/parser"
)

const (
	JavaTypeshort  = "short"
	JavaTypeint    = "int"
	JavaTypelong   = "long"
	JavaTypebool   = "boolean"
	JavaTypebyte   = "byte"
	JavaTypedouble = "double"

	JavaTypeString = "String"

	JavaTypeShort  = "Short"
	JavaTypeInt    = "Integer"
	JavaTypeLong   = "Long"
	JavaTypeBool   = "Boolean"
	JavaTypeByte   = "Byte"
	JavaTypeDouble = "Double"

	// other types (such as array, map, etc.) are implemented in the method 'Typecast'
)

const (
	TPL_STRUCT  = "tgen/java/struct"
	TPL_SERVICE = "tgen/java/service"
)

var plaintypemapping = map[string]string{
	langs.ThriftTypeI16:    JavaTypeshort,
	langs.ThriftTypeI32:    JavaTypeint,
	langs.ThriftTypeI64:    JavaTypelong,
	langs.ThriftTypeString: JavaTypeString,
	langs.ThriftTypeByte:   JavaTypebyte,
	langs.ThriftTypeBool:   JavaTypebool,
	langs.ThriftTypeDouble: JavaTypedouble,
}

var objecttypemapping = map[string]string{
	langs.ThriftTypeI16:    JavaTypeShort,
	langs.ThriftTypeI32:    JavaTypeInt,
	langs.ThriftTypeI64:    JavaTypeLong,
	langs.ThriftTypeString: JavaTypeString,
	langs.ThriftTypeByte:   JavaTypeByte,
	langs.ThriftTypeBool:   JavaTypeBool,
	langs.ThriftTypeDouble: JavaTypeDouble,
}

type JavaGen struct {
	langs.BaseGen
}

type BaseJava struct {
	Namespace string
	t         *parser.Thrift
	ts        *map[string]*parser.Thrift
}

func (this *BaseJava) FilterVariableName(n string) string {
	if this.IsKeyword(n) {
		return fmt.Sprintf("t%s%s", strings.ToUpper(n[:1]), n[1:])
	}
	return n
}

func (this *BaseJava) IsKeyword(n string) bool {
	switch n {
	case "package", "int", "short", "long", "byte", "boolean", "case", "switch", "if ", "for", "else",
		"goto", "Integer", "Short", "Long", "Byte", "Boolean", "class", "break", "try", "catch",
		"double", "Double", "do", "while", "final", "finally", "continue", "interface", "private",
		"public", "protected", "return", "this", "throw", "static", "super", "throws",
		"true", "false", "float", "volatile", "synchronized", "abstract", "default", "extends",
		"native", "new":
		return true
	}
	return false
}

func (this *BaseJava) PlainTypecast(t *parser.Type) string {
	return this.typecast(t, true)
}

func (this *BaseJava) ObjectTypecast(t *parser.Type) string {
	return this.typecast(t, false)
}

func (this *BaseJava) typecast(t *parser.Type, isplain bool) string {
	if t == nil {
		if isplain {
			return "void"
		} else {
			return "Void"
		}
	}

	var typemapping map[string]string

	if isplain {
		typemapping = plaintypemapping
	} else {
		typemapping = objecttypemapping
	}

	if t, ok := typemapping[t.Name]; ok {
		return t
	}

	switch t.Name {
	case langs.ThriftTypeList, langs.ThriftTypeSet:
		return fmt.Sprintf("ArrayList<%s>", this.ObjectTypecast(t.ValueType))
	case langs.ThriftTypeMap:
		return fmt.Sprintf("Map<%s, %s>", this.ObjectTypecast(t.KeyType), this.ObjectTypecast(t.ValueType))
	default:
		s := strings.Split(t.Name, ".")
		if len(s) == 1 {
			return s[0]
		} else if len(s) == 2 {
			pkg := ""
			for k, v := range this.t.Includes {
				if k == s[0] {

					for p, t := range *this.ts {
						if v == p {
							pkg = t.Namespaces["java"]
							break
						}
					}

					break
				}
			}

			if pkg == "" {
				return s[1]
			}
			return fmt.Sprintf("%s.%s", pkg, s[1])
		} else {
			return t.Name
		}
	}
}

func (this *BaseJava) AssembleParams(method *parser.Method) string {
	var buf bytes.Buffer

	for i, arg := range method.Arguments {
		if i != 0 {
			buf.WriteString(", ")
		}

		buf.WriteString(fmt.Sprintf("final %s %s", this.PlainTypecast(arg.Type), this.FilterVariableName(arg.Name)))
	}

	if len(method.Arguments) == 0 {
		buf.WriteString("")
	} else {
		buf.WriteString(", ")
	}

	buf.WriteString(fmt.Sprintf("final Listener<%s> listener", this.ObjectTypecast(method.ReturnType)))

	return buf.String()
}

func (this *BaseJava) GetInnerType(t *parser.Type) string {
	if t == nil {
		return "Void"
	}

	// map is ignored
	if t.Name == langs.ThriftTypeList || t.Name == langs.ThriftTypeSet {
		return this.GetInnerType(t.ValueType)
	}

	return this.ObjectTypecast(t)
}

type javaStruct struct {
	*BaseJava
	*parser.Struct
}

func (this *javaStruct) HasKeyword() bool {
	for _, f := range this.Struct.Fields {
		if this.BaseJava.IsKeyword(f.Name) {
			return true
		}
	}
	return false
}

type javaService struct {
	*BaseJava
	*parser.Service
}

func generateAll(gen *JavaGen, output string, parsedThrift map[string]*parser.Thrift) {
	generateWithModel(gen, global.MODE_REST, filepath.Join(output, global.MODE_REST), parsedThrift)
	generateWithModel(gen, global.MODE_JSONRPC, filepath.Join(output, global.MODE_JSONRPC), parsedThrift)
}

func (g *JavaGen) Generate(output string, parsedThrift map[string]*parser.Thrift) {
	if global.Mode != "" {
		generateWithModel(g, global.Mode, output, parsedThrift)
	} else {
		generateAll(g, output, parsedThrift)
	}

	// generatejsonrpc(filepath.Join(output, "jsonrpc"), parsedThrift)
	// genraterest(filepath.Join(output, "rest"), parsedThrift)
}

func generateWithModel(gen *JavaGen, m string, output string, parsedThrift map[string]*parser.Thrift) {
	if m != global.MODE_REST && m != global.MODE_JSONRPC {
		log.Fatalf("mode '%s' is invalid", m)
	}

	gen.BaseGen.Init("java", parsedThrift)

	if err := os.MkdirAll(output, 0755); err != nil {
		panic(fmt.Errorf("failed to create output directory %s", output))
	}

	// init templates
	var structpl *template.Template
	var servicetpl *template.Template
	if m == global.MODE_REST {
		structpl = initemplate(TPL_STRUCT, "tmpl/java/rest_struct.gojava")
		servicetpl = initemplate(TPL_SERVICE, "tmpl/java/rest_service.gojava")
	} else if m == global.MODE_JSONRPC {
		structpl = initemplate(TPL_STRUCT, "tmpl/java/jsonrpc_struct.gojava")
		servicetpl = initemplate(TPL_SERVICE, "tmpl/java/jsonrpc_service.gojava")
	}

	// key is the absoule path of thrift file
	for _, t := range parsedThrift {
		// due to java's features,
		// we generate the struct and service in seperate template file

		ns := t.Namespaces["java"]

		log.Printf("## structs")

		for _, s := range t.Structs {
			// filename is the struct name
			name := s.Name + ".java"

			// fix java file path
			p := filepath.Join(output, strings.Replace(ns, ".", "/", -1))
			if err := os.MkdirAll(p, 0755); err != nil {
				panic(fmt.Errorf("failed to create output directory %s", p))
			}

			path := filepath.Join(p, name)

			base := BaseJava{Namespace: ns, t: t, ts: &parsedThrift}
			data := &javaStruct{BaseJava: &base, Struct: s}

			if err := outputfile(path, structpl, TPL_STRUCT, data); err != nil {
				panic(fmt.Errorf("failed to write file %s. error: %v\n", path, err))
			}

			log.Printf("%s", path)
		}

		log.Printf("## services")

		for _, s := range t.Services {
			// filename is the service name plus 'Service'
			name := s.Name + "Service.java"

			// fix java file path
			p := filepath.Join(output, strings.Replace(ns, ".", "/", -1))
			if err := os.MkdirAll(p, 0755); err != nil {
				panic(fmt.Errorf("failed to create output directory %s", p))
			}

			path := filepath.Join(p, name)

			base := BaseJava{Namespace: ns, t: t, ts: &parsedThrift}
			data := &javaService{BaseJava: &base, Service: s}

			if err := outputfile(path, servicetpl, TPL_SERVICE, data); err != nil {
				panic(fmt.Errorf("failed to write file %s. error: %v\n", path, err))
			}

			log.Printf("%s", path)
		}
	}
}

func initemplate(n string, path string) *template.Template {
	data, err := tmpl.Asset(path)
	if err != nil {
		panic(err)
	}

	tpl, err := template.New(n).Parse(string(data))
	if err != nil {
		panic(err)
	}

	return tpl
}

func outputfile(fp string, t *template.Template, tplname string, data interface{}) error {
	file, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}

	defer file.Close()

	return t.ExecuteTemplate(file, tplname, data)
}

func init() {
	langs.Langs["java"] = &JavaGen{}
}
<|endoftext|>


<commit_before>package golatch

import (
	"testing"
)

func TestLatchErrorResponseUnmarshal(t *testing.T) {
	json := `{"error":{"code":205, "message":"Account and application already paired"}}`
	response := &LatchErrorResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchErrorResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.Err.Code != 205 || response.Err.Message != "Account and application already paired" {
		t.Errorf("LatchErrorResponse.Unmarshal() failed: json: %q , got %q", json, response)
	}
}

func TestLatchPairResponseUnmarshal(t *testing.T) {
	json := `{"data":{"accountId":"MyAccountId"}}`
	response := &LatchPairResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchPairResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.AccountId() != "MyAccountId" {
		t.Errorf("LatchPairResponse.Unmarshal() failed: json: %q , got %q", json, response)
	}
}

func TestLatchStatusResponseUnmarshal(t *testing.T) {
	json := `{"data":{"operations":{"MyApplicationID":{"status":"on", "two_factor":{"token":"g2sEXg","generated":1425209705208},"operations":{"MyOperationID":{"status":"on"}}}}}}`
	response := &LatchStatusResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchStatusResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.Status() != LATCH_STATUS_ON {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected on status: json: %q , got %q", json, response)
	} else if two_factor := response.TwoFactor(); two_factor.Token != "g2sEXg" || two_factor.Generated != 1425209705208 {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, two factor data is wrong: json: %q , got %q", json, response)
	}

	operations := response.Operations()
	if operations == nil || len(operations) == 0 {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected 1 operation, found none: json: %q , got %q", json, response)
		return
	}

	var key string
	var operation LatchOperationStatus
	for key, operation = range operations {
		break
	}
	if key != "MyOperationID" || operation.Status != LATCH_STATUS_ON {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected 1 operation with ID %q and status %q: json: %q , got %q", "MyOperationID", "on", json, response)
		return
	}
}

func TestLatchAddOperationResponse(t *testing.T) {
	json := `{"data":{"operationId":"MyOperationId"}}`
	response := &LatchAddOperationResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchAddOperationResponse.Unmarshal() failed json: %q , error %q", json, err)
	} else if response.OperationId() != "MyOperationId" {
		t.Errorf("LatchAddOperationResponse.Unmarshal() failed, expected operationId=%q : json: %q , got %q", "MyOperationId", json, response)
	}
}

func TestLatchShowOperationResponseUnmarshal(t *testing.T) {
	json := `{"data":{"operations":{"MyOperationId":{"name":"My Operation", "two_factor": "MANDATORY", "lock_on_request":"OPT_IN" ,"operations":{"MyNestedOperationID":{"name":"My Nested Operation"}}}}}}`
	response := &LatchShowOperationResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed json: %q , error %q", json, err)
	}
	operation := response.FirstOperation()
	if operation.Name != "My Operation" || operation.TwoFactor != MANDATORY || operation.LockOnRequest != OPT_IN || len(operation.Operations) == 0 {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed: expected:%q,%q,%q,%d and got %q,%q,%q,%d", "My Operation", MANDATORY, OPT_IN, 1, operation.Name, operation.TwoFactor, operation.LockOnRequest, len(operation.Operations))
	}

	var nested_operation_id string
	var nested_operation LatchOperation
	for nested_operation_id, nested_operation = range operation.Operations {
		break
	}

	if nested_operation_id != "MyNestedOperationID" || nested_operation.Name != "My Nested Operation" {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed: expected nested operation:%q with name %q, got %q with name %q", "MyNestedOperationID", "My Nested Operation", nested_operation_id, nested_operation.Name)
	}
}

func TestLatchHistoryResponseUnmarshal(t *testing.T) {
	json := `{"data":{"2Wv8UqaT6iZRQEbyG9Kv":{"status":"on","pairedOn":1428528090941,"name":"GoLatch Test","description":"","imageURL":"https://s3-eu-west-1.amazonaws.com/latch-ireland/avatar1.jpg","contactPhone":"666111222","contactEmail":"","two_factor":"DISABLED","lock_on_request":"DISABLED","operations":{"wJrfCBzZCtiZfVFwt9aJ":{"name":"Operation 1","status":"on","two_factor":"off","lock_on_request":"off","operations":{}}}},"lastSeen":1428858456785,"clientVersion":{"Android":"1.4.1"},"count":5,"history":[{"t":1428528254424,"action":"get","what":"status","value":"on","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"},{"t":1428528260264,"action":"USER_UPDATE","what":"status","value":"off","was":"on","name":"GoLatch Test","userAgent":"","ip":"127.0.0.1"},{"t":1428528264520,"action":"get","what":"status","value":"off","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"},{"t":1428528274326,"action":"USER_UPDATE","what":"status","value":"on","was":"off","name":"GoLatch Test","userAgent":"","ip":"127.0.0.1"},{"t":1428528277313,"action":"get","what":"status","value":"on","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"}]}}`
	response := &LatchHistoryResponse{AppID: "2Wv8UqaT6iZRQEbyG9Kv"}

	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed json: %q , error %q", json, err)
	}

	application := response.Application()
	operations := application.Operations
	lastSeen := response.LastSeen()
	clientVersion := response.ClientVersion()
	historyCount := response.HistoryCount()
	history := response.History()

	//Test application data
	if application.Status != "on" ||
		application.PairedOn != 1428528090941 ||
		application.Name != "GoLatch Test" ||
		application.Description != "" ||
		application.ImageURL != "https://s3-eu-west-1.amazonaws.com/latch-ireland/avatar1.jpg" ||
		application.ContactPhone != "666111222" ||
		application.ContactEmail != "" ||
		application.TwoFactor != DISABLED ||
		application.LockOnRequest != DISABLED {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect application data json: %s , object %s", json, response)
	}
	if operation := operations["wJrfCBzZCtiZfVFwt9aJ"]; len(operations) != 1 ||
		operation.Name != "Operation 1" ||
		operation.Status != "on" ||
		operation.LockOnRequest != "off" ||
		operation.TwoFactor != "off" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect operations data json: %s , object %s", json, response)
	}

	//Test LastSeen
	if lastSeen != 1428858456785 {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect lastSeen data json: %s , object %s", json, response)
	}

	//Test Client Version
	if client := clientVersion["Android"]; len(clientVersion) != 1 || client != "1.4.1" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect clientVersion data json: %s , object %s", json, response)
	}

	//Test History
	if historyCount != 5 || len(history) != 5 {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect history data json: %s , object %s", json, response)
	} else if firstHistoryEntry := history[0]; firstHistoryEntry.Time != 1428528254424 ||
		firstHistoryEntry.Action != "get" ||
		firstHistoryEntry.What != "status" ||
		firstHistoryEntry.Value != "on" ||
		firstHistoryEntry.Was != "-" ||
		firstHistoryEntry.Name != "GoLatch Test" ||
		firstHistoryEntry.UserAgent != "Go 1.1 package http" ||
		firstHistoryEntry.IP != "127.0.0.1" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect history entry data json: %s , object %s", json, response)
	}
}
<commit_msg>Fixed test.<commit_after>package golatch

import (
	"testing"
)

func TestLatchErrorResponseUnmarshal(t *testing.T) {
	json := `{"error":{"code":205, "message":"Account and application already paired"}}`
	response := &LatchErrorResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchErrorResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.Err.Code != 205 || response.Err.Message != "Account and application already paired" {
		t.Errorf("LatchErrorResponse.Unmarshal() failed: json: %q , got %q", json, response)
	}
}

func TestLatchPairResponseUnmarshal(t *testing.T) {
	json := `{"data":{"accountId":"MyAccountId"}}`
	response := &LatchPairResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchPairResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.AccountId() != "MyAccountId" {
		t.Errorf("LatchPairResponse.Unmarshal() failed: json: %q , got %q", json, response)
	}
}

func TestLatchStatusResponseUnmarshal(t *testing.T) {
	json := `{"data":{"operations":{"MyApplicationID":{"status":"on", "two_factor":{"token":"g2sEXg","generated":1425209705208},"operations":{"MyOperationID":{"status":"on"}}}}}}`
	response := &LatchStatusResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchStatusResponse.Unmarshal() failed json: json: %q , error %q", json, err)
	} else if response.Status() != LATCH_STATUS_ON {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected on status: json: %q , got %q", json, response)
	} else if two_factor := response.TwoFactor(); two_factor.Token != "g2sEXg" || two_factor.Generated != 1425209705208 {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, two factor data is wrong: json: %q , got %q", json, response)
	}

	operations := response.Operations()
	if operations == nil || len(operations) == 0 {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected 1 operation, found none: json: %q , got %q", json, response)
		return
	}

	var key string
	var operation LatchOperationStatus
	for key, operation = range operations {
		break
	}
	if key != "MyOperationID" || operation.Status != LATCH_STATUS_ON {
		t.Errorf("LatchStatusResponse.Unmarshal() failed, expected 1 operation with ID %q and status %q: json: %q , got %q", "MyOperationID", "on", json, response)
		return
	}
}

func TestLatchAddOperationResponse(t *testing.T) {
	json := `{"data":{"operationId":"MyOperationId"}}`
	response := &LatchAddOperationResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchAddOperationResponse.Unmarshal() failed json: %q , error %q", json, err)
	} else if response.OperationId() != "MyOperationId" {
		t.Errorf("LatchAddOperationResponse.Unmarshal() failed, expected operationId=%q : json: %q , got %q", "MyOperationId", json, response)
	}
}

func TestLatchShowOperationResponseUnmarshal(t *testing.T) {
	json := `{"data":{"operations":{"MyOperationId":{"name":"My Operation", "two_factor": "MANDATORY", "lock_on_request":"OPT_IN" ,"operations":{"MyNestedOperationID":{"name":"My Nested Operation"}}}}}}`
	response := &LatchShowOperationResponse{}
	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed json: %q , error %q", json, err)
	}
	id, operation := response.FirstOperation()
	if id != "MyOperationId" || operation.Name != "My Operation" || operation.TwoFactor != MANDATORY || operation.LockOnRequest != OPT_IN || len(operation.Operations) == 0 {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed: expected:%q,%q,%q,%d and got %q,%q,%q,%d", "My Operation", MANDATORY, OPT_IN, 1, operation.Name, operation.TwoFactor, operation.LockOnRequest, len(operation.Operations))
	}

	var nested_operation_id string
	var nested_operation LatchOperation
	for nested_operation_id, nested_operation = range operation.Operations {
		break
	}

	if nested_operation_id != "MyNestedOperationID" || nested_operation.Name != "My Nested Operation" {
		t.Errorf("LatchShowOperationResponse.Unmarshal() failed: expected nested operation:%q with name %q, got %q with name %q", "MyNestedOperationID", "My Nested Operation", nested_operation_id, nested_operation.Name)
	}
}

func TestLatchHistoryResponseUnmarshal(t *testing.T) {
	json := `{"data":{"2Wv8UqaT6iZRQEbyG9Kv":{"status":"on","pairedOn":1428528090941,"name":"GoLatch Test","description":"","imageURL":"https://s3-eu-west-1.amazonaws.com/latch-ireland/avatar1.jpg","contactPhone":"666111222","contactEmail":"","two_factor":"DISABLED","lock_on_request":"DISABLED","operations":{"wJrfCBzZCtiZfVFwt9aJ":{"name":"Operation 1","status":"on","two_factor":"off","lock_on_request":"off","operations":{}}}},"lastSeen":1428858456785,"clientVersion":{"Android":"1.4.1"},"count":5,"history":[{"t":1428528254424,"action":"get","what":"status","value":"on","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"},{"t":1428528260264,"action":"USER_UPDATE","what":"status","value":"off","was":"on","name":"GoLatch Test","userAgent":"","ip":"127.0.0.1"},{"t":1428528264520,"action":"get","what":"status","value":"off","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"},{"t":1428528274326,"action":"USER_UPDATE","what":"status","value":"on","was":"off","name":"GoLatch Test","userAgent":"","ip":"127.0.0.1"},{"t":1428528277313,"action":"get","what":"status","value":"on","was":"-","name":"GoLatch Test","userAgent":"Go 1.1 package http","ip":"127.0.0.1"}]}}`
	response := &LatchHistoryResponse{AppID: "2Wv8UqaT6iZRQEbyG9Kv"}

	err := response.Unmarshal(json)

	if err != nil {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed json: %q , error %q", json, err)
	}

	application := response.Application()
	operations := application.Operations
	lastSeen := response.LastSeen()
	clientVersion := response.ClientVersion()
	historyCount := response.HistoryCount()
	history := response.History()

	//Test application data
	if application.Status != "on" ||
		application.PairedOn != 1428528090941 ||
		application.Name != "GoLatch Test" ||
		application.Description != "" ||
		application.ImageURL != "https://s3-eu-west-1.amazonaws.com/latch-ireland/avatar1.jpg" ||
		application.ContactPhone != "666111222" ||
		application.ContactEmail != "" ||
		application.TwoFactor != DISABLED ||
		application.LockOnRequest != DISABLED {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect application data json: %s , object %s", json, response)
	}
	if operation := operations["wJrfCBzZCtiZfVFwt9aJ"]; len(operations) != 1 ||
		operation.Name != "Operation 1" ||
		operation.Status != "on" ||
		operation.LockOnRequest != "off" ||
		operation.TwoFactor != "off" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect operations data json: %s , object %s", json, response)
	}

	//Test LastSeen
	if lastSeen != 1428858456785 {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect lastSeen data json: %s , object %s", json, response)
	}

	//Test Client Version
	if client := clientVersion["Android"]; len(clientVersion) != 1 || client != "1.4.1" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect clientVersion data json: %s , object %s", json, response)
	}

	//Test History
	if historyCount != 5 || len(history) != 5 {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect history data json: %s , object %s", json, response)
	} else if firstHistoryEntry := history[0]; firstHistoryEntry.Time != 1428528254424 ||
		firstHistoryEntry.Action != "get" ||
		firstHistoryEntry.What != "status" ||
		firstHistoryEntry.Value != "on" ||
		firstHistoryEntry.Was != "-" ||
		firstHistoryEntry.Name != "GoLatch Test" ||
		firstHistoryEntry.UserAgent != "Go 1.1 package http" ||
		firstHistoryEntry.IP != "127.0.0.1" {
		t.Errorf("LatchHistoryResponse.Unmarshal() failed, incorrect history entry data json: %s , object %s", json, response)
	}
}
<|endoftext|>


<commit_before>package weavedns

import (
	"fmt"
	"log"
	"net"
	"time"
)

func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err error) {
	iface, err = findInterface(ifaceName)
	if err == nil || wait == 0 {
		return
	}
	log.Println("Waiting for interface", ifaceName, "to come up")
	for ; err != nil && wait > 0; wait -= 1 {
		time.Sleep(1 * time.Second)
		iface, err = findInterface(ifaceName)
	}
	if err == nil {
		log.Println("Interface", ifaceName, "is up")
	}
	return
}

func findInterface(ifaceName string) (iface *net.Interface, err error) {
	iface, err = net.InterfaceByName(ifaceName)
	if err != nil {
		return iface, fmt.Errorf("Unable to find interface %s", ifaceName)
	}
	if 0 == (net.FlagUp & iface.Flags) {
		return iface, fmt.Errorf("Interface %s is not up", ifaceName)
	}
	return
}
<commit_msg>Replaced another log.Printf<commit_after>package weavedns

import (
	"fmt"
	"net"
	"time"
)

func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err error) {
	iface, err = findInterface(ifaceName)
	if err == nil || wait == 0 {
		return
	}
	Info.Println("Waiting for interface", ifaceName, "to come up")
	for ; err != nil && wait > 0; wait -= 1 {
		time.Sleep(1 * time.Second)
		iface, err = findInterface(ifaceName)
	}
	if err == nil {
		Info.Println("Interface", ifaceName, "is up")
	}
	return
}

func findInterface(ifaceName string) (iface *net.Interface, err error) {
	iface, err = net.InterfaceByName(ifaceName)
	if err != nil {
		return iface, fmt.Errorf("Unable to find interface %s", ifaceName)
	}
	if 0 == (net.FlagUp & iface.Flags) {
		return iface, fmt.Errorf("Interface %s is not up", ifaceName)
	}
	return
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"log"
	"time"

	"golang.org/x/net/context"
	"gopkg.in/square/go-jose.v2"

	"github.com/coreos/dex/storage"
)

// rotationStrategy describes a strategy for generating cryptographic keys, how
// often to rotate them, and how long they can validate signatures after rotation.
type rotationStrategy struct {
	// Time between rotations.
	period time.Duration

	// After being rotated how long can a key validate signatues?
	verifyFor time.Duration

	// Keys are always RSA keys. Though cryptopasta recommends ECDSA keys, not every
	// client may support these (e.g. github.com/coreos/go-oidc/oidc).
	key func() (*rsa.PrivateKey, error)
}

// staticRotationStrategy returns a strategy which never rotates keys.
func staticRotationStrategy(key *rsa.PrivateKey) rotationStrategy {
	return rotationStrategy{
		// Setting these values to 100 years is easier than having a flag indicating no rotation.
		period:    time.Hour * 8760 * 100,
		verifyFor: time.Hour * 8760 * 100,
		key:       func() (*rsa.PrivateKey, error) { return key, nil },
	}
}

// defaultRotationStrategy returns a strategy which rotates keys every provided period,
// holding onto the public parts for some specified amount of time.
func defaultRotationStrategy(rotationPeriod, verifyFor time.Duration) rotationStrategy {
	return rotationStrategy{
		period:    rotationPeriod,
		verifyFor: verifyFor,
		key: func() (*rsa.PrivateKey, error) {
			return rsa.GenerateKey(rand.Reader, 2048)
		},
	}
}

type keyRotater struct {
	storage.Storage

	strategy rotationStrategy
	now      func() time.Time
}

// startKeyRotation begins key rotation in a new goroutine, closing once the context is canceled.
//
// The method blocks until after the first attempt to rotate keys has completed. That way
// healthy storages will return from this call with valid keys.
func startKeyRotation(ctx context.Context, s storage.Storage, strategy rotationStrategy, now func() time.Time) {
	rotater := keyRotater{s, strategy, now}

	// Try to rotate immediately so properly configured storages will have keys.
	if err := rotater.rotate(); err != nil {
		log.Printf("failed to rotate keys: %v", err)
	}

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(strategy.period):
				if err := rotater.rotate(); err != nil {
					log.Printf("failed to rotate keys: %v", err)
				}
			}
		}
	}()
	return
}

func (k keyRotater) rotate() error {
	keys, err := k.GetKeys()
	if err != nil && err != storage.ErrNotFound {
		return fmt.Errorf("get keys: %v", err)
	}
	if k.now().Before(keys.NextRotation) {
		return nil
	}
	log.Println("keys expired, rotating")

	// Generate the key outside of a storage transaction.
	key, err := k.strategy.key()
	if err != nil {
		return fmt.Errorf("generate key: %v", err)
	}
	b := make([]byte, 20)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		panic(err)
	}
	keyID := hex.EncodeToString(b)
	priv := &jose.JSONWebKey{
		Key:       key,
		KeyID:     keyID,
		Algorithm: "RS256",
		Use:       "sig",
	}
	pub := &jose.JSONWebKey{
		Key:       key.Public(),
		KeyID:     keyID,
		Algorithm: "RS256",
		Use:       "sig",
	}

	var nextRotation time.Time
	err = k.Storage.UpdateKeys(func(keys storage.Keys) (storage.Keys, error) {
		tNow := k.now()
		if tNow.Before(keys.NextRotation) {
			return storage.Keys{}, errors.New("keys already rotated")
		}

		// Remove expired verification keys.
		i := 0
		for _, key := range keys.VerificationKeys {
			if !key.Expiry.After(tNow) {
				keys.VerificationKeys[i] = key
				i++
			}
		}
		keys.VerificationKeys = keys.VerificationKeys[:i]

		if keys.SigningKeyPub != nil {
			// Move current signing key to a verification only key.
			verificationKey := storage.VerificationKey{
				PublicKey: keys.SigningKeyPub,
				Expiry:    tNow.Add(k.strategy.verifyFor),
			}
			keys.VerificationKeys = append(keys.VerificationKeys, verificationKey)
		}

		nextRotation = k.now().Add(k.strategy.period)
		keys.SigningKey = priv
		keys.SigningKeyPub = pub
		keys.NextRotation = nextRotation
		return keys, nil
	})
	if err != nil {
		return err
	}
	log.Printf("keys rotated, next rotation: %s", nextRotation)
	return nil
}
<commit_msg>server: fix key rotation polling<commit_after>package server

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"log"
	"time"

	"golang.org/x/net/context"
	"gopkg.in/square/go-jose.v2"

	"github.com/coreos/dex/storage"
)

// rotationStrategy describes a strategy for generating cryptographic keys, how
// often to rotate them, and how long they can validate signatures after rotation.
type rotationStrategy struct {
	// Time between rotations.
	period time.Duration

	// After being rotated how long can a key validate signatues?
	verifyFor time.Duration

	// Keys are always RSA keys. Though cryptopasta recommends ECDSA keys, not every
	// client may support these (e.g. github.com/coreos/go-oidc/oidc).
	key func() (*rsa.PrivateKey, error)
}

// staticRotationStrategy returns a strategy which never rotates keys.
func staticRotationStrategy(key *rsa.PrivateKey) rotationStrategy {
	return rotationStrategy{
		// Setting these values to 100 years is easier than having a flag indicating no rotation.
		period:    time.Hour * 8760 * 100,
		verifyFor: time.Hour * 8760 * 100,
		key:       func() (*rsa.PrivateKey, error) { return key, nil },
	}
}

// defaultRotationStrategy returns a strategy which rotates keys every provided period,
// holding onto the public parts for some specified amount of time.
func defaultRotationStrategy(rotationPeriod, verifyFor time.Duration) rotationStrategy {
	return rotationStrategy{
		period:    rotationPeriod,
		verifyFor: verifyFor,
		key: func() (*rsa.PrivateKey, error) {
			return rsa.GenerateKey(rand.Reader, 2048)
		},
	}
}

type keyRotater struct {
	storage.Storage

	strategy rotationStrategy
	now      func() time.Time
}

// startKeyRotation begins key rotation in a new goroutine, closing once the context is canceled.
//
// The method blocks until after the first attempt to rotate keys has completed. That way
// healthy storages will return from this call with valid keys.
func startKeyRotation(ctx context.Context, s storage.Storage, strategy rotationStrategy, now func() time.Time) {
	rotater := keyRotater{s, strategy, now}

	// Try to rotate immediately so properly configured storages will have keys.
	if err := rotater.rotate(); err != nil {
		log.Printf("failed to rotate keys: %v", err)
	}

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Second * 30):
				if err := rotater.rotate(); err != nil {
					log.Printf("failed to rotate keys: %v", err)
				}
			}
		}
	}()
	return
}

func (k keyRotater) rotate() error {
	keys, err := k.GetKeys()
	if err != nil && err != storage.ErrNotFound {
		return fmt.Errorf("get keys: %v", err)
	}
	if k.now().Before(keys.NextRotation) {
		return nil
	}
	log.Println("keys expired, rotating")

	// Generate the key outside of a storage transaction.
	key, err := k.strategy.key()
	if err != nil {
		return fmt.Errorf("generate key: %v", err)
	}
	b := make([]byte, 20)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		panic(err)
	}
	keyID := hex.EncodeToString(b)
	priv := &jose.JSONWebKey{
		Key:       key,
		KeyID:     keyID,
		Algorithm: "RS256",
		Use:       "sig",
	}
	pub := &jose.JSONWebKey{
		Key:       key.Public(),
		KeyID:     keyID,
		Algorithm: "RS256",
		Use:       "sig",
	}

	var nextRotation time.Time
	err = k.Storage.UpdateKeys(func(keys storage.Keys) (storage.Keys, error) {
		tNow := k.now()
		if tNow.Before(keys.NextRotation) {
			return storage.Keys{}, errors.New("keys already rotated")
		}

		// Remove expired verification keys.
		i := 0
		for _, key := range keys.VerificationKeys {
			if !key.Expiry.After(tNow) {
				keys.VerificationKeys[i] = key
				i++
			}
		}
		keys.VerificationKeys = keys.VerificationKeys[:i]

		if keys.SigningKeyPub != nil {
			// Move current signing key to a verification only key.
			verificationKey := storage.VerificationKey{
				PublicKey: keys.SigningKeyPub,
				Expiry:    tNow.Add(k.strategy.verifyFor),
			}
			keys.VerificationKeys = append(keys.VerificationKeys, verificationKey)
		}

		nextRotation = k.now().Add(k.strategy.period)
		keys.SigningKey = priv
		keys.SigningKeyPub = pub
		keys.NextRotation = nextRotation
		return keys, nil
	})
	if err != nil {
		return err
	}
	log.Printf("keys rotated, next rotation: %s", nextRotation)
	return nil
}
<|endoftext|>


<commit_before>package filer2

import (
	"fmt"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/notification"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
)

func (f *Filer) NotifyUpdateEvent(oldEntry, newEntry *Entry, deleteChunks bool) {
	var fullpath string
	if oldEntry != nil {
		fullpath = string(oldEntry.FullPath)
	} else if newEntry != nil {
		fullpath = string(newEntry.FullPath)
	} else {
		return
	}

	// println("fullpath:", fullpath)

	if strings.HasPrefix(fullpath, "/.meta") {
		return
	}

	newParentPath := ""
	if newEntry != nil {
		newParentPath, _ = newEntry.FullPath.DirAndName()
	}
	eventNotification := &filer_pb.EventNotification{
		OldEntry:      oldEntry.ToProtoEntry(),
		NewEntry:      newEntry.ToProtoEntry(),
		DeleteChunks:  deleteChunks,
		NewParentPath: newParentPath,
	}

	if notification.Queue != nil {
		glog.V(3).Infof("notifying entry update %v", fullpath)
		notification.Queue.SendMessage(fullpath, eventNotification)
	}

	if false {
		f.logMetaEvent(time.Now(), fullpath, eventNotification)
	}

}

func (f *Filer) logMetaEvent(ts time.Time, fullpath string, eventNotification *filer_pb.EventNotification) {

	dir, _ := util.FullPath(fullpath).DirAndName()

	event := &filer_pb.FullEventNotification{
		Directory:         dir,
		EventNotification: eventNotification,
	}
	data, err := proto.Marshal(event)
	if err != nil {
		glog.Errorf("failed to marshal filer_pb.FullEventNotification %+v: %v", event, err)
		return
	}

	f.metaLogBuffer.AddToBuffer(ts, []byte(dir), data)

}

func (f *Filer) logFlushFunc(startTime, stopTime time.Time, buf []byte) {
	targetFile := fmt.Sprintf("/.meta/log/%04d/%02d/%02d/%02d/%02d/%02d.%09d.log",
		startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(),
		startTime.Second(), startTime.Nanosecond())

	if err := f.appendToFile(targetFile, buf); err != nil {
		glog.V(0).Infof("log write failed %s: %v", targetFile, err)
	}
}

func (f *Filer) ReadLogBuffer(lastReadTime time.Time, eachEventFn func(fullpath string, eventNotification *filer_pb.EventNotification) error) (newLastReadTime time.Time, err error) {

	var buf []byte
	newLastReadTime, buf = f.metaLogBuffer.ReadFromBuffer(lastReadTime)
	var processedTs int64

	for pos := 0; pos+4 < len(buf); {

		size := util.BytesToUint32(buf[pos : pos+4])
		entryData := buf[pos+4 : pos+4+int(size)]

		logEntry := &filer_pb.LogEntry{}
		err = proto.Unmarshal(entryData, logEntry)
		if err != nil {
			glog.Errorf("unexpected unmarshal filer_pb.LogEntry: %v", err)
			return lastReadTime, fmt.Errorf("unexpected unmarshal filer_pb.LogEntry: %v", err)
		}

		event := &filer_pb.FullEventNotification{}
		err = proto.Unmarshal(logEntry.Data, event)
		if err != nil {
			glog.Errorf("unexpected unmarshal filer_pb.FullEventNotification: %v", err)
			return lastReadTime, fmt.Errorf("unexpected unmarshal filer_pb.FullEventNotification: %v", err)
		}

		err = eachEventFn(event.Directory, event.EventNotification)

		processedTs = logEntry.TsNs

		if err != nil {
			newLastReadTime = time.Unix(0, processedTs)
			return
		}

		pos += 4 + int(size)

	}

	newLastReadTime = time.Unix(0, processedTs)
	return

}
<commit_msg>still log, but not persisting the changes<commit_after>package filer2

import (
	"fmt"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/notification"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
)

func (f *Filer) NotifyUpdateEvent(oldEntry, newEntry *Entry, deleteChunks bool) {
	var fullpath string
	if oldEntry != nil {
		fullpath = string(oldEntry.FullPath)
	} else if newEntry != nil {
		fullpath = string(newEntry.FullPath)
	} else {
		return
	}

	// println("fullpath:", fullpath)

	if strings.HasPrefix(fullpath, "/.meta") {
		return
	}

	newParentPath := ""
	if newEntry != nil {
		newParentPath, _ = newEntry.FullPath.DirAndName()
	}
	eventNotification := &filer_pb.EventNotification{
		OldEntry:      oldEntry.ToProtoEntry(),
		NewEntry:      newEntry.ToProtoEntry(),
		DeleteChunks:  deleteChunks,
		NewParentPath: newParentPath,
	}

	if notification.Queue != nil {
		glog.V(3).Infof("notifying entry update %v", fullpath)
		notification.Queue.SendMessage(fullpath, eventNotification)
	}

	f.logMetaEvent(time.Now(), fullpath, eventNotification)

}

func (f *Filer) logMetaEvent(ts time.Time, fullpath string, eventNotification *filer_pb.EventNotification) {

	dir, _ := util.FullPath(fullpath).DirAndName()

	event := &filer_pb.FullEventNotification{
		Directory:         dir,
		EventNotification: eventNotification,
	}
	data, err := proto.Marshal(event)
	if err != nil {
		glog.Errorf("failed to marshal filer_pb.FullEventNotification %+v: %v", event, err)
		return
	}

	f.metaLogBuffer.AddToBuffer(ts, []byte(dir), data)

}

func (f *Filer) logFlushFunc(startTime, stopTime time.Time, buf []byte) {

	return

	targetFile := fmt.Sprintf("/.meta/log/%04d/%02d/%02d/%02d/%02d/%02d.%09d.log",
		startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(),
		startTime.Second(), startTime.Nanosecond())

	if err := f.appendToFile(targetFile, buf); err != nil {
		glog.V(0).Infof("log write failed %s: %v", targetFile, err)
	}
}

func (f *Filer) ReadLogBuffer(lastReadTime time.Time, eachEventFn func(fullpath string, eventNotification *filer_pb.EventNotification) error) (newLastReadTime time.Time, err error) {

	var buf []byte
	newLastReadTime, buf = f.metaLogBuffer.ReadFromBuffer(lastReadTime)
	var processedTs int64

	for pos := 0; pos+4 < len(buf); {

		size := util.BytesToUint32(buf[pos : pos+4])
		entryData := buf[pos+4 : pos+4+int(size)]

		logEntry := &filer_pb.LogEntry{}
		err = proto.Unmarshal(entryData, logEntry)
		if err != nil {
			glog.Errorf("unexpected unmarshal filer_pb.LogEntry: %v", err)
			return lastReadTime, fmt.Errorf("unexpected unmarshal filer_pb.LogEntry: %v", err)
		}

		event := &filer_pb.FullEventNotification{}
		err = proto.Unmarshal(logEntry.Data, event)
		if err != nil {
			glog.Errorf("unexpected unmarshal filer_pb.FullEventNotification: %v", err)
			return lastReadTime, fmt.Errorf("unexpected unmarshal filer_pb.FullEventNotification: %v", err)
		}

		err = eachEventFn(event.Directory, event.EventNotification)

		processedTs = logEntry.TsNs

		if err != nil {
			newLastReadTime = time.Unix(0, processedTs)
			return
		}

		pos += 4 + int(size)

	}

	newLastReadTime = time.Unix(0, processedTs)
	return

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/getgauge/common"
	"os"
	"path"
	"runtime"
	"sort"
	"strings"
)

type installDescription struct {
	Name        string
	Description string
	Versions    []versionInstallDescription
}

type versionInstallDescription struct {
	Version             string
	GaugeVersionSupport versionSupport
	Install             platformSpecificCommand
	DownloadUrls        downloadUrls
}

type downloadUrls struct {
	X86 platformSpecificUrl
	X64 platformSpecificUrl
}

type platformSpecificCommand struct {
	Windows []string
	Linux   []string
	Darwin  []string
}

type platformSpecificUrl struct {
	Windows string
	Linux   string
	Darwin  string
}

type versionSupport struct {
	Minimum string
	Maximum string
}

func installPlugin(pluginName, version string) error {
	installDescription, err := getInstallDescription(pluginName)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not find install description for Plugin: '%s' %s. : %s \n", pluginName, version, err))
	}
	if err := installPluginWithDescription(installDescription, version); err != nil {
		return err
	}

	return nil
}

func installPluginWithDescription(installDescription *installDescription, version string) error {
	var versionInstallDescription *versionInstallDescription
	var err error
	if version != "" {
		versionInstallDescription, err = installDescription.getVersion(version)
		if err != nil {
			return err
		}
		if compatibilityError := checkCompatiblity(currentGaugeVersion, &versionInstallDescription.GaugeVersionSupport); compatibilityError != nil {
			return errors.New(fmt.Sprintf("Plugin Version %s is not supported for gauge %s : %s", installDescription.Name, versionInstallDescription.Version, versionInstallDescription.Version, currentGaugeVersion.String(), compatibilityError.Error()))
		}
	} else {
		versionInstallDescription, err = installDescription.getLatestCompatibleVersionTo(currentGaugeVersion)
		if err != nil {
			return errors.New(fmt.Sprintf("Could not find compatible version for plugin %s. : %s", installDescription.Name, err))
		}
	}
	return installPluginVersion(installDescription, versionInstallDescription)
}

func installPluginVersion(installDesc *installDescription, versionInstallDescription *versionInstallDescription) error {
	if common.IsPluginInstalled(installDesc.Name, versionInstallDescription.Version) {
		return errors.New(fmt.Sprintf("Plugin %s %s is already installed.", installDesc.Name, versionInstallDescription.Version))
	}

	fmt.Printf("Installing Plugin => %s %s\n", installDesc.Name, versionInstallDescription.Version)
	pluginZip, err := downloadPluginZip(versionInstallDescription.DownloadUrls)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not download plugin zip: %s.", err))
	}
	unzippedPluginDir, err := common.UnzipArchive(pluginZip)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to Unzip plugin-zip file %s.", err))
	}
	fmt.Printf("Plugin unzipped to => %s\n", unzippedPluginDir)
	if err := runInstallCommands(versionInstallDescription.Install, unzippedPluginDir); err != nil {
		return errors.New(fmt.Sprintf("Failed to Run install command. %s.", err))
	}
	return copyPluginFilesToGauge(installDesc, versionInstallDescription, unzippedPluginDir)
}

func runInstallCommands(installCommands platformSpecificCommand, workingDir string) error {
	command := []string{}
	switch runtime.GOOS {
	case "windows":
		command = installCommands.Windows
		break
	case "darwin":
		command = installCommands.Darwin
		break
	default:
		command = installCommands.Linux
		break
	}

	if len(command) == 0 {
		return errors.New(fmt.Sprintf("Platform not supported: %s.", runtime.GOOS))
	}

	fmt.Printf("Running plugin install command => %s\n", command)
	cmd, err := common.ExecuteCommand(command, workingDir, os.Stdout, os.Stderr)

	if err != nil {
		return err
	}

	return cmd.Wait()
}

func copyPluginFilesToGauge(installDesc *installDescription, versionInstallDesc *versionInstallDescription, pluginContents string) error {
	pluginsDir, err := common.GetPrimaryPluginsInstallDir()
	if err != nil {
		return err
	}
	versionedPluginDir := path.Join(pluginsDir, installDesc.Name, versionInstallDesc.Version)
	if common.DirExists(versionedPluginDir) {
		return errors.New(fmt.Sprintf("Plugin %s %s already installed at %s", installDesc.Name, versionInstallDesc.Version, versionedPluginDir))
	}
	return common.MirrorDir(pluginContents, versionedPluginDir)

}

func downloadPluginZip(downloadUrls downloadUrls) (string, error) {
	var platformLinks *platformSpecificUrl
	if strings.Contains(runtime.GOARCH, "64") {
		platformLinks = &downloadUrls.X64
	} else {
		platformLinks = &downloadUrls.X86
	}

	var downloadLink string
	switch runtime.GOOS {
	case "windows":
		downloadLink = platformLinks.Windows
		break
	case "darwin":
		downloadLink = platformLinks.Darwin
		break
	default:
		downloadLink = platformLinks.Linux
		break
	}
	if downloadLink == "" {
		return "", errors.New("Plugin download URL not available for current platform.")
	}
	downloadedFile, err := common.DownloadToTempDir(downloadLink)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Could not download File %s: %s", downloadLink, err.Error()))
	}
	return downloadedFile, err
}

func getInstallDescription(plugin string) (*installDescription, error) {
	installJson, err := getPluginInstallJson(plugin)
	if err != nil {
		return nil, err
	}
	InstallJsonContents, readErr := common.ReadFileContents(installJson)
	if readErr != nil {
		return nil, readErr
	}
	installDescription := &installDescription{}
	if err = json.Unmarshal([]byte(InstallJsonContents), installDescription); err != nil {
		return nil, err
	}
	return installDescription, nil
}

func getPluginInstallJson(plugin string) (string, error) {
	versionInstallDescriptionJsonFile := plugin + "-install.json"
	versionInstallDescriptionJsonUrl, err := constructPluginInstallJsonUrl(plugin)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Could not construct plugin install json file URL. %s", err))
	}
	downloadedFile, downloadErr := common.DownloadToTempDir(versionInstallDescriptionJsonUrl)
	if downloadErr != nil {
		return "", errors.New(fmt.Sprintf("Could not find %s file. Check install name and version. %s", versionInstallDescriptionJsonFile, downloadErr.Error()))
	}
	return downloadedFile, nil
}

func constructPluginInstallJsonUrl(plugin string) (string, error) {
	installJsonFile := plugin + "-install.json"
	repoUrl, err := getGaugeRepositoryUrl()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s/%s", repoUrl, installJsonFile), nil
}

func getGaugeRepositoryUrl() (string, error) {
	config, err := common.GetGaugeConfiguration()
	if err != nil {
		return "", err
	}
	return config[common.GaugeRepositoryUrl], nil
}

func (installDesc *installDescription) getVersion(version string) (*versionInstallDescription, error) {
	for _, versionInstallDescription := range installDesc.Versions {
		if versionInstallDescription.Version == version {
			return &versionInstallDescription, nil
		}
	}
	return nil, errors.New("Could not find install description for Version " + version)
}

func (installDesc *installDescription) getLatestCompatibleVersionTo(version *version) (*versionInstallDescription, error) {
	installDesc.sortVersionInstallDescriptions()
	for _, versionInstallDesc := range installDesc.Versions {
		if err := checkCompatiblity(version, &versionInstallDesc.GaugeVersionSupport); err == nil {
			return &versionInstallDesc, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("Compatible version to %s not found", version))

}

func (installDescription *installDescription) sortVersionInstallDescriptions() {
	sort.Sort(ByDecreasingVersion(installDescription.Versions))
}

func checkCompatiblity(version *version, versionSupport *versionSupport) error {
	minSupportVersion, err := parseVersion(versionSupport.Minimum)
	if err != nil {
		return errors.New(fmt.Sprintf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err))
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := parseVersion(versionSupport.Maximum)
		if err != nil {
			return errors.New(fmt.Sprintf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err))
		}
		if version.isBetween(minSupportVersion, maxSupportVersion) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("Version %s is not between %s and %s", version, minSupportVersion, maxSupportVersion))
		}
	}

	if minSupportVersion.isLesserThanEqualTo(version) {
		return nil
	}
	return errors.New(fmt.Sprintf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, version))
}

type ByDecreasingVersion []versionInstallDescription

func (a ByDecreasingVersion) Len() int      { return len(a) }
func (a ByDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDecreasingVersion) Less(i, j int) bool {
	version1, _ := parseVersion(a[i].Version)
	version2, _ := parseVersion(a[j].Version)
	return version1.isGreaterThan(version2)
}
<commit_msg>Platform not supported message edited.<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/getgauge/common"
	"os"
	"path"
	"runtime"
	"sort"
	"strings"
)

type installDescription struct {
	Name        string
	Description string
	Versions    []versionInstallDescription
}

type versionInstallDescription struct {
	Version             string
	GaugeVersionSupport versionSupport
	Install             platformSpecificCommand
	DownloadUrls        downloadUrls
}

type downloadUrls struct {
	X86 platformSpecificUrl
	X64 platformSpecificUrl
}

type platformSpecificCommand struct {
	Windows []string
	Linux   []string
	Darwin  []string
}

type platformSpecificUrl struct {
	Windows string
	Linux   string
	Darwin  string
}

type versionSupport struct {
	Minimum string
	Maximum string
}

func installPlugin(pluginName, version string) error {
	installDescription, err := getInstallDescription(pluginName)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not find install description for Plugin: '%s' %s. : %s \n", pluginName, version, err))
	}
	if err := installPluginWithDescription(installDescription, version); err != nil {
		return err
	}

	return nil
}

func installPluginWithDescription(installDescription *installDescription, version string) error {
	var versionInstallDescription *versionInstallDescription
	var err error
	if version != "" {
		versionInstallDescription, err = installDescription.getVersion(version)
		if err != nil {
			return err
		}
		if compatibilityError := checkCompatiblity(currentGaugeVersion, &versionInstallDescription.GaugeVersionSupport); compatibilityError != nil {
			return errors.New(fmt.Sprintf("Plugin Version %s is not supported for gauge %s : %s", installDescription.Name, versionInstallDescription.Version, versionInstallDescription.Version, currentGaugeVersion.String(), compatibilityError.Error()))
		}
	} else {
		versionInstallDescription, err = installDescription.getLatestCompatibleVersionTo(currentGaugeVersion)
		if err != nil {
			return errors.New(fmt.Sprintf("Could not find compatible version for plugin %s. : %s", installDescription.Name, err))
		}
	}
	return installPluginVersion(installDescription, versionInstallDescription)
}

func installPluginVersion(installDesc *installDescription, versionInstallDescription *versionInstallDescription) error {
	if common.IsPluginInstalled(installDesc.Name, versionInstallDescription.Version) {
		return errors.New(fmt.Sprintf("Plugin %s %s is already installed.", installDesc.Name, versionInstallDescription.Version))
	}

	fmt.Printf("Installing Plugin => %s %s\n", installDesc.Name, versionInstallDescription.Version)
	pluginZip, err := downloadPluginZip(versionInstallDescription.DownloadUrls)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not download plugin zip: %s.", err))
	}
	unzippedPluginDir, err := common.UnzipArchive(pluginZip)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to Unzip plugin-zip file %s.", err))
	}
	fmt.Printf("Plugin unzipped to => %s\n", unzippedPluginDir)
	if err := runInstallCommands(versionInstallDescription.Install, unzippedPluginDir); err != nil {
		return errors.New(fmt.Sprintf("Failed to Run install command. %s.", err))
	}
	return copyPluginFilesToGauge(installDesc, versionInstallDescription, unzippedPluginDir)
}

func runInstallCommands(installCommands platformSpecificCommand, workingDir string) error {
	command := []string{}
	switch runtime.GOOS {
	case "windows":
		command = installCommands.Windows
		break
	case "darwin":
		command = installCommands.Darwin
		break
	default:
		command = installCommands.Linux
		break
	}

	if len(command) == 0 {
		return nil
	}

	fmt.Printf("Running plugin install command => %s\n", command)
	cmd, err := common.ExecuteCommand(command, workingDir, os.Stdout, os.Stderr)

	if err != nil {
		return err
	}

	return cmd.Wait()
}

func copyPluginFilesToGauge(installDesc *installDescription, versionInstallDesc *versionInstallDescription, pluginContents string) error {
	pluginsDir, err := common.GetPrimaryPluginsInstallDir()
	if err != nil {
		return err
	}
	versionedPluginDir := path.Join(pluginsDir, installDesc.Name, versionInstallDesc.Version)
	if common.DirExists(versionedPluginDir) {
		return errors.New(fmt.Sprintf("Plugin %s %s already installed at %s", installDesc.Name, versionInstallDesc.Version, versionedPluginDir))
	}
	return common.MirrorDir(pluginContents, versionedPluginDir)

}

func downloadPluginZip(downloadUrls downloadUrls) (string, error) {
	var platformLinks *platformSpecificUrl
	if strings.Contains(runtime.GOARCH, "64") {
		platformLinks = &downloadUrls.X64
	} else {
		platformLinks = &downloadUrls.X86
	}

	var downloadLink string
	switch runtime.GOOS {
	case "windows":
		downloadLink = platformLinks.Windows
		break
	case "darwin":
		downloadLink = platformLinks.Darwin
		break
	default:
		downloadLink = platformLinks.Linux
		break
	}
	if downloadLink == "" {
		return "", errors.New("Platform not supported for %s. Download URL not specified.")
	}
	downloadedFile, err := common.DownloadToTempDir(downloadLink)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Could not download File %s: %s", downloadLink, err.Error()))
	}
	return downloadedFile, err
}

func getInstallDescription(plugin string) (*installDescription, error) {
	installJson, err := getPluginInstallJson(plugin)
	if err != nil {
		return nil, err
	}
	InstallJsonContents, readErr := common.ReadFileContents(installJson)
	if readErr != nil {
		return nil, readErr
	}
	installDescription := &installDescription{}
	if err = json.Unmarshal([]byte(InstallJsonContents), installDescription); err != nil {
		return nil, err
	}
	return installDescription, nil
}

func getPluginInstallJson(plugin string) (string, error) {
	versionInstallDescriptionJsonFile := plugin + "-install.json"
	versionInstallDescriptionJsonUrl, err := constructPluginInstallJsonUrl(plugin)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Could not construct plugin install json file URL. %s", err))
	}
	downloadedFile, downloadErr := common.DownloadToTempDir(versionInstallDescriptionJsonUrl)
	if downloadErr != nil {
		return "", errors.New(fmt.Sprintf("Could not find %s file. Check install name and version. %s", versionInstallDescriptionJsonFile, downloadErr.Error()))
	}
	return downloadedFile, nil
}

func constructPluginInstallJsonUrl(plugin string) (string, error) {
	installJsonFile := plugin + "-install.json"
	repoUrl, err := getGaugeRepositoryUrl()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s/%s", repoUrl, installJsonFile), nil
}

func getGaugeRepositoryUrl() (string, error) {
	config, err := common.GetGaugeConfiguration()
	if err != nil {
		return "", err
	}
	return config[common.GaugeRepositoryUrl], nil
}

func (installDesc *installDescription) getVersion(version string) (*versionInstallDescription, error) {
	for _, versionInstallDescription := range installDesc.Versions {
		if versionInstallDescription.Version == version {
			return &versionInstallDescription, nil
		}
	}
	return nil, errors.New("Could not find install description for Version " + version)
}

func (installDesc *installDescription) getLatestCompatibleVersionTo(version *version) (*versionInstallDescription, error) {
	installDesc.sortVersionInstallDescriptions()
	for _, versionInstallDesc := range installDesc.Versions {
		if err := checkCompatiblity(version, &versionInstallDesc.GaugeVersionSupport); err == nil {
			return &versionInstallDesc, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("Compatible version to %s not found", version))

}

func (installDescription *installDescription) sortVersionInstallDescriptions() {
	sort.Sort(ByDecreasingVersion(installDescription.Versions))
}

func checkCompatiblity(version *version, versionSupport *versionSupport) error {
	minSupportVersion, err := parseVersion(versionSupport.Minimum)
	if err != nil {
		return errors.New(fmt.Sprintf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err))
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := parseVersion(versionSupport.Maximum)
		if err != nil {
			return errors.New(fmt.Sprintf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err))
		}
		if version.isBetween(minSupportVersion, maxSupportVersion) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("Version %s is not between %s and %s", version, minSupportVersion, maxSupportVersion))
		}
	}

	if minSupportVersion.isLesserThanEqualTo(version) {
		return nil
	}
	return errors.New(fmt.Sprintf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, version))
}

type ByDecreasingVersion []versionInstallDescription

func (a ByDecreasingVersion) Len() int      { return len(a) }
func (a ByDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDecreasingVersion) Less(i, j int) bool {
	version1, _ := parseVersion(a[i].Version)
	version2, _ := parseVersion(a[j].Version)
	return version1.isGreaterThan(version2)
}
<|endoftext|>