<commit_before>package api

import (
	"testing"

	"reflect"

	"github.com/tecsisa/authorizr/database"
)

func TestGetUserByExternalId(t *testing.T) {
	testcases := map[string]struct {
		// User authenticated
		authUser AuthenticatedUser
		// Resource urn that user wants to access
		id string
		// Error to compare when we expect an error
		expectedUser              *User
		wantError                 *Error
		getGroupsByUserIDResult   []Group
		getPoliciesAttachedResult []Policy
		getUserByExternalIDResult *User

		// GetUserByExternalID Method Out Arguments
		err error
	}{
		"OKCase": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			id: "123",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "123",
				Path:       "/users/test/",
			},
		},
		"ErrorAuthUserNotExist": {
			authUser: AuthenticatedUser{
				Identifier: "notAdminUser",
				Admin:      false,
			},
			id: "123",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "123",
				Path:       "/users/test/",
			},
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
		},
		"ErrorUnauthorizedResource": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      false,
			},
			getUserByExternalIDResult: &User{
				ID:         "000",
				ExternalID: "000",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "000"),
			},
			getGroupsByUserIDResult: []Group{
				Group{
					ID:   "GROUP-USER-ID",
					Name: "groupUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_GROUP, "/path/", "groupUser"),
				},
			},
			getPoliciesAttachedResult: []Policy{
				Policy{
					ID:   "POLICY-USER-ID",
					Name: "policyUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_POLICY, "/path/", "policyUser"),
					Statements: &[]Statement{
						Statement{
							Effect: "allow",
							Action: []string{
								USER_ACTION_GET_USER,
							},
							Resources: []string{
								GetUrnPrefix("", RESOURCE_USER, "/path/"),
							},
						},
						Statement{
							Effect: "deny",
							Action: []string{
								USER_ACTION_GET_USER,
							},
							Resources: []string{
								CreateUrn("", RESOURCE_USER, "/path/", "000"),
							},
						},
					},
				},
			},
			id: "000",
			expectedUser: &User{
				ID:         "000",
				ExternalID: "000",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "000"),
			},
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
		},
		"ErrorUserNotExist": {
			authUser: AuthenticatedUser{
				Identifier: "notAdminUser",
				Admin:      true,
			},
			id: "111",
			wantError: &Error{
				Code: USER_BY_EXTERNAL_ID_NOT_FOUND,
			},
			err: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "Error",
			},
		},
		"NoIDpassed": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
		},
		"InternalError": {
			id: "1234",
			err: &database.Error{
				Code: database.INTERNAL_ERROR,
			},
			wantError: &Error{
				Code: UNKNOWN_API_ERROR,
			},
		},
	}

	testRepo := makeTestRepo()
	testAPI := makeTestAPI(testRepo)

	for x, testcase := range testcases {
		testRepo.ArgsOut[GetUserByExternalIDMethod][0] = testcase.expectedUser
		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.err
		testRepo.ArgsOut[GetGroupsByUserIDMethod][0] = testcase.getGroupsByUserIDResult
		testRepo.ArgsOut[GetPoliciesAttachedMethod][0] = testcase.getPoliciesAttachedResult
		user, err := testAPI.GetUserByExternalId(testcase.authUser, testcase.id)
		if testcase.wantError != nil {
			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed", x)
			} else {
				if testcase.expectedUser.ExternalID != user.ExternalID {
					t.Fatalf("Test %v failed. Received different users (wanted:%v / received:%v)", x, testcase.expectedUser.ExternalID, user.ExternalID)
				}
			}
		}
	}

}

func TestAddUser(t *testing.T) {
	testcases := map[string]struct {
		// User authenticated
		authUser AuthenticatedUser
		// Resource urn that user wants to access
		externalID string
		path       string
		// Error to compare when we expect an error
		expectedUser *User
		wantError    *Error
		// GetUserByExternalID Method Out Arguments
		err    error
		errGet error
	}{
		"OKCase": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "1234",
				Path:       "/tecsisa/",
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
		"AlreadyExists": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			wantError: &Error{
				Code:    USER_ALREADY_EXIST,
				Message: "User already exists",
			},
		},
		"Nopath": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			externalID: "1234",
		},
		"NoID": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			path: "/tecsisa/",
		},
		"NoAuth": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      false,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
	}

	testRepo := makeTestRepo()
	testAPI := makeTestAPI(testRepo)

	for x, testcase := range testcases {

		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.errGet
		testRepo.ArgsOut[AddUserMethod][0] = testcase.expectedUser
		testRepo.ArgsOut[AddUserMethod][1] = testcase.err
		user, err := testAPI.AddUser(testcase.authUser, testcase.externalID, testcase.path)
		if testcase.wantError != nil {
			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed: %v", x, err)
			} else {
				if !reflect.DeepEqual(testcase.expectedUser, user) {
					t.Fatalf("Test %v failed. Received different users", x)
				}
			}
		}
	}

}

func TestUpdateUser(t *testing.T) {
	testcases := map[string]struct {
		// User authenticated
		authUser AuthenticatedUser
		// Resource urn that user wants to access
		externalID string
		path       string
		// Error to compare when we expect an error
		expectedUser *User
		wantError    *Error
		// GetUserByExternalID Method Out Arguments
		err    error
		errGet error
	}{
		"OKCase": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "1234",
				Path:       "/tecsisa/",
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
		"AlreadyExists": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			wantError: &Error{
				Code:    USER_ALREADY_EXIST,
				Message: "User already exists",
			},
		},
		"Nopath": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			externalID: "1234",
		},
		"NoID": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			path: "/tecsisa/",
		},
		"NoAuth": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      false,
			},
			externalID: "1234",
			path:       "/tecsisa/",
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
	}

	testRepo := makeTestRepo()
	testAPI := makeTestAPI(testRepo)

	for x, testcase := range testcases {

		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.errGet
		testRepo.ArgsOut[AddUserMethod][0] = testcase.expectedUser
		testRepo.ArgsOut[AddUserMethod][1] = testcase.err
		user, err := testAPI.AddUser(testcase.authUser, testcase.externalID, testcase.path)
		if testcase.wantError != nil {
			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed: %v", x, err)
			} else {
				if !reflect.DeepEqual(testcase.expectedUser.ExternalID, user.ExternalID) {
					t.Fatalf("Test %v failed. Received different users (wanted:%v / received:%v)", x)
				}
			}
		}
	}

}
<commit_msg>AddUser test<commit_after>package api

import (
	"testing"

	"reflect"

	"github.com/tecsisa/authorizr/database"
)

func TestGetUserByExternalId(t *testing.T) {
	testcases := map[string]struct {
		// User authenticated
		authUser AuthenticatedUser
		// Resource urn that user wants to access
		id string
		// Error to compare when we expect an error
		expectedUser              *User
		wantError                 *Error
		getGroupsByUserIDResult   []Group
		getPoliciesAttachedResult []Policy
		getUserByExternalIDResult *User

		// GetUserByExternalID Method Out Arguments
		err error
	}{
		"OKCase": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			id: "123",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "123",
				Path:       "/users/test/",
			},
		},
		"ErrorAuthUserNotExist": {
			authUser: AuthenticatedUser{
				Identifier: "notAdminUser",
				Admin:      false,
			},
			id: "123",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "123",
				Path:       "/users/test/",
			},
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
		},
		"ErrorUnauthorizedResource": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      false,
			},
			getUserByExternalIDResult: &User{
				ID:         "000",
				ExternalID: "000",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "000"),
			},
			getGroupsByUserIDResult: []Group{
				Group{
					ID:   "GROUP-USER-ID",
					Name: "groupUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_GROUP, "/path/", "groupUser"),
				},
			},
			getPoliciesAttachedResult: []Policy{
				Policy{
					ID:   "POLICY-USER-ID",
					Name: "policyUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_POLICY, "/path/", "policyUser"),
					Statements: &[]Statement{
						Statement{
							Effect: "allow",
							Action: []string{
								USER_ACTION_GET_USER,
							},
							Resources: []string{
								GetUrnPrefix("", RESOURCE_USER, "/path/"),
							},
						},
						Statement{
							Effect: "deny",
							Action: []string{
								USER_ACTION_GET_USER,
							},
							Resources: []string{
								CreateUrn("", RESOURCE_USER, "/path/", "000"),
							},
						},
					},
				},
			},
			id: "000",
			expectedUser: &User{
				ID:         "000",
				ExternalID: "000",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "000"),
			},
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
		},
		"ErrorUserNotExist": {
			authUser: AuthenticatedUser{
				Identifier: "notAdminUser",
				Admin:      true,
			},
			id: "111",
			wantError: &Error{
				Code: USER_BY_EXTERNAL_ID_NOT_FOUND,
			},
			err: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "Error",
			},
		},
		"NoIDpassed": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
		},
		"InternalError": {
			id: "1234",
			err: &database.Error{
				Code: database.INTERNAL_ERROR,
			},
			wantError: &Error{
				Code: UNKNOWN_API_ERROR,
			},
		},
	}

	testRepo := makeTestRepo()
	testAPI := makeTestAPI(testRepo)

	for x, testcase := range testcases {
		testRepo.ArgsOut[GetUserByExternalIDMethod][0] = testcase.expectedUser
		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.err
		testRepo.ArgsOut[GetGroupsByUserIDMethod][0] = testcase.getGroupsByUserIDResult
		testRepo.ArgsOut[GetPoliciesAttachedMethod][0] = testcase.getPoliciesAttachedResult
		user, err := testAPI.GetUserByExternalId(testcase.authUser, testcase.id)
		if testcase.wantError != nil {
			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed", x)
			} else {
				if testcase.expectedUser.ExternalID != user.ExternalID {
					t.Fatalf("Test %v failed. Received different users (wanted:%v / received:%v)", x, testcase.expectedUser.ExternalID, user.ExternalID)
				}
			}
		}
	}

}

func TestAddUser(t *testing.T) {
	testcases := map[string]struct {
		// User authenticated
		authUser AuthenticatedUser
		// Resource urn that user wants to access
		externalID                string
		path                      string
		getUserByExternalIDResult *User
		getGroupsByUserIDResult   []Group
		getPoliciesAttachedResult []Policy

		// Error to compare when we expect an error
		expectedUser *User
		wantError    *Error
		// GetUserByExternalID Method Out Arguments
		err    error
		errGet error
	}{
		"OKCase": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/example/",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "1234",
				Path:       "/example/",
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
		"AlreadyExists": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "1234",
			path:       "/example/",
			wantError: &Error{
				Code:    USER_ALREADY_EXIST,
				Message: "User already exists",
			},
		},
		"Nopath": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			externalID: "1234",
		},
		"NoID": {
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
			path: "/example/",
		},
		"NoAuth": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      false,
			},
			externalID: "1234",
			path:       "/example/",
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
		},
		"ErrorUnauthorizedResource": {
			authUser: AuthenticatedUser{
				Identifier: "000",
				Admin:      false,
			},
			getUserByExternalIDResult: &User{
				ID:         "000",
				ExternalID: "000",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "000"),
			},
			getGroupsByUserIDResult: []Group{
				Group{
					ID:   "GROUP-USER-ID",
					Name: "groupUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_GROUP, "/path/", "groupUser"),
				},
			},
			getPoliciesAttachedResult: []Policy{
				Policy{
					ID:   "POLICY-USER-ID",
					Name: "policyUser",
					Path: "/path/",
					Urn:  CreateUrn("example", RESOURCE_POLICY, "/path/", "policyUser"),
					Statements: &[]Statement{
						Statement{
							Effect: "allow",
							Action: []string{
								USER_ACTION_CREATE_USER,
							},
							Resources: []string{
								GetUrnPrefix("", RESOURCE_USER, "/test/"),
							},
						},
						Statement{
							Effect: "deny",
							Action: []string{
								USER_ACTION_CREATE_USER,
							},
							Resources: []string{
								GetUrnPrefix("", RESOURCE_USER, "/test/asd"),
							},
						},
					},
				},
			},
			externalID: "1234",
			path:       "/test/asd/",
			wantError: &Error{
				Code: UNAUTHORIZED_RESOURCES_ERROR,
			},
		},
		"InvalidExtID": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "*%~#@|",
			path:       "/example/",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "1234",
				Path:       "/example/",
			},
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
		},
		"InvalidPath": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			externalID: "012",
			path:       "/**%%/*123",
			expectedUser: &User{
				ID:         "543210",
				ExternalID: "1234",
				Path:       "/example/",
			},
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
		},
		"DBErr1": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			getUserByExternalIDResult: &User{
				ID:         "123456",
				ExternalID: "123456",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "123456"),
			},
			errGet: &database.Error{
				Code:    database.USER_NOT_FOUND,
				Message: "User not found",
			},
			externalID: "12",
			path:       "/example/",
			err: &database.Error{
				Code: database.INTERNAL_ERROR,
			},
			wantError: &Error{
				Code: UNKNOWN_API_ERROR,
			},
		},
		"DBErr2": {
			authUser: AuthenticatedUser{
				Identifier: "123456",
				Admin:      true,
			},
			getUserByExternalIDResult: &User{
				ID:         "123456",
				ExternalID: "123456",
				Path:       "/path/",
				Urn:        CreateUrn("", RESOURCE_USER, "/path/", "123456"),
			},
			errGet: &database.Error{
				Code:    database.INTERNAL_ERROR,
				Message: "User not found",
			},
			externalID: "12",
			path:       "/example/",
			err: &database.Error{
				Code: database.INTERNAL_ERROR,
			},
			wantError: &Error{
				Code: UNKNOWN_API_ERROR,
			},
		},
	}

	testRepo := makeTestRepo()
	testAPI := makeTestAPI(testRepo)

	for x, testcase := range testcases {
		testRepo.ArgsOut[GetUserByExternalIDMethod][0] = testcase.getUserByExternalIDResult
		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.errGet
		testRepo.ArgsOut[GetGroupsByUserIDMethod][0] = testcase.getGroupsByUserIDResult
		testRepo.ArgsOut[GetPoliciesAttachedMethod][0] = testcase.getPoliciesAttachedResult
		testRepo.ArgsOut[AddUserMethod][0] = testcase.expectedUser
		testRepo.ArgsOut[AddUserMethod][1] = testcase.err
		user, err := testAPI.AddUser(testcase.authUser, testcase.externalID, testcase.path)
		if testcase.wantError != nil {
			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed: %v", x, err)
			} else {
				if !reflect.DeepEqual(testcase.expectedUser, user) {
					t.Fatalf("Test %v failed. Received different users", x)
				}
			}
		}
	}

}

//func TestUpdateUser(t *testing.T) {
//	testcases := map[string]struct {
//		// User authenticated
//		authUser AuthenticatedUser
//		// Resource urn that user wants to access
//		externalID string
//		path       string
//		// Error to compare when we expect an error
//		expectedUser *User
//		wantError    *Error
//		// GetUserByExternalID Method Out Arguments
//		err    error
//		errGet error
//	}{
//		"OKCase": {
//			authUser: AuthenticatedUser{
//				Identifier: "123456",
//				Admin:      true,
//			},
//			externalID: "1234",
//			path:       "/example/",
//			expectedUser: &User{
//				ID:         "543210",
//				ExternalID: "1234",
//				Path:       "/example/",
//			},
//			errGet: &database.Error{
//				Code:    database.USER_NOT_FOUND,
//				Message: "User not found",
//			},
//		},
//		"AlreadyExists": {
//			authUser: AuthenticatedUser{
//				Identifier: "123456",
//				Admin:      true,
//			},
//			externalID: "1234",
//			path:       "/example/",
//			wantError: &Error{
//				Code:    USER_ALREADY_EXIST,
//				Message: "User already exists",
//			},
//		},
//		"Nopath": {
//			wantError: &Error{
//				Code: INVALID_PARAMETER_ERROR,
//			},
//			externalID: "1234",
//		},
//		"NoID": {
//			wantError: &Error{
//				Code: INVALID_PARAMETER_ERROR,
//			},
//			path: "/example/",
//		},
//		"NoAuth": {
//			authUser: AuthenticatedUser{
//				Identifier: "123456",
//				Admin:      false,
//			},
//			externalID: "1234",
//			path:       "/example/",
//			wantError: &Error{
//				Code: UNAUTHORIZED_RESOURCES_ERROR,
//			},
//			errGet: &database.Error{
//				Code:    database.USER_NOT_FOUND,
//				Message: "User not found",
//			},
//		},
//	}
//
//	testRepo := makeTestRepo()
//	testAPI := makeTestAPI(testRepo)
//
//	for x, testcase := range testcases {
//
//		testRepo.ArgsOut[GetUserByExternalIDMethod][1] = testcase.errGet
//		testRepo.ArgsOut[AddUserMethod][0] = testcase.expectedUser
//		testRepo.ArgsOut[AddUserMethod][1] = testcase.err
//		user, err := testAPI.AddUser(testcase.authUser, testcase.externalID, testcase.path)
//		if testcase.wantError != nil {
//			if errCode := err.(*Error).Code; errCode != testcase.wantError.Code {
//				t.Fatalf("Test %v failed. Got error %v, expected %v", x, errCode, testcase.wantError.Code)
//			}
//		} else {
//			if err != nil {
//				t.Fatalf("Test %v failed: %v", x, err)
//			} else {
//				if !reflect.DeepEqual(testcase.expectedUser.ExternalID, user.ExternalID) {
//					t.Fatalf("Test %v failed. Received different users (wanted:%v / received:%v)", x)
//				}
//			}
//		}
//	}
//
//}
<|endoftext|>


<commit_before>package goproxy

import (
	"bytes"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strings"
)

// ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request
// before sending it to the remote server
type ReqCondition interface {
	RespCondition
	HandleReq(req *http.Request, ctx *ProxyCtx) bool
}

// RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response
// before sending it to the proxy client. Note that resp might be nil, in case there was an
// error sending the request.
type RespCondition interface {
	HandleResp(resp *http.Response, ctx *ProxyCtx) bool
}

// ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx)
type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool

// RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx)
type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool

func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool {
	return c(req, ctx)
}

// ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that
// to be usable as RespCondition.
func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
	return c(ctx.Req, ctx)
}

func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
	return c(resp, ctx)
}

// UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested
// has the given prefix, with or without the host.
// For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match
// requests to url 'http://host/x'
func UrlHasPrefix(prefix string) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return strings.HasPrefix(req.URL.Path, prefix) ||
			strings.HasPrefix(req.URL.Host+"/"+req.URL.Path, prefix) ||
			strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix)
	}
}

// UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings
// with or without the host prefix.
// UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to
// any host, and requests of the form 'GET foo'.
func UrlIs(urls ...string) ReqConditionFunc {
	urlSet := make(map[string]bool)
	for _, u := range urls {
		urlSet[u] = true
	}
	return func(req *http.Request, ctx *ProxyCtx) bool {
		_, pathOk := urlSet[req.URL.Path]
		_, hostAndOk := urlSet[req.URL.Host+req.URL.Path]
		return pathOk || hostAndOk
	}
}

// ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches
// any of the given regular expressions.
func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		for _, re := range regexps {
			if re.MatchString(req.Host) {
				return true
			}
		}
		return false
	}
}

// ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal
// to one of the given strings
func ReqHostIs(hosts ...string) ReqConditionFunc {
	hostSet := make(map[string]bool)
	for _, h := range hosts {
		hostSet[h] = true
	}
	return func(req *http.Request, ctx *ProxyCtx) bool {
		_, ok := hostSet[req.URL.Host]
		return ok
	}
}

var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`)

// IsLocalHost checks whether the destination host is explicitly local host
// (buggy, there can be IPv6 addresses it doesn't catch)
var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool {
	return req.URL.Host == "::1" ||
		req.URL.Host == "0:0:0:0:0:0:0:1" ||
		localHostIpv4.MatchString(req.URL.Host) ||
		req.URL.Host == "localhost"
}

// UrlMatches returns a ReqCondition testing whether the destination URL
// of the request matches the given regexp, with or without prefix
func UrlMatches(re *regexp.Regexp) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return re.MatchString(req.URL.Path) ||
			re.MatchString(req.URL.Host+req.URL.Path)
	}
}

// DstHostIs returns a ReqCondition testing wether the host in the request url is the given string
func DstHostIs(host string) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return req.URL.Host == host
	}
}

// SrcIpIs returns a ReqCondition testing wether the source IP of the request is the given string
func SrcIpIs(ip string) ReqCondition {
	return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool {
		return strings.HasPrefix(req.RemoteAddr, ip+":")
	})
}

// Not returns a ReqCondition negating the given ReqCondition
func Not(r ReqCondition) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return !r.HandleReq(req, ctx)
	}
}

// ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal
// to one of the given strings.
func ContentTypeIs(typ string, types ...string) RespCondition {
	types = append(types, typ)
	return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
		if resp == nil {
			return false
		}
		contentType := resp.Header.Get("Content-Type")
		for _, typ := range types {
			if contentType == typ || strings.HasPrefix(contentType, typ+";") {
				return true
			}
		}
		return false
	})
}

// ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
// You will use the ReqProxyConds struct to register a ReqHandler, that would filter
// the request, only if all the given ReqCondition matched.
// Typical usage:
//	proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...)
func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds {
	return &ReqProxyConds{proxy, conds}
}

// ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would
// handle the request if all conditions on the HTTP request are met.
type ReqProxyConds struct {
	proxy    *ProxyHttpServer
	reqConds []ReqCondition
}

// DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f))
func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) {
	pcond.Do(FuncReqHandler(f))
}

// ReqProxyConds.Do will register the ReqHandler on the proxy,
// the ReqHandler will handle the HTTP request if all the conditions
// aggregated in the ReqProxyConds are met. Typical usage:
//	proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy
//	proxy.OnRequest(cond1,cond2).Do(handler)
//	// given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true
//	// if they are, will call handler.Handle(req,ctx)
func (pcond *ReqProxyConds) Do(h ReqHandler) {
	pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers,
		FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(r, ctx) {
					return r, nil
				}
			}
			return h.Handle(r, ctx)
		}))
}

// HandleConnect is used when proxy receives an HTTP CONNECT request,
// it'll then use the HttpsHandler to determine what should it
// do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction
// struct returned will determine what to do with this request. ConnectAccept will simply accept the request
// forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the
// client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man
// in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped
// connection.
// The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy
// will use the default tls configuration.
//	proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests
func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) {
	pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
		FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return nil, ""
				}
			}
			return h.HandleConnect(host, ctx)
		}))
}

// HandleConnectFunc is equivalent to HandleConnect,
// for example, accepting CONNECT request if they contain a password in header
//	io.WriteString(h,password)
//	passHash := h.Sum(nil)
//	proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
//		c := sha1.New()
//		io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth"))
//		if c.Sum(nil) == passHash {
//			return OkConnect, host
//		}
//		return RejectConnect, host
//	})
func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) {
	pcond.HandleConnect(FuncHttpsHandler(f))
}

func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) {
	pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
		FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return nil, ""
				}
			}
			return &ConnectAction{Action: ConnectHijack, Hijack: f}, host
		}))
}

// ProxyConds is used to aggregate RespConditions for a ProxyHttpServer.
// Upon calling ProxyConds.Do, it will register a RespHandler that would
// handle the HTTP response from remote server if all conditions on the HTTP response are met.
type ProxyConds struct {
	proxy    *ProxyHttpServer
	reqConds []ReqCondition
	respCond []RespCondition
}

// ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f))
func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) {
	pcond.Do(FuncRespHandler(f))
}

// ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every
// request that matches the conditions aggregated in pcond.
func (pcond *ProxyConds) Do(h RespHandler) {
	pcond.proxy.respHandlers = append(pcond.proxy.respHandlers,
		FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return resp
				}
			}
			for _, cond := range pcond.respCond {
				if !cond.HandleResp(resp, ctx) {
					return resp
				}
			}
			return h.Handle(resp, ctx)
		}))
}

// OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is
//	proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used
//				// if cond1.HandleResp(resp) && cond2.HandleResp(resp)
func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds {
	return &ProxyConds{proxy, make([]ReqCondition, 0), conds}
}

// AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to
// eavesdrop all https connections to www.google.com, we can use
//	proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm)
var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
	return MitmConnect, host
}

// AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow
// connections to hosts on any other port than 443
//	proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))).
//		HandleConnect(goproxy.AlwaysReject)
var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
	return RejectConnect, host
}

// HandleBytes will return a RespHandler that read the entire body of the request
// to a byte array in memory, would run the user supplied f function on the byte arra,
// and will replace the body of the original response with the resulting byte array.
func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler {
	return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			ctx.Warnf("Cannot read response %s", err)
			return resp
		}
		resp.Body.Close()

		resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx)))
		return resp
	})
}
<commit_msg>Remove extra slash from UrlHasPrefix<commit_after>package goproxy

import (
	"bytes"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strings"
)

// ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request
// before sending it to the remote server
type ReqCondition interface {
	RespCondition
	HandleReq(req *http.Request, ctx *ProxyCtx) bool
}

// RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response
// before sending it to the proxy client. Note that resp might be nil, in case there was an
// error sending the request.
type RespCondition interface {
	HandleResp(resp *http.Response, ctx *ProxyCtx) bool
}

// ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx)
type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool

// RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx)
type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool

func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool {
	return c(req, ctx)
}

// ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that
// to be usable as RespCondition.
func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
	return c(ctx.Req, ctx)
}

func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
	return c(resp, ctx)
}

// UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested
// has the given prefix, with or without the host.
// For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match
// requests to url 'http://host/x'
func UrlHasPrefix(prefix string) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return strings.HasPrefix(req.URL.Path, prefix) ||
			strings.HasPrefix(req.URL.Host+req.URL.Path, prefix) ||
			strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix)
	}
}

// UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings
// with or without the host prefix.
// UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to
// any host, and requests of the form 'GET foo'.
func UrlIs(urls ...string) ReqConditionFunc {
	urlSet := make(map[string]bool)
	for _, u := range urls {
		urlSet[u] = true
	}
	return func(req *http.Request, ctx *ProxyCtx) bool {
		_, pathOk := urlSet[req.URL.Path]
		_, hostAndOk := urlSet[req.URL.Host+req.URL.Path]
		return pathOk || hostAndOk
	}
}

// ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches
// any of the given regular expressions.
func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		for _, re := range regexps {
			if re.MatchString(req.Host) {
				return true
			}
		}
		return false
	}
}

// ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal
// to one of the given strings
func ReqHostIs(hosts ...string) ReqConditionFunc {
	hostSet := make(map[string]bool)
	for _, h := range hosts {
		hostSet[h] = true
	}
	return func(req *http.Request, ctx *ProxyCtx) bool {
		_, ok := hostSet[req.URL.Host]
		return ok
	}
}

var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`)

// IsLocalHost checks whether the destination host is explicitly local host
// (buggy, there can be IPv6 addresses it doesn't catch)
var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool {
	return req.URL.Host == "::1" ||
		req.URL.Host == "0:0:0:0:0:0:0:1" ||
		localHostIpv4.MatchString(req.URL.Host) ||
		req.URL.Host == "localhost"
}

// UrlMatches returns a ReqCondition testing whether the destination URL
// of the request matches the given regexp, with or without prefix
func UrlMatches(re *regexp.Regexp) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return re.MatchString(req.URL.Path) ||
			re.MatchString(req.URL.Host+req.URL.Path)
	}
}

// DstHostIs returns a ReqCondition testing wether the host in the request url is the given string
func DstHostIs(host string) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return req.URL.Host == host
	}
}

// SrcIpIs returns a ReqCondition testing wether the source IP of the request is the given string
func SrcIpIs(ip string) ReqCondition {
	return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool {
		return strings.HasPrefix(req.RemoteAddr, ip+":")
	})
}

// Not returns a ReqCondition negating the given ReqCondition
func Not(r ReqCondition) ReqConditionFunc {
	return func(req *http.Request, ctx *ProxyCtx) bool {
		return !r.HandleReq(req, ctx)
	}
}

// ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal
// to one of the given strings.
func ContentTypeIs(typ string, types ...string) RespCondition {
	types = append(types, typ)
	return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
		if resp == nil {
			return false
		}
		contentType := resp.Header.Get("Content-Type")
		for _, typ := range types {
			if contentType == typ || strings.HasPrefix(contentType, typ+";") {
				return true
			}
		}
		return false
	})
}

// ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
// You will use the ReqProxyConds struct to register a ReqHandler, that would filter
// the request, only if all the given ReqCondition matched.
// Typical usage:
//	proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...)
func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds {
	return &ReqProxyConds{proxy, conds}
}

// ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would
// handle the request if all conditions on the HTTP request are met.
type ReqProxyConds struct {
	proxy    *ProxyHttpServer
	reqConds []ReqCondition
}

// DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f))
func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) {
	pcond.Do(FuncReqHandler(f))
}

// ReqProxyConds.Do will register the ReqHandler on the proxy,
// the ReqHandler will handle the HTTP request if all the conditions
// aggregated in the ReqProxyConds are met. Typical usage:
//	proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy
//	proxy.OnRequest(cond1,cond2).Do(handler)
//	// given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true
//	// if they are, will call handler.Handle(req,ctx)
func (pcond *ReqProxyConds) Do(h ReqHandler) {
	pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers,
		FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(r, ctx) {
					return r, nil
				}
			}
			return h.Handle(r, ctx)
		}))
}

// HandleConnect is used when proxy receives an HTTP CONNECT request,
// it'll then use the HttpsHandler to determine what should it
// do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction
// struct returned will determine what to do with this request. ConnectAccept will simply accept the request
// forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the
// client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man
// in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped
// connection.
// The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy
// will use the default tls configuration.
//	proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests
func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) {
	pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
		FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return nil, ""
				}
			}
			return h.HandleConnect(host, ctx)
		}))
}

// HandleConnectFunc is equivalent to HandleConnect,
// for example, accepting CONNECT request if they contain a password in header
//	io.WriteString(h,password)
//	passHash := h.Sum(nil)
//	proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
//		c := sha1.New()
//		io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth"))
//		if c.Sum(nil) == passHash {
//			return OkConnect, host
//		}
//		return RejectConnect, host
//	})
func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) {
	pcond.HandleConnect(FuncHttpsHandler(f))
}

func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) {
	pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
		FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return nil, ""
				}
			}
			return &ConnectAction{Action: ConnectHijack, Hijack: f}, host
		}))
}

// ProxyConds is used to aggregate RespConditions for a ProxyHttpServer.
// Upon calling ProxyConds.Do, it will register a RespHandler that would
// handle the HTTP response from remote server if all conditions on the HTTP response are met.
type ProxyConds struct {
	proxy    *ProxyHttpServer
	reqConds []ReqCondition
	respCond []RespCondition
}

// ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f))
func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) {
	pcond.Do(FuncRespHandler(f))
}

// ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every
// request that matches the conditions aggregated in pcond.
func (pcond *ProxyConds) Do(h RespHandler) {
	pcond.proxy.respHandlers = append(pcond.proxy.respHandlers,
		FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
			for _, cond := range pcond.reqConds {
				if !cond.HandleReq(ctx.Req, ctx) {
					return resp
				}
			}
			for _, cond := range pcond.respCond {
				if !cond.HandleResp(resp, ctx) {
					return resp
				}
			}
			return h.Handle(resp, ctx)
		}))
}

// OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is
//	proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used
//				// if cond1.HandleResp(resp) && cond2.HandleResp(resp)
func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds {
	return &ProxyConds{proxy, make([]ReqCondition, 0), conds}
}

// AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to
// eavesdrop all https connections to www.google.com, we can use
//	proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm)
var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
	return MitmConnect, host
}

// AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow
// connections to hosts on any other port than 443
//	proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))).
//		HandleConnect(goproxy.AlwaysReject)
var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
	return RejectConnect, host
}

// HandleBytes will return a RespHandler that read the entire body of the request
// to a byte array in memory, would run the user supplied f function on the byte arra,
// and will replace the body of the original response with the resulting byte array.
func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler {
	return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			ctx.Warnf("Cannot read response %s", err)
			return resp
		}
		resp.Body.Close()

		resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx)))
		return resp
	})
}
<|endoftext|>


<commit_before>// gitbot project gitbot.go
package gitbot

import (
	"encoding/json"
	"fmt"
	"github.com/aarondl/cinotify"
	"github.com/aarondl/ultimateq/data"
	"github.com/gorilla/mux"
	"net/http"
)

const (
	Name       = "gitbot"
	COMMIT_MSG = "[%s/%s] %s"
	NOMSG      = "No message"
	UNKNOWN    = "Unknown"
)

func AlertChan(pl PushPayload, target string, endpoint *data.DataEndpoint) {
	var msg string
	var c *PushCommit
	name := pl.Name()
	branch := pl.Branch()

	endpoint.Privmsg(target, pl)

	for i := 0; i < pl.NumCommits(); i++ {
		c = pl.Commits[i]
		msg = fmt.Sprintf(COMMIT_MSG, name, branch, c)
		endpoint.Privmsg(target, msg)
	}
}

func init() {
	cinotify.Register(Name, gitHandler{})
}

// gitHandler implements cinotify.Handler
type gitHandler struct {
}

func (_ gitHandler) Handle(r *http.Request) fmt.Stringer {
	defer r.Body.Close()
	dec := json.NewDecoder(r.Body)
	var err error

	switch r.Header.Get("X-GITHUB-EVENT") {
	//Triggered when a repository branch is pushed to.
	case "push":
		var pushPayload *PushPayload
		if err = dec.Decode(pushPayload); err == nil {
			return pushPayload
		}

	// Triggered when a commit comment is created.
	case "commit_comment":
		var pl *PayloadCommitComment
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Represents a deleted branch or tag.
	case "delete":
		var pl *PayloadDelete
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a user forks a repository.
	case "fork":
		var pl *PayloadFork
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a Wiki page is created or updated.
	case "gollum":
		var pl *PayloadGollum
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when an issue comment is created.
	case "issue_comment":
		var pl *PayloadIssueComment
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when an issue is created, closed or reopened.
	case "issues":
		var pl *PayloadIssues
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a user is added as a collaborator to a repository.
	case "member":
		var pl *PayloadMember
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a pull request is created, closed, reopened or synchronized.
	case "pull_request":
		var pl *PayloadPullRequest
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a comment is created on a portion of the unified diff of a pull request.
	case "pull_request_review_comment":

	//Triggered when a release is published.
	case "release":
		var pl *PayloadRelease
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//Triggered when a user is added to a team or when a repository is added to a team.
	case "team_add":
		var pl *PayloadTeamAdd
		if err = dec.Decode(pl); err == nil {
			return pl
		}

	//The WatchEvent is related to starring a repository, not watching.
	case "watch":
		var pl *PayloadWatch
		if err = dec.Decode(pl); err == nil {
			return pl
		}
	}

	if err != nil {
		cinotify.DoLogf("cinotify/gitbot/%s: Failed to decode json payload: %v",
			r.Header.Get("X-GITHUB-EVENT"), err)
	}
	return nil
}

func (_ gitHandler) Route(r *mux.Route) {
	r.Path("/").Methods("POST").Headers(
		"Content-Type", "application/json",
		"X-GITHUB-EVENT", "",
	)
}
<commit_msg>Fix Unmarshal errors<commit_after>// gitbot project gitbot.go
package gitbot

import (
	"encoding/json"
	"fmt"
	"github.com/aarondl/cinotify"
	"github.com/aarondl/ultimateq/data"
	"github.com/gorilla/mux"
	"net/http"
)

const (
	Name       = "gitbot"
	COMMIT_MSG = "[%s/%s] %s"
	NOMSG      = "No message"
	UNKNOWN    = "Unknown"
)

func AlertChan(pl PushPayload, target string, endpoint *data.DataEndpoint) {
	var msg string
	var c *PushCommit
	name := pl.Name()
	branch := pl.Branch()

	endpoint.Privmsg(target, pl)

	for i := 0; i < pl.NumCommits(); i++ {
		c = pl.Commits[i]
		msg = fmt.Sprintf(COMMIT_MSG, name, branch, c)
		endpoint.Privmsg(target, msg)
	}
}

func init() {
	cinotify.Register(Name, gitHandler{})
}

// gitHandler implements cinotify.Handler
type gitHandler struct {
}

func (_ gitHandler) Handle(r *http.Request) fmt.Stringer {
	defer r.Body.Close()
	dec := json.NewDecoder(r.Body)
	var err error

	switch r.Header.Get("X-GITHUB-EVENT") {
	//Triggered when a repository branch is pushed to.
	case "push":
		var pushPayload PushPayload
		if err = dec.Decode(&pushPayload); err == nil {
			return pushPayload
		}

	// Triggered when a commit comment is created.
	case "commit_comment":
		var pl PayloadCommitComment
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Represents a deleted branch or tag.
	case "delete":
		var pl PayloadDelete
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a user forks a repository.
	case "fork":
		var pl PayloadFork
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a Wiki page is created or updated.
	case "gollum":
		var pl PayloadGollum
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when an issue comment is created.
	case "issue_comment":
		var pl PayloadIssueComment
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when an issue is created, closed or reopened.
	case "issues":
		var pl PayloadIssues
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a user is added as a collaborator to a repository.
	case "member":
		var pl PayloadMember
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a pull request is created, closed, reopened or synchronized.
	case "pull_request":
		var pl PayloadPullRequest
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a comment is created on a portion of the unified diff of a pull request.
	case "pull_request_review_comment":

	//Triggered when a release is published.
	case "release":
		var pl PayloadRelease
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//Triggered when a user is added to a team or when a repository is added to a team.
	case "team_add":
		var pl PayloadTeamAdd
		if err = dec.Decode(&pl); err == nil {
			return pl
		}

	//The WatchEvent is related to starring a repository, not watching.
	case "watch":
		var pl PayloadWatch
		if err = dec.Decode(&pl); err == nil {
			return pl
		}
	}

	if err != nil {
		cinotify.DoLogf("cinotify/gitbot/%s: Failed to decode json payload: %v",
			r.Header.Get("X-GITHUB-EVENT"), err)
	}
	return nil
}

func (_ gitHandler) Route(r *mux.Route) {
	r.Path("/").Methods("POST").Headers(
		"Content-Type", "application/json",
		"X-GITHUB-EVENT", "",
	)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"github.com/thoj/go-ircevent"
	"hash"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type GitHubAdapter struct {
	ircbot   *irc.Connection
	config   *GitHubConfig
	channels map[string]*GitHubRepositoryConfig
}

type GithubCreate struct {
	Ref          string
	RefType      string `json:"ref_type"`
	MasterBranch string
	Description  string
	PusherType   string
	Repository   GithubRepository
	Sender       GithubUser
}

type GithubDelete struct {
        Ref             string
        RefType         string `json:"ref_type"`
        PusherType      string
        Repository      GithubRepository
        Sender          GithubUser
}

type GithubRepository struct {
	Id               uint64
	Name             string
	FullName         string `json:"full_name"`
	Owner            GithubUser
	Private          bool
	HtmlUrl          string
	Description      string
	Fork             bool
	Url              string
	ForksUrl         string
	KeysUrl          string
	CollaboratorsUrl string
	TeamsUrl         string
	HooksUrl         string
	IssueEventsUrl   string
	EventsUrl        string
	AssigneesUrl     string
	BranchesUrl      string
	TagsUrl          string
	BlobsUrl         string
	GitTagsUrl       string
	GitRefsUrl       string
	TreesUrl         string
	StatusesUrl      string
	LanguagesUrl     string
	StargazersUrl    string
	ContributorsUrl  string
	SubscribersUrl   string
	SubscriptionUrl  string
	CommitsUrl       string
	GitCommitsUrl    string
	CommentsUrl      string
	CompareUrl       string
	MergesUrl        string
	ArchiveUrl       string
	DownloadsUrl     string
	IssuesUrl        string
	PullsUrl         string
	MilestonesUrl    string
	NotificationsUrl string
	LabelsUrl        string
	ReleasesUrl      string
	CreatedAt        string
	UpdatedAt        string
	PushedAt         string
	GitUrl           string
	SshUrl           string
	CloneUrl         string
	SvnUrl           string
	Homepage         string
	Size             uint64
	StargazersCount  uint64
	WatchersCount    uint64
	Language         string
	HasIssues        bool
	HasDownloads     bool
	HasWiki          bool
	ForksCount       uint64
	MirrorUrl        string
	OpenIssuesCount  uint64
	Forks            uint64
	OpenIssues       uint64
	Watchers         uint64
	DefaultBranch    string
}

type GithubUser struct {
	Login             string
	Id                uint64
	AvatarURL         string
	GravatarId        string
	Url               string
	HtmlUrl           string
	FollowersUrl      string
	FollowingUrl      string
	GistsUrl          string
	StarredUrl        string
	SubscriptionsUrl  string
	OrganizationsUrl  string
	ReposUrl          string
	EventsUrl         string
	ReceivedEventsUrl string
	Type              string
	SiteAdmin         bool
	Name              string
	Email             string
}

type GithubPush struct {
	Ref        string
	After      string
	Before     string
	Created    bool
	Deleted    bool
	Forced     bool
	Compare    string
	Commits    []GithubCommit
	HeadCommit GithubCommit `json:"head_commit"`
	Repository GithubRepository
	Pusher     GithubUser
}

type GithubCommit struct {
	Id        string
	Distinct  bool
	Message   string
	Timestamp string
	Url       string
	Author    GithubUser
	Committer GithubUser
	Added     []string
	Removed   []string
	Modified  []string
}

// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
func CheckMAC(message []byte, messageMAC string, key string) bool {
	var err error
	var mac hash.Hash
	var macdata []byte
	var macparts = strings.Split(messageMAC, "=")
	macdata, err = hex.DecodeString(macparts[1])
	if err != nil {
		log.Print("Error decoding hex digest: ", err)
		return false
	}
	switch macparts[0] {
	case "md5":
		mac = hmac.New(md5.New, []byte(key))
	case "sha1":
		mac = hmac.New(sha1.New, []byte(key))
	case "sha256":
		mac = hmac.New(sha256.New, []byte(key))
	case "sha512":
		mac = hmac.New(sha512.New, []byte(key))
	default:
		log.Print("Unsupported hash: ", macparts[0])
		return false
	}
	mac.Write(message)
	expectedMAC := mac.Sum(nil)
	return hmac.Equal(macdata, expectedMAC)
}

func (g *GithubUser) String() string {
	if len(g.Name) > 0 && len(g.Email) > 0 {
		return g.Name + " <" + g.Email + ">"
	} else if len(g.Name) > 0 {
		return g.Name
	} else if len(g.Email) > 0 {
		return g.Email
	}
	return g.Login
}

func (g *GithubRepository) String() string {
	if len(g.FullName) > 0 {
		return g.FullName
	}
	return g.Name
}

func (g *GithubCommit) String() string {
	var lines []string = strings.Split(g.Message, "\n")                // Commit message
	var text string = g.Author.String() + " \x02" + g.Id[0:7] + "\x0f" // First 7 characters

	if len(g.Added) > 0 {
		text += " \x0303" + strings.Join(g.Added, " ") + "\x0f"
	}

	if len(g.Removed) > 0 {
		text += " \x0304" + strings.Join(g.Removed, " ") + "\x0f"
	}

	if len(g.Modified) > 0 {
		text += " \x0310" + strings.Join(g.Modified, " ") + "\x0f"
	}
	if len(lines) > 0 {
		text += " " + lines[0]
	}
	return text
}

func (g *GithubPush) Strings() []string {
	var refs []string = strings.Split(g.Ref, "/")
	var prefix string = "\x0303" + g.Repository.String() + "\x0f \x0305" + refs[len(refs)-1] + "\x0f"
	var pushes []string = make([]string, 0)

	for _, commit := range g.Commits {
		pushes = append(pushes, prefix+" "+commit.String())
		pushes = append(pushes, prefix+" "+commit.Url)
	}
	return pushes
}

func (g *GithubCreate) String() string {
	return "\x0303" + g.Sender.String() + "\x0f has pushed a new " + g.RefType + " \x0305" + g.Ref + "\x0f to \x0303" + g.Repository.String() + "\x0f"
}

func (g *GithubDelete) String() string {
	return "\x0303" + g.Sender.String() + "\x0f has deleted a " + g.RefType + " \x0305" + g.Ref + "\x0f from \x0303" + g.Repository.String() + "\x0f"
}

func NewGitHubAdapter(ircbot *irc.Connection, config *GitHubConfig) *GitHubAdapter {
	var channels = make(map[string]*GitHubRepositoryConfig)
	for _, repo := range config.Repo {
		channels[repo.GetName()] = repo
	}
	return &GitHubAdapter{
		ircbot:   ircbot,
		config:   config,
		channels: channels,
	}
}

func (g *GitHubAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var body []byte
	var err error
	body, err = ioutil.ReadAll(req.Body)
	if err != nil {
		log.Print("Error reading body: ", err)
		return
	}

	switch req.Header.Get("X-GitHub-Event") {
	case "create":
		var create GithubCreate
		var githubconf *GitHubRepositoryConfig
		var ok bool
		var err error

		err = json.Unmarshal(body, &create)
		if err != nil {
			log.Print("Error decoding github create: ", err)
			return
		}
		log.Print(create.String())
		githubconf, ok = g.channels[create.Repository.String()]
		if !ok {
			log.Print("Repository ", create.Repository.String(), " not configured.")
			return
		}
		if !CheckMAC(body, req.Header.Get("X-Hub-Signature"), githubconf.GetSecret()) {
			log.Print("DEBUG Spam, spam spam")
			return
		}
		for _, channel := range githubconf.GetIrcChannel() {
			g.ircbot.Privmsg(channel, create.String())
		}
        case "delete":
                var del GithubDelete
                var githubconf *GitHubRepositoryConfig
                var ok bool
                var err error

                err = json.Unmarshal(body, &del)
                if err != nil {
                        log.Print("Error decoding github delete: ", err)
                        return
                }
                log.Print(del.String())
                githubconf, ok = g.channels[del.Repository.String()]
                if !ok {
                        log.Print("Repository ", del.Repository.String(), " not configured.")
                        return
                }
		if !CheckMAC(body, req.Header.Get("X-Hub-Signature"), githubconf.GetSecret()) {
			log.Print("DEBUG Spam, spam spam")
			return
		}
		for _, channel := range githubconf.GetIrcChannel() {
			g.ircbot.Privmsg(channel, del.String())
		}
	case "push":
		var push GithubPush
		var ok bool
		var githubconf *GitHubRepositoryConfig
		var err error

		err = json.Unmarshal(body, &push)

		if err != nil {
			log.Print("Error decoding github push: ", err)
			return
		}

		githubconf, ok = g.channels[push.Repository.String()]
		if !ok {
			log.Print("Repository ", push.Repository.String(), " not configured.")
			return
		}
		if !CheckMAC(body, req.Header.Get("X-Hub-Signature"), githubconf.GetSecret()) {
			log.Print("DEBUG Spam, spam spam")
			return
		}
		for _, channel := range githubconf.GetIrcChannel() {
			for _, commit := range push.Strings() {
				g.ircbot.Privmsg(channel, commit)
			}
		}
	default:
		log.Print("Unknown GitHub event.", req.Header.Get("X-GitHub-Event"))
	}
}
<commit_msg>Created an interface for github events to reduce the lines of code.<commit_after>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"github.com/thoj/go-ircevent"
	"hash"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type GitHubAdapter struct {
	ircbot   *irc.Connection
	config   *GitHubConfig
	channels map[string]*GitHubRepositoryConfig
}

// Every GithubEvent has a method Strings that returns a slice of string with the text for the irc PRIVMSG.
type GithubEvent interface {
        Strings() []string
        GetRepository() string
}

type GithubCreate struct {
	Ref          string
	RefType      string `json:"ref_type"`
	MasterBranch string
	Description  string
	PusherType   string
	Repository   GithubRepository
	Sender       GithubUser
}

type GithubDelete struct {
        Ref             string
        RefType         string `json:"ref_type"`
        PusherType      string
        Repository      GithubRepository
        Sender          GithubUser
}

type GithubPush struct {
	Ref        string
	After      string
	Before     string
	Created    bool
	Deleted    bool
	Forced     bool
	Compare    string
	Commits    []GithubCommit
	HeadCommit GithubCommit `json:"head_commit"`
	Repository GithubRepository
	Pusher     GithubUser
}

type GithubCommit struct {
	Id        string
	Distinct  bool
	Message   string
	Timestamp string
	Url       string
	Author    GithubUser
	Committer GithubUser
	Added     []string
	Removed   []string
	Modified  []string
}

type GithubRepository struct {
	Id               uint64
	Name             string
	FullName         string `json:"full_name"`
	Owner            GithubUser
	Private          bool
	HtmlUrl          string
	Description      string
	Fork             bool
	Url              string
	ForksUrl         string
	KeysUrl          string
	CollaboratorsUrl string
	TeamsUrl         string
	HooksUrl         string
	IssueEventsUrl   string
	EventsUrl        string
	AssigneesUrl     string
	BranchesUrl      string
	TagsUrl          string
	BlobsUrl         string
	GitTagsUrl       string
	GitRefsUrl       string
	TreesUrl         string
	StatusesUrl      string
	LanguagesUrl     string
	StargazersUrl    string
	ContributorsUrl  string
	SubscribersUrl   string
	SubscriptionUrl  string
	CommitsUrl       string
	GitCommitsUrl    string
	CommentsUrl      string
	CompareUrl       string
	MergesUrl        string
	ArchiveUrl       string
	DownloadsUrl     string
	IssuesUrl        string
	PullsUrl         string
	MilestonesUrl    string
	NotificationsUrl string
	LabelsUrl        string
	ReleasesUrl      string
	CreatedAt        string
	UpdatedAt        string
	PushedAt         string
	GitUrl           string
	SshUrl           string
	CloneUrl         string
	SvnUrl           string
	Homepage         string
	Size             uint64
	StargazersCount  uint64
	WatchersCount    uint64
	Language         string
	HasIssues        bool
	HasDownloads     bool
	HasWiki          bool
	ForksCount       uint64
	MirrorUrl        string
	OpenIssuesCount  uint64
	Forks            uint64
	OpenIssues       uint64
	Watchers         uint64
	DefaultBranch    string
}

type GithubUser struct {
	Login             string
	Id                uint64
	AvatarURL         string
	GravatarId        string
	Url               string
	HtmlUrl           string
	FollowersUrl      string
	FollowingUrl      string
	GistsUrl          string
	StarredUrl        string
	SubscriptionsUrl  string
	OrganizationsUrl  string
	ReposUrl          string
	EventsUrl         string
	ReceivedEventsUrl string
	Type              string
	SiteAdmin         bool
	Name              string
	Email             string
}

// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
func CheckMAC(message []byte, messageMAC string, key string) bool {
	var err error
	var mac hash.Hash
	var macdata []byte
	var macparts = strings.Split(messageMAC, "=")
	macdata, err = hex.DecodeString(macparts[1])
	if err != nil {
		log.Print("Error decoding hex digest: ", err)
		return false
	}
	switch macparts[0] {
	case "md5":
		mac = hmac.New(md5.New, []byte(key))
	case "sha1":
		mac = hmac.New(sha1.New, []byte(key))
	case "sha256":
		mac = hmac.New(sha256.New, []byte(key))
	case "sha512":
		mac = hmac.New(sha512.New, []byte(key))
	default:
		log.Print("Unsupported hash: ", macparts[0])
		return false
	}
	mac.Write(message)
	expectedMAC := mac.Sum(nil)
	return hmac.Equal(macdata, expectedMAC)
}

func (g *GithubUser) String() string {
	if len(g.Name) > 0 && len(g.Email) > 0 {
		return g.Name + " <" + g.Email + ">"
	} else if len(g.Name) > 0 {
		return g.Name
	} else if len(g.Email) > 0 {
		return g.Email
	}
	return g.Login
}

func (g *GithubRepository) String() string {
	if len(g.FullName) > 0 {
		return g.FullName
	}
	return g.Name
}

func (g *GithubCommit) String() string {
	var lines []string = strings.Split(g.Message, "\n")                // Commit message
	var text string
        text = g.Author.String() + " \x02" + g.Id[0:7] + "\x0f" // First 7 characters

	if len(g.Added) > 0 {
		text += " \x0303" + strings.Join(g.Added, " ") + "\x0f"
	}

	if len(g.Removed) > 0 {
		text += " \x0304" + strings.Join(g.Removed, " ") + "\x0f"
	}

	if len(g.Modified) > 0 {
		text += " \x0310" + strings.Join(g.Modified, " ") + "\x0f"
	}
	if len(lines) > 0 {
		text += " " + lines[0]
	}
	return text
}

func (g *GithubPush) Strings() []string {
	var refs []string = strings.Split(g.Ref, "/")
	var prefix string = "\x0303" + g.Repository.String() + "\x0f \x0305" + refs[len(refs)-1] + "\x0f"
	var pushes []string = make([]string, 0)

	for _, commit := range g.Commits {
		pushes = append(pushes, prefix+" "+commit.String())
		pushes = append(pushes, prefix+" "+commit.Url)
	}
	return pushes
}

func (g *GithubPush) GetRepository() string {
       return g.Repository.String()
}

func (g *GithubCreate) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has pushed a new " + g.RefType + " \x0305" + g.Ref + "\x0f to \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubCreate) GetRepository() string {
       return g.Repository.String()
}

func (g *GithubDelete) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has deleted a " + g.RefType + " \x0305" + g.Ref + "\x0f from \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubDelete) GetRepository() string {
       return g.Repository.String()
}

func (g *GitHubAdapter) WriteGithubEvent(event GithubEvent, body []byte, signature string) error {
        var ok bool
        var githubconf *GitHubRepositoryConfig
        var err error

        err = json.Unmarshal(body, &event)

        if err != nil {
                log.Print("Error decoding github event: ", err)
                return err
        }

        githubconf, ok = g.channels[event.GetRepository()]
        if !ok {
                log.Print("Repository ", event.GetRepository(), " not configured.")
                return err
        }
        if !CheckMAC(body, signature, githubconf.GetSecret()) {
                log.Print("DEBUG Spam, spam spam")
                return err
        }
        for _, channel := range githubconf.GetIrcChannel() {
                for _, commit := range event.Strings() {
                        g.ircbot.Privmsg(channel, commit)
                }
        }
        return err
}

func NewGitHubAdapter(ircbot *irc.Connection, config *GitHubConfig) *GitHubAdapter {
	var channels = make(map[string]*GitHubRepositoryConfig)
	for _, repo := range config.Repo {
		channels[repo.GetName()] = repo
	}
	return &GitHubAdapter{
		ircbot:   ircbot,
		config:   config,
		channels: channels,
	}
}

func (g *GitHubAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var body []byte
	var err error
	body, err = ioutil.ReadAll(req.Body)
	if err != nil {
		log.Print("Error reading body: ", err)
		return
	}

	switch req.Header.Get("X-GitHub-Event") {
	case "create":
                var create GithubCreate
                g.WriteGithubEvent(&create, body, req.Header.Get("X-Hub-Signature"))
        case "delete":
                var del GithubDelete
                g.WriteGithubEvent(&del, body, req.Header.Get("X-Hub-Signature"))
	case "push":
		var push GithubPush
                g.WriteGithubEvent(&push, body, req.Header.Get("X-Hub-Signature"))
	default:
		log.Print("Unknown GitHub event.", req.Header.Get("X-GitHub-Event"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"image"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"math/rand"
	"os"
	"path/filepath"
)

// Global Vars
var seed string
var glitchFactor float64
var brightnessFactor float64
var useScanLines bool
var inputImage string
var outputImage string

// Custom usage info func for flags package
func usage() {
	fmt.Fprintf(os.Stderr, "Usage: glitch [-gbls] input_image output_image\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func bail(message string) {
	fmt.Fprintf(os.Stderr, message+"\n")
	os.Exit(1)
}

func random(min, max int) int {
	offset := 0
	input := max - min

	if input <= 0 {
		offset = 1 + input*-1
		input = offset
	}

	return rand.Intn(input) + min - offset
}

func randomseed() (seedInt int64) {
	hasher := md5.New()
	io.WriteString(hasher, seed)
	hash := hasher.Sum(nil)

	length := len(hash)
	for i, hashByte := range hash {
		// Get byte shift offset as a uint64
		shift := uint64((length - i - length) * 8)
		// OR the shifted byte onto the return value
		seedInt |= int64(hashByte) << shift
	}

	return
}

// Actually does useful stuff
func glitchify() {
	reader, err := os.Open(inputImage)
	if err != nil {
		bail("Couldn't open input image!")
	}

	// Decode the image data from the input file. Don't care about format registration
	inputData, _, err := image.Decode(reader)
	if err != nil {
		bail("Couldn't decode image data!")
	}

	// Close reader since we've got the image data now
	reader.Close()

	// Useful values
	bounds := inputData.Bounds()
	width, height := bounds.Max.X, bounds.Max.Y
	maxOffset := int(glitchFactor / 100.0 * float64(width))

	// Initialise output as identical to input
	outputData := image.NewRGBA(bounds)
	draw.Draw(outputData, bounds, inputData, bounds.Min, draw.Src)

	// Random image slice offsetting
	for i := 0.0; i < glitchFactor*2; i++ {
		startY := random(0, height)
		chunkHeight := int(math.Min(float64(height-startY), float64(random(1, height/4))))
		offset := random(-maxOffset, maxOffset)

		if offset == 0 {
			continue
		}

		if offset < 0 {
			draw.Draw(outputData, image.Rect(-offset, startY, width+offset, chunkHeight), inputData, image.Pt(0, startY), draw.Src)
			draw.Draw(outputData, image.Rect(0, startY, -offset, chunkHeight), inputData, image.Pt(width+offset, startY), draw.Src)
		} else {
			draw.Draw(outputData, image.Rect(0, startY, width, chunkHeight), inputData, image.Pt(offset, startY), draw.Src)
			draw.Draw(outputData, image.Rect(width-offset, startY, offset, chunkHeight), inputData, image.Pt(0, startY), draw.Src)
		}
	}

	writer, err := os.Create(outputImage)
	if err != nil {
		bail("Couldn't create output file!")
	}
	defer writer.Close()

	switch filepath.Ext(outputImage) {
	case ".jpg", ".jpeg":
		err = jpeg.Encode(writer, outputData, &jpeg.Options{jpeg.DefaultQuality})
	case ".gif":
		err = gif.Encode(writer, outputData, &gif.Options{256, nil, nil})
	case ".png":
		err = png.Encode(writer, outputData)
	default:
		bail("Image format not supported. Please use GIF, JPEG or PNG.")
	}

	if err != nil {
		bail("There was an error encoding the image data.")
	}
}

// Main
func main() {
	// Setup usage info
	flag.Usage = usage

	// Get the host name for the default seed
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "unknown"
	}

	// Get Glitch Factor
	flag.Float64Var(&glitchFactor, "glitch", 5.0, "Defines how much glitching to do (0-100)")
	flag.Float64Var(&glitchFactor, "g", 5.0, "Defines how much glitching to do (0-100) - shorthand syntax")

	// Get Brightness Factor
	flag.Float64Var(&brightnessFactor, "brightness", 5.0, "Defines how much brightening to do (0-100)")
	flag.Float64Var(&brightnessFactor, "b", 5.0, "Defines how much brightening to do (0-100) - shorthand syntax")

	// Should do scan line effect?
	flag.BoolVar(&useScanLines, "scanlines", true, "Apply the scan line filter")
	flag.BoolVar(&useScanLines, "l", true, "Apply the scan line filter - shorthand syntax")

	// A seed to use for the randomiser
	flag.StringVar(&seed, "seed", hostname, "Seed for the randomiser")
	flag.StringVar(&seed, "s", hostname, "Seed for the randomiser - shorthand syntax")

	flag.Parse()

	inputImage = flag.Arg(0)
	outputImage = flag.Arg(1)

	// Sanitise input
	switch {
	case len(inputImage) == 0:
		fmt.Fprintf(os.Stderr, "No input image specified\n")
		usage()
	case len(outputImage) == 0:
		fmt.Fprintf(os.Stderr, "No output image specified\n")
		usage()
	case glitchFactor > 100.0 || glitchFactor < 0.0:
		fmt.Fprintf(os.Stderr, "Glitch factor must be between 0 and 100\n")
		usage()
	case brightnessFactor > 100.0 || brightnessFactor < 0.0:
		fmt.Fprintf(os.Stderr, "Brightness factor must be between 0 and 100\n")
		usage()
	}

	// Seed the random number generator
	rand.Seed(randomseed())

	// Onto the main event!
	glitchify()
}
<commit_msg>Actually make program exit on bad inputs.<commit_after>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"image"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"math/rand"
	"os"
	"path/filepath"
)

// Global Vars
var seed string
var glitchFactor float64
var brightnessFactor float64
var useScanLines bool
var inputImage string
var outputImage string

// Custom usage info func for flags package
func usage() {
	fmt.Fprintf(os.Stderr, "Usage: glitch [-gbls] input_image output_image\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func bail(message string) {
	fmt.Fprintf(os.Stderr, message+"\n")
	os.Exit(1)
}

func random(min, max int) int {
	offset := 0
	input := max - min

	if input <= 0 {
		offset = 1 + input*-1
		input = offset
	}

	return rand.Intn(input) + min - offset
}

func randomseed() (seedInt int64) {
	hasher := md5.New()
	io.WriteString(hasher, seed)
	hash := hasher.Sum(nil)

	length := len(hash)
	for i, hashByte := range hash {
		// Get byte shift offset as a uint64
		shift := uint64((length - i - length) * 8)
		// OR the shifted byte onto the return value
		seedInt |= int64(hashByte) << shift
	}

	return
}

// Actually does useful stuff
func glitchify() {
	reader, err := os.Open(inputImage)
	if err != nil {
		bail("Couldn't open input image!")
	}

	// Decode the image data from the input file. Don't care about format registration
	inputData, _, err := image.Decode(reader)
	if err != nil {
		bail("Couldn't decode image data!")
	}

	// Close reader since we've got the image data now
	reader.Close()

	// Useful values
	bounds := inputData.Bounds()
	width, height := bounds.Max.X, bounds.Max.Y
	maxOffset := int(glitchFactor / 100.0 * float64(width))

	// Initialise output as identical to input
	outputData := image.NewRGBA(bounds)
	draw.Draw(outputData, bounds, inputData, bounds.Min, draw.Src)

	// Random image slice offsetting
	for i := 0.0; i < glitchFactor*2; i++ {
		startY := random(0, height)
		chunkHeight := int(math.Min(float64(height-startY), float64(random(1, height/4))))
		offset := random(-maxOffset, maxOffset)

		if offset == 0 {
			continue
		}

		if offset < 0 {
			draw.Draw(outputData, image.Rect(-offset, startY, width+offset, chunkHeight), inputData, image.Pt(0, startY), draw.Src)
			draw.Draw(outputData, image.Rect(0, startY, -offset, chunkHeight), inputData, image.Pt(width+offset, startY), draw.Src)
		} else {
			draw.Draw(outputData, image.Rect(0, startY, width, chunkHeight), inputData, image.Pt(offset, startY), draw.Src)
			draw.Draw(outputData, image.Rect(width-offset, startY, offset, chunkHeight), inputData, image.Pt(0, startY), draw.Src)
		}
	}

	writer, err := os.Create(outputImage)
	if err != nil {
		bail("Couldn't create output file!")
	}
	defer writer.Close()

	switch filepath.Ext(outputImage) {
	case ".jpg", ".jpeg":
		err = jpeg.Encode(writer, outputData, &jpeg.Options{jpeg.DefaultQuality})
	case ".gif":
		err = gif.Encode(writer, outputData, &gif.Options{256, nil, nil})
	case ".png":
		err = png.Encode(writer, outputData)
	default:
		bail("Image format not supported. Please use GIF, JPEG or PNG.")
	}

	if err != nil {
		bail("There was an error encoding the image data.")
	}
}

// Main
func main() {
	// Setup usage info
	flag.Usage = usage

	// Get the host name for the default seed
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "unknown"
	}

	// Get Glitch Factor
	flag.Float64Var(&glitchFactor, "glitch", 5.0, "Defines how much glitching to do (0-100)")
	flag.Float64Var(&glitchFactor, "g", 5.0, "Defines how much glitching to do (0-100) - shorthand syntax")

	// Get Brightness Factor
	flag.Float64Var(&brightnessFactor, "brightness", 5.0, "Defines how much brightening to do (0-100)")
	flag.Float64Var(&brightnessFactor, "b", 5.0, "Defines how much brightening to do (0-100) - shorthand syntax")

	// Should do scan line effect?
	flag.BoolVar(&useScanLines, "scanlines", true, "Apply the scan line filter")
	flag.BoolVar(&useScanLines, "l", true, "Apply the scan line filter - shorthand syntax")

	// A seed to use for the randomiser
	flag.StringVar(&seed, "seed", hostname, "Seed for the randomiser")
	flag.StringVar(&seed, "s", hostname, "Seed for the randomiser - shorthand syntax")

	flag.Parse()

	inputImage = flag.Arg(0)
	outputImage = flag.Arg(1)

	// Sanitise input
	switch {
	case len(inputImage) == 0:
		fmt.Fprintf(os.Stderr, "No input image specified\n")
		usage()
		os.Exit(2)
	case len(outputImage) == 0:
		fmt.Fprintf(os.Stderr, "No output image specified\n")
		usage()
		os.Exit(2)
	case glitchFactor > 100.0 || glitchFactor < 0.0:
		fmt.Fprintf(os.Stderr, "Glitch factor must be between 0 and 100\n")
		usage()
		os.Exit(2)
	case brightnessFactor > 100.0 || brightnessFactor < 0.0:
		fmt.Fprintf(os.Stderr, "Brightness factor must be between 0 and 100\n")
		usage()
		os.Exit(2)
	}

	// Seed the random number generator
	rand.Seed(randomseed())

	// Onto the main event!
	glitchify()
}
<|endoftext|>


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

// Gofind searches for Go packages via pkg.go.dev.
//
// Usage:
//      gofind [<flag> ...] <query> ...
//
// Flags
//      -raw   don't apply any formatting if set
package main

import (
	"flag"
	"fmt"
	"go/doc"
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

func usage() {
	_, _ = fmt.Fprint(os.Stderr, usageDoc)
	os.Exit(2)
}

const usageDoc = `Find Go modules via pkg.go.dev.

Usage:
    gofind [-a] [-raw] query ...

Flags:
    -a     load all search results if set, not just the first 10 results
    -raw   don't apply any formatting if set

Examples:
    gofind logging
    gofind -a logging
    gofind go cloud        # Search for multiple terms
    gofind "go cloud"      # Search for an exact match
    gofind yaml OR json    # Combine searches
`

func main() {
	allFlag := flag.Bool("a", false, "load all search results, not just the first 10 results")
	rawFlag := flag.Bool("raw", false, "don't apply any formatting")

	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		usage()
	}
	args := flag.Args()
	for i, arg := range args {
		if strings.Contains(arg, " ") {
			args[i] = `"` + arg + `"`
		}
	}
	query := strings.Join(args, " ")

	run(query, *allFlag, *rawFlag)
}

func run(query string, all, raw bool) {
	modules, err := search(query, all)
	check(err)
	for _, mod := range modules {
		if raw {
			fmt.Println(mod.modulePath + "\t" + mod.synopsis + "\t" + mod.info)
			continue
		}
		check(mod.writeTo(os.Stdout))
	}
}

func search(query string, all bool) ([]searchResult, error) {
	limit := 10
	if all {
		limit = 100
	}
	return searchLimited(query, limit)
}

func searchLimited(query string, limit int) ([]searchResult, error) {
	v := url.Values{}
	v.Set("q", query)
	v.Set("limit", strconv.Itoa(limit))
	req, err := http.NewRequest("GET", "https://beta.pkg.go.dev/search?"+v.Encode(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "text/html")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP status code error: %d %sr", res.StatusCode, res.Status)
	}

	htmlDoc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		return nil, err
	}

	return scrapeSearchResults(htmlDoc)
}

func scrapeSearchResults(htmlDoc *goquery.Document) (sr []searchResult, err error) {
	htmlDoc.Find(".SearchSnippet").Each(func(i int, s *goquery.Selection) {
		headerNameSel := s.Find(".SearchSnippet-header-name")
		moduleName := strings.TrimSpace(headerNameSel.Text())
		modulePath := strings.TrimSpace(headerNameSel.Parent().Contents().Last().Text())
		synopsis := strings.TrimSpace(s.Find(".SearchSnippet-synopsis").Text())
		info := formatInfo(s.Find(".SearchSnippet-infoLabel").Text())
		sr = append(sr, searchResult{
			moduleName: moduleName,
			modulePath: modulePath,
			synopsis:   synopsis,
			info:       info,
		})
	})
	return sr, err
}

func formatInfo(info string) string {
	var parts []string
	for _, p := range strings.Split(info, "|") {
		parts = append(parts, strings.TrimSpace(p))
	}
	return strings.Join(parts, " | ")
}

type searchResult struct {
	moduleName string
	modulePath string
	synopsis   string
	info       string
}

const (
	punchCardWidth = 80
	indent         = "    "
)

func (s searchResult) writeTo(w io.Writer) error {
	_, err := fmt.Fprintf(w, "%s - %s\n", s.moduleName, s.modulePath)
	if err != nil {
		return err
	}
	if s.synopsis != "" {
		doc.ToText(w, s.synopsis, indent, "", punchCardWidth-2*len(indent))
	}
	_, err = fmt.Fprintf(w, "\n%s%s\n\n", indent, s.info)
	return err
}

func check(err error) {
	if err != nil {
		fail(err)
	}
}

func fail(msg ...interface{}) {
	_, _ = fmt.Fprintln(os.Stderr, msg...)
	os.Exit(1)
}
<commit_msg>adapt to changes in HTML structure<commit_after>// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Gofind searches for Go packages via pkg.go.dev.
//
// Usage:
//      gofind [<flag> ...] <query> ...
//
// Flags
//      -raw   don't apply any formatting if set
package main

import (
	"flag"
	"fmt"
	"go/doc"
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

func usage() {
	_, _ = fmt.Fprint(os.Stderr, usageDoc)
	os.Exit(2)
}

const usageDoc = `Find Go modules via pkg.go.dev.

Usage:
    gofind [-a] [-raw] query ...

Flags:
    -a     load all search results if set, not just the first 10 results
    -raw   don't apply any formatting if set

Examples:
    gofind logging
    gofind -a logging
    gofind go cloud        # Search for multiple terms
    gofind "go cloud"      # Search for an exact match
    gofind yaml OR json    # Combine searches
`

func main() {
	allFlag := flag.Bool("a", false, "load all search results, not just the first 10 results")
	rawFlag := flag.Bool("raw", false, "don't apply any formatting")

	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		usage()
	}
	args := flag.Args()
	for i, arg := range args {
		if strings.Contains(arg, " ") {
			args[i] = `"` + arg + `"`
		}
	}
	query := strings.Join(args, " ")

	run(query, *allFlag, *rawFlag)
}

func run(query string, all, raw bool) {
	modules, err := search(query, all)
	check(err)
	for _, mod := range modules {
		if raw {
			fmt.Println(mod.modulePath + "\t" + mod.synopsis + "\t" + mod.info)
			continue
		}
		check(mod.writeTo(os.Stdout))
	}
}

func search(query string, all bool) ([]searchResult, error) {
	limit := 10
	if all {
		limit = 100
	}
	return searchLimited(query, limit)
}

func searchLimited(query string, limit int) ([]searchResult, error) {
	v := url.Values{}
	v.Set("q", query)
	v.Set("limit", strconv.Itoa(limit))
	req, err := http.NewRequest("GET", "https://beta.pkg.go.dev/search?"+v.Encode(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "text/html")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP status code error: %d %sr", res.StatusCode, res.Status)
	}

	htmlDoc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		return nil, err
	}

	return scrapeSearchResults(htmlDoc)
}

func scrapeSearchResults(htmlDoc *goquery.Document) (sr []searchResult, err error) {
	htmlDoc.Find(".SearchSnippet").Each(func(i int, s *goquery.Selection) {
		header := s.Find(".SearchSnippet-headerContainer").Text()
		moduleName := strings.TrimSpace(header[:strings.Index(header, "(")])
		modulePath := trimParens(strings.TrimSpace(s.Find(".SearchSnippet-header-path").Text()))
		synopsis := strings.TrimSpace(s.Find(".SearchSnippet-synopsis").Text())
		info := formatInfo(s.Find(".SearchSnippet-infoLabel").Text())
		sr = append(sr, searchResult{
			moduleName: moduleName,
			modulePath: modulePath,
			synopsis:   synopsis,
			info:       info,
		})
	})
	return sr, err
}

func trimParens(s string) string {
	return strings.TrimSuffix(strings.TrimPrefix(s, "("), ")")
}

func formatInfo(info string) string {
	var parts []string
	for _, p := range strings.Split(info, "|") {
		parts = append(parts, strings.TrimSpace(p))
	}
	return strings.Join(parts, " | ")
}

type searchResult struct {
	moduleName string
	modulePath string
	synopsis   string
	info       string
}

const (
	punchCardWidth = 80
	indent         = "    "
)

func (s searchResult) writeTo(w io.Writer) error {
	_, err := fmt.Fprintf(w, "%s (%s)\n", s.moduleName, s.modulePath)
	if err != nil {
		return err
	}
	if s.synopsis != "" {
		doc.ToText(w, s.synopsis, indent, "", punchCardWidth-2*len(indent))
	}
	_, err = fmt.Fprintf(w, "\n%s%s\n\n", indent, s.info)
	return err
}

func check(err error) {
	if err != nil {
		fail(err)
	}
}

func fail(msg ...interface{}) {
	_, _ = fmt.Fprintln(os.Stderr, msg...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// The "-dev" suffix in the version info indicates that fact, and it means the
// current build is from a version greater that version. For example, v0.7-dev
// means version > 0.7 and < 0.8. (There's exceptions to this rule, see
// docs/releasing.md for more details.)
//
// When releasing a new Kubernetes version, this file should be updated to
// reflect the new version, and then a git annotated tag (using format vX.Y
// where X == Major version and Y == Minor version) should be created to point
// to the commit that updates pkg/version/base.go

var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion instead.
	gitMajor     string = "0"              // major version, always numeric
	gitMinor     string = "9.1"            // minor version, numeric possibly followed by "+"
	gitVersion   string = "v0.9.1"         // version from git, output of $(git describe)
	gitCommit    string = ""               // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"
)
<commit_msg>Kubernetes version v0.9.1-dev<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// The "-dev" suffix in the version info indicates that fact, and it means the
// current build is from a version greater that version. For example, v0.7-dev
// means version > 0.7 and < 0.8. (There's exceptions to this rule, see
// docs/releasing.md for more details.)
//
// When releasing a new Kubernetes version, this file should be updated to
// reflect the new version, and then a git annotated tag (using format vX.Y
// where X == Major version and Y == Minor version) should be created to point
// to the commit that updates pkg/version/base.go

var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion instead.
	gitMajor     string = "0"              // major version, always numeric
	gitMinor     string = "9.1+"           // minor version, numeric possibly followed by "+"
	gitVersion   string = "v0.9.1-dev"     // version from git, output of $(git describe)
	gitCommit    string = ""               // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"
)
<|endoftext|>