From a5d79fddd9232022ffc80fe4275e1c199c70b019 Mon Sep 17 00:00:00 2001 From: Adnan Hajdarevic Date: Tue, 24 Feb 2015 16:52:55 +0100 Subject: [PATCH] separated github from post handler --- hooks/hooks.go | 13 +++++++ webhook.go | 94 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/hooks/hooks.go b/hooks/hooks.go index a077a4f..822d48c 100644 --- a/hooks/hooks.go +++ b/hooks/hooks.go @@ -14,6 +14,7 @@ type Hook struct { Command string `json:"command"` Cwd string `json:"cwd"` Secret string `json:"secret"` + Args []string `json:"args"` Rule rules.Rule `json:"trigger-rule"` } @@ -49,6 +50,14 @@ func (h *Hook) UnmarshalJSON(j []byte) error { h.Secret = v.(string) } + if v, ok := m["args"]; ok { + h.Args = make([]string, 0) + + for i := range v.([]interface{}) { + h.Args = append(h.Args, v.([]interface{})[i].(string)) + } + } + if v, ok := m["trigger-rule"]; ok { rule := v.(map[string]interface{}) @@ -150,5 +159,9 @@ func (h *Hooks) SetDefaults() { if h.list[i].Cwd == "" { h.list[i].Cwd = "." } + + if h.list[i].Args == nil { + h.list[i].Args = make([]string, 1) + } } } diff --git a/webhook.go b/webhook.go index 704c240..46a51f2 100644 --- a/webhook.go +++ b/webhook.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "os/exec" "strings" "time" @@ -70,49 +71,84 @@ func rootHandler() string { return fmt.Sprintf("webhook %s running for %s serving %d hook(s)\n", version, time.Since(appStart).String(), webhooks.Count()) } -func hookHandler(req *http.Request, params martini.Params) string { - defer req.Body.Close() - body, err := ioutil.ReadAll(req.Body) - if err != nil { - l4g.Warn("Error occurred while trying to read the request body: %s", err) +func githubHandler(id string, body []byte, signature string, params interface{}) { + if hook := webhooks.Match(id, params); hook != nil { + if hook.Secret != "" { + if signature == "" { + l4g.Error("Hook %s got matched and contains the secret, but the request didn't contain any signature.", hook.ID) + return + } + + mac := hmac.New(sha1.New, []byte(hook.Secret)) + mac.Write(body) + expectedMAC := hex.EncodeToString(mac.Sum(nil)) + + if !hmac.Equal([]byte(signature), []byte(expectedMAC)) { + l4g.Error("Hook %s got matched and contains the secret, but the request contained invalid signature. Expected %s, got %s.", hook.ID, expectedMAC, signature) + return + } + } + + cmd := exec.Command(hook.Command) + cmd.Dir = hook.Cwd + out, err := cmd.Output() + l4g.Info("Hook %s triggered successfully! Command output:\n%s\nError: %+v", hook.ID, out, err) } +} - payloadJSON := make(map[string]interface{}) +func defaultPostHookHandler(id string, formValues url.Values) { + if hook := webhooks.Match(id, make(map[string]interface{})); hook != nil { + args := make([]string, 0) + args = append(args, hook.Command) + for i := range hook.Args { + if arg := formValues[hook.Args[i]]; len(arg) > 0 { + args = append(args, arg[0]) + } + } + cmd := exec.Command(hook.Command) + cmd.Args = args + cmd.Dir = hook.Cwd + + out, err := cmd.Output() + + l4g.Info("Hook %s triggered successfully! Command output:\n%s\nError: %+v", hook.ID, out, err) + } +} + +func hookHandler(req *http.Request, params martini.Params) string { if req.Header.Get("Content-Type") == "application/json" { + defer req.Body.Close() + + body, err := ioutil.ReadAll(req.Body) + if err != nil { + l4g.Warn("Error occurred while trying to read the request body: %s", err) + } + + payloadJSON := make(map[string]interface{}) + decoder := json.NewDecoder(strings.NewReader(string(body))) decoder.UseNumber() - err := decoder.Decode(&payloadJSON) + err = decoder.Decode(&payloadJSON) if err != nil { l4g.Warn("Error occurred while trying to parse the payload as JSON: %s", err) } - } - go func(id string, body []byte, signature string, params interface{}) { - if hook := webhooks.Match(id, params); hook != nil { - if hook.Secret != "" { - if signature == "" { - l4g.Error("Hook %s got matched and contains the secret, but the request didn't contain any signature.", hook.ID) - return - } + githubSignature := "" - mac := hmac.New(sha1.New, []byte(hook.Secret)) - mac.Write(body) - expectedMAC := hex.EncodeToString(mac.Sum(nil)) - - if !hmac.Equal([]byte(signature), []byte(expectedMAC)) { - l4g.Error("Hook %s got matched and contains the secret, but the request contained invalid signature. Expected %s, got %s.", hook.ID, expectedMAC, signature) - return - } - } - - cmd := exec.Command(hook.Command, "", "", hook.Cwd) - out, _ := cmd.Output() - l4g.Info("Hook %s triggered successfully! Command output:\n%s", hook.ID, out) + if len(req.Header.Get("X-Hub-Signature")) > 5 { + githubSignature = req.Header.Get("X-Hub-Signature")[5:] } - }(params["id"], body, req.Header.Get("X-Hub-Signature")[5:], payloadJSON) + + if strings.Contains(req.Header.Get("User-Agent"), "Github") { + go githubHandler(params["id"], body, githubSignature, payloadJSON) + } + } else { + req.ParseForm() + go defaultPostHookHandler(params["id"], req.Form) + } return "Got it, thanks. :-)" }