diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/cert.go | 158 | ||||
-rw-r--r-- | cmd/dump.go | 8 | ||||
-rw-r--r-- | cmd/serve.go | 8 | ||||
-rw-r--r-- | cmd/web.go | 69 |
4 files changed, 222 insertions, 21 deletions
diff --git a/cmd/cert.go b/cmd/cert.go new file mode 100644 index 00000000..b693b7d9 --- /dev/null +++ b/cmd/cert.go @@ -0,0 +1,158 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "log" + "math/big" + "net" + "os" + "strings" + "time" + + "github.com/codegangsta/cli" +) + +var CmdCert = cli.Command{ + Name: "cert", + Usage: "Generate self-signed certificate", + Description: `Generate a self-signed X.509 certificate for a TLS server. +Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, + Action: runCert, + Flags: []cli.Flag{ + cli.StringFlag{"host", "", "Comma-separated hostnames and IPs to generate a certificate for", ""}, + cli.StringFlag{"ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", ""}, + cli.IntFlag{"rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set", ""}, + cli.StringFlag{"start-date", "", "Creation date formatted as Jan 1 15:04:05 2011", ""}, + cli.DurationFlag{"duration", 365 * 24 * time.Hour, "Duration that certificate is valid for", ""}, + cli.BoolFlag{"ca", "whether this cert should be its own Certificate Authority", ""}, + }, +} + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + log.Fatal("unable to marshal ECDSA private key: %v", err) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +func runCert(ctx *cli.Context) { + if len(ctx.String("host")) == 0 { + log.Fatal("Missing required --host parameter") + } + + var priv interface{} + var err error + switch ctx.String("ecdsa-curve") { + case "": + priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits")) + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve")) + } + if err != nil { + log.Fatalf("Failed to generate private key: %s", err) + } + + var notBefore time.Time + if len(ctx.String("start-date")) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date")) + if err != nil { + log.Fatalf("Failed to parse creation date: %s", err) + } + } + + notAfter := notBefore.Add(ctx.Duration("duration")) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("Failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(ctx.String("host"), ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if ctx.Bool("ca") { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } + + certOut, err := os.Create("cert.pem") + if err != nil { + log.Fatalf("Failed to open cert.pem for writing: %s", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + log.Println("Written cert.pem") + + keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatal("failed to open key.pem for writing: %v", err) + } + pem.Encode(keyOut, pemBlockForKey(priv)) + keyOut.Close() + log.Println("Written key.pem") +} diff --git a/cmd/dump.go b/cmd/dump.go index 2a54db1a..41491224 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -24,16 +24,18 @@ var CmdDump = cli.Command{ Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gogs server image to send to maintainer`, Action: runDump, - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + cli.BoolFlag{"verbose, v", "show process details", ""}, + }, } -func runDump(*cli.Context) { +func runDump(ctx *cli.Context) { setting.NewConfigContext() models.LoadModelsConfig() models.SetEngine() log.Printf("Dumping local repositories...%s", setting.RepoRootPath) - zip.Verbose = false + zip.Verbose = ctx.Bool("verbose") defer os.Remove("gogs-repo.zip") if err := zip.PackTo(setting.RepoRootPath, "gogs-repo.zip", true); err != nil { log.Fatalf("Fail to dump local repositories: %v", err) diff --git a/cmd/serve.go b/cmd/serve.go index b1dffc92..c18bf3ad 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -171,7 +171,13 @@ func runServ(k *cli.Context) { uuid := uuid.NewV4().String() os.Setenv("uuid", uuid) - gitcmd := exec.Command(verb, repoPath) + var gitcmd *exec.Cmd + verbs := strings.Split(verb, " ") + if len(verbs) == 2 { + gitcmd = exec.Command(verbs[0], verbs[1], repoPath) + } else { + gitcmd = exec.Command(verb, repoPath) + } gitcmd.Dir = setting.RepoRootPath gitcmd.Stdout = os.Stdout gitcmd.Stdin = os.Stdin @@ -11,6 +11,7 @@ import ( "net/http" "os" "path" + "strings" "github.com/Unknwon/macaron" "github.com/codegangsta/cli" @@ -26,6 +27,7 @@ import ( "github.com/gogits/gogs/modules/auth/apiv1" "github.com/gogits/gogs/modules/avatar" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/git" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware/binding" @@ -50,6 +52,7 @@ and it takes care of all the other things for you`, // checkVersion checks if binary matches the version of templates files. func checkVersion() { + // Templates. data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION")) if err != nil { log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err) @@ -57,6 +60,12 @@ func checkVersion() { if string(data) != setting.AppVer { log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?") } + + // Macaron. + macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], ".")) + if macaronVer.LessThan(git.MustParseVersion("0.1.8")) { + log.Fatal(4, "Macaron version does not match, did you forget to update?(github.com/Unknwon/macaron)") + } } // newMacaron initializes Macaron instance. @@ -64,20 +73,22 @@ func newMacaron() *macaron.Macaron { m := macaron.New() m.Use(macaron.Logger()) m.Use(macaron.Recovery()) - m.Use(macaron.Static("public", + m.Use(macaron.Static( + path.Join(setting.StaticRootPath, "public"), macaron.StaticOptions{ SkipLogging: !setting.DisableRouterLog, }, )) - if setting.EnableGzip { - m.Use(macaron.Gzip()) - } + // if setting.EnableGzip { + // m.Use(macaron.Gzip()) + // } m.Use(macaron.Renderer(macaron.RenderOptions{ Directory: path.Join(setting.StaticRootPath, "templates"), Funcs: []template.FuncMap{base.TemplateFuncs}, IndentJSON: macaron.Env != macaron.PROD, })) m.Use(i18n.I18n(i18n.Options{ + SubURL: setting.AppSubUrl, Langs: setting.Langs, Names: setting.Names, Redirect: true, @@ -87,14 +98,18 @@ func newMacaron() *macaron.Macaron { Interval: setting.CacheInternal, Conn: setting.CacheConn, })) - m.Use(captcha.Captchaer()) + m.Use(captcha.Captchaer(captcha.Options{ + SubURL: setting.AppSubUrl, + })) m.Use(session.Sessioner(session.Options{ Provider: setting.SessionProvider, Config: *setting.SessionConfig, })) m.Use(csrf.Generate(csrf.Options{ - Secret: setting.SecretKey, - SetCookie: true, + Secret: setting.SecretKey, + SetCookie: true, + Header: "X-Csrf-Token", + CookiePath: setting.AppSubUrl, })) m.Use(toolbox.Toolboxer(m, toolbox.Options{ HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{ @@ -123,6 +138,7 @@ func runWeb(*cli.Context) { // Routers. m.Get("/", ignSignIn, routers.Home) + m.Get("/explore", ignSignIn, routers.Explore) m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) m.Group("", func(r *macaron.Router) { @@ -183,7 +199,8 @@ func runWeb(*cli.Context) { r.Get("/logout", user.SignOut) }) - m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy + // FIXME: Legacy + m.Get("/user/:username", ignSignIn, user.Profile) // Gravatar service. avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg") @@ -259,6 +276,13 @@ func runWeb(*cli.Context) { m.Group("/settings", func(r *macaron.Router) { r.Get("", org.Settings) r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) + r.Get("/hooks", org.SettingsHooks) + r.Get("/hooks/new", repo.WebHooksNew) + r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) + r.Get("/hooks/:id", repo.WebHooksEdit) + r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) r.Route("/delete", "GET,POST", org.SettingsDelete) }) @@ -284,9 +308,11 @@ func runWeb(*cli.Context) { r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration) r.Get("/hooks", repo.Webhooks) r.Get("/hooks/new", repo.WebHooksNew) - r.Post("/hooks/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) r.Get("/hooks/:id", repo.WebHooksEdit) - r.Post("/hooks/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) }) }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) @@ -327,6 +353,8 @@ func runWeb(*cli.Context) { r.Get("/issues/:index", repo.ViewIssue) r.Get("/pulls", repo.Pulls) r.Get("/branches", repo.Branches) + r.Get("/archive/*", repo.Download) + r.Get("/issues2/", repo.Issues2) }, ignSignIn, middleware.RepoAssignment(true)) m.Group("/:username/:reponame", func(r *macaron.Router) { @@ -339,22 +367,29 @@ func runWeb(*cli.Context) { r.Get("/commit/:branchname", repo.Diff) r.Get("/commit/:branchname/*", repo.Diff) r.Get("/releases", repo.Releases) - r.Get("/archive/*.*", repo.Download) + r.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff) }, ignSignIn, middleware.RepoAssignment(true, true)) m.Group("/:username", func(r *macaron.Router) { - r.Get("/:reponame", middleware.RepoAssignment(true, true, true), repo.Home) - m.Group("/:reponame", func(r *macaron.Router) { - r.Any("/*", repo.Http) - }) - }, ignSignInAndCsrf) + r.Get("/:reponame", ignSignIn, middleware.RepoAssignment(true, true, true), repo.Home) + r.Any("/:reponame/*", ignSignInAndCsrf, repo.Http) + }) + + // robots.txt + m.Get("/robots.txt", func(ctx *middleware.Context) { + if setting.HasRobotsTxt { + ctx.ServeFile(path.Join(setting.CustomPath, "robots.txt")) + } else { + ctx.Error(404) + } + }) // Not found handler. m.NotFound(routers.NotFound) var err error listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort) - log.Info("Listen: %v://%s", setting.Protocol, listenAddr) + log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl) switch setting.Protocol { case setting.HTTP: err = http.ListenAndServe(listenAddr, m) |